Index: /extensions/Password_Policy/admin/PP_admin.php
===================================================================
--- /extensions/Password_Policy/admin/PP_admin.php	(revision 25088)
+++ /extensions/Password_Policy/admin/PP_admin.php	(revision 25089)
@@ -34,6 +34,32 @@
 
 
-  if (isset($_POST['submit']) and isset($_POST['PP_Password_Enforced']) and isset($_POST['PP_AdminPassword_Enforced']) and isset($_POST['PP_PwdReset']))
+// +----------------------------------------------------------+
+// |            FCK Editor for email text fields              |
+// +----------------------------------------------------------+
+$toolbar = 'Basic';
+$width = '750px';
+$height = '300px';
+$areas = array();
+array_push($areas,'PP_CustomLockMsg');
+
+if (function_exists('set_fckeditor_instance'))
+{
+  $fcke_config = unserialize($conf['FCKEditor']);
+  foreach($areas as $area)
   {
+    if (!isset($fcke_config[$area]))
+    {
+      $fcke_config[$area] = false;
+    }
+  }
+  $conf['FCKEditor'] = serialize($fcke_config);
+
+  set_fckeditor_instance($areas, $toolbar, $width, $height);
+}
+
+
+  if (isset($_POST['submit']) and isset($_POST['PP_Password_Enforced']) and isset($_POST['PP_AdminPassword_Enforced']) and isset($_POST['PP_PwdReset']) and isset($_POST['PP_LogFailedPassw']))
+  {
+    $_POST['PP_CustomLockMsg'] = str_replace('\"', '"', str_replace("\'", "'", str_replace("\\\\", "\\", $_POST['PP_CustomLockMsg'])));
     // Save global PP configuration
     // -----------------------------
@@ -43,4 +69,7 @@
     $newconf_PP['ADMINPASSWENF'] = (isset($_POST['PP_AdminPassword_Enforced']) ? $_POST['PP_AdminPassword_Enforced'] : 'false');
     $newconf_PP['PWDRESET'] = (isset($_POST['PP_PwdReset']) ? $_POST['PP_PwdReset'] : 'false');
+    $newconf_PP['LOGFAILBLOCK'] = (isset($_POST['PP_LogFailedPassw']) ? $_POST['PP_LogFailedPassw'] : 'false');
+    $newconf_PP['NBLOGFAIL'] = (isset($_POST['PP_NbFailedPassw']) ? $_POST['PP_NbFailedPassw'] : '0');
+    $newconf_PP['USRLOCKEDTXT'] = (isset($_POST['PP_CustomLockMsg']) ? $_POST['PP_CustomLockMsg'] : l10n('PP_User_Account_Locked_Txt'));
 
     $conf['PasswordPolicy'] = serialize($newconf_PP);
@@ -82,4 +111,8 @@
             'PP_PWDRESET_FALSE'                => $conf_PP['PWDRESET']=='false' ? 'checked="checked"' : '' ,
 						'PP_PASSWORD_TEST_SCORE'           => $PP_Password_Test_Score,
+            'PP_LOGFAILEDPASSW_TRUE'           => $conf_PP['LOGFAILBLOCK']=='true' ? 'checked="checked"' : '' ,
+            'PP_LOGFAILEDPASSW_FALSE'          => $conf_PP['LOGFAILBLOCK']=='false' ? 'checked="checked"' : '' ,
+            'PP_NBLOGFAIL'                     => $conf_PP['NBLOGFAIL'],
+            'PP_USRLOCKEDTXT'                  => $conf_PP['USRLOCKEDTXT']
     )
   );
Index: /extensions/Password_Policy/admin/template/PP_admin.tpl
===================================================================
--- /extensions/Password_Policy/admin/template/PP_admin.tpl	(revision 25088)
+++ /extensions/Password_Policy/admin/template/PP_admin.tpl	(revision 25089)
@@ -4,14 +4,23 @@
 {combine_css path= $PP_PATH|@cat:'admin/template/pp.css'}
 
-<script type="text/javascript">
+{footer_script}{literal}
 jQuery().ready(function()
-{ldelim}
+{
+  jQuery("#PP_LoginAttempts_true").click(function() {
+    jQuery("#PP_USRLOCKEDTXT").show("slow");
+  });
+  jQuery("#PP_LoginAttempts_false").click(function() {
+    jQuery("#PP_USRLOCKEDTXT").hide("slow");
+  });
+  
+
   jQuery('.cluetip').cluetip(
-  {ldelim}
-    width: 500,
+  {
+    width: 550,
     splitTitle: '|'
-  {rdelim});
-{rdelim});
-</script>
+  });
+});
+{/literal}{/footer_script}
+
 
 <div class="titrePage">
@@ -69,4 +78,35 @@
         </label>
       </li>
+
+      <li>
+        <label class="cluetip" title="{'PP_LoginAttempts'|translate}|{'PP_LoginAttempts_d'|translate}">
+          {'PP_LoginAttempts'|@translate}
+        </label>
+
+        <label for="PP_LoginAttempts_false"><input id="PP_LoginAttempts_false" value="false" {$PP_LOGFAILEDPASSW_FALSE} name="PP_LogFailedPassw" type="radio"/>
+          {'PP_Disable'|@translate}
+        </label>
+        <label for="PP_LoginAttempts_true"><input id="PP_LoginAttempts_true" value="true" {$PP_LOGFAILEDPASSW_TRUE} name="PP_LogFailedPassw" type="radio"/>
+          {'PP_Enable'|@translate}
+        </label>
+      </li>
+
+      <ul>
+        <li id="PP_USRLOCKEDTXT" {if $PP_LOGFAILEDPASSW_FALSE} style="display: none"{/if}>
+          <label class="cluetip" title="{'PP_Max number of failed attempts'|translate}|{'PP_Max number of failed attempts_d'|translate}">
+            {'PP_Max number of failed attempts'|@translate}&nbsp;&nbsp;<input type="text" name="PP_NbFailedPassw" value="{$PP_NBLOGFAIL}" size="5" style="text-align: center;"/>
+          </label>
+<br />
+          <label class="cluetip" title="{'PP_UserLocked_Custom_Txt'|translate}|{'PP_UserLocked_Custom_Txt_d'|translate}">
+            {'PP_UserLocked_Custom_Txt'|@translate}
+          </label>
+          <textarea class="pp_textfields" name="PP_CustomLockMsg" id="PP_CustomLockMsg" rows="10" {$TAG_INPUT_ENABLED}>{$PP_USRLOCKEDTXT}</textarea>
+          {if 'FCK_PATH'|@defined}
+            <div style="text-align:right;">
+              <a href="#" onClick="toogleEditor('PP_CustomLockMsg'); return false;">FCK Editor On/Off</a>
+            </div>
+          {/if}
+        </li>
+      </ul>
     </ul>
 
Index: /extensions/Password_Policy/admin/template/pp.css
===================================================================
--- /extensions/Password_Policy/admin/template/pp.css	(revision 25088)
+++ /extensions/Password_Policy/admin/template/pp.css	(revision 25089)
@@ -17,2 +17,7 @@
   margin-bottom: 5px;
 }
+
+textarea.pp_textfields
+{
+  width: 70%;
+}
Index: /extensions/Password_Policy/include/functions.inc.php
===================================================================
--- /extensions/Password_Policy/include/functions.inc.php	(revision 25088)
+++ /extensions/Password_Policy/include/functions.inc.php	(revision 25089)
@@ -30,5 +30,5 @@
  * Triggered on loc_begin_index
  * 
- * Initiating GhostTracker - Perform user logout after registration if not validated
+ * Initiating GhostTracker - Perform user logout after registration if account locked
  */
 function PP_Init()
@@ -45,19 +45,66 @@
   {
     // Perform user logout if user account is locked
-    if ((isset($conf_PP['LOGFAILBLOCK']) and $conf_PP['LOGFAILBLOCK'] == 'true')
-          and PP_UsrBlock_Verif($user['id'])
-          and !is_admin()
-          and !is_webmaster())
+    if (
+        (isset($conf_PP['LOGFAILBLOCK']) and $conf_PP['LOGFAILBLOCK'] == 'true')
+        and PP_UsrBlock_Verif($user['username'])
+        and !is_admin()
+        and !is_webmaster()
+        )
     {
       invalidate_user_cache();
       logout_user();
-      if ( $conf['guest_access'] )
-      {
-        redirect( make_index_url().'?PP_msg=locked', 0);
+      if ($conf['guest_access'])
+      {
+        redirect(make_index_url().'?PP_msg=locked', 0);
       }
       else
       {
-        redirect( get_root_url().'identification.php?PP_msg=locked' , 0);
-      }
+        redirect(get_root_url().'identification.php?PP_msg=locked' , 0);
+      }
+    }
+  }
+}
+
+
+/**
+ * Triggered on init
+ * 
+ * Displays messages on index page
+ */
+function PP_InitPage()
+{
+  global $conf, $template, $page, $lang, $errors;
+
+  load_language('plugin.lang', PP_PATH);
+
+  if( isset($_GET['PP_msg']))
+  {
+    PP_DisplayMsg();
+  }
+}
+
+
+/**
+ * Triggered on init
+ * 
+ * Display a message according to $_GET['PP_msg']
+ */
+function PP_DisplayMsg()
+{
+  if (isset($_GET['PP_msg']))
+  {
+    global $user, $lang, $conf, $page;
+    $conf_PP = unserialize($conf['PasswordPolicy']);
+
+    // User account locked after x failed attempts
+    if (isset($conf_PP['USRLOCKEDTXT']) and !empty($conf_PP['USRLOCKEDTXT']) and $_GET['PP_msg']=="locked")
+    {
+      if (function_exists('get_user_language_desc'))// Extended Description [lang] feature
+      {
+        $custom_text = get_user_language_desc($conf_PP['USRLOCKEDTXT']);
+      }
+      else $custom_text = l10n($conf_PP['USRLOCKEDTXT']);
+
+      $page["errors"][]=$custom_text;
     }
   }
@@ -70,7 +117,9 @@
  *
  */
-function PP_log_fail()
+function PP_log_fail($username)
 {
   global $conf, $user;
+
+  include_once(PHPWG_ROOT_PATH.'admin/include/functions.php');
 
   $conf_PP = unserialize($conf['PasswordPolicy']);
@@ -87,5 +136,5 @@
 UPDATE '.USERS_TABLE.'
 SET PP_loginfailcount = PP_loginfailcount+1
-WHERE user_id = '.$userid.'
+WHERE username = "'.stripslashes($username).'"
 LIMIT 1
 ;';
@@ -95,32 +144,35 @@
 SELECT PP_loginfailcount
 FROM '.USERS_TABLE.'
-WHERE user_id = '.$userid.'
-;';
-
-    $datas = pwg_query($query);
-
-    // If number of failed logon exeeds 3, set the account as locked
-    if (isset($datas['PP_loginfailcount']) and $datas['PP_loginfailcount'] > $conf_PP['NBLOGFAIL'])
+WHERE username = "'.stripslashes($username).'"
+;';
+
+    $datas = pwg_db_fetch_assoc(pwg_query($query));
+
+    // If number of failed logon exeeds $conf_PP['NBLOGFAIL'], set the account as locked
+    if (isset($datas['PP_loginfailcount']) and $datas['PP_loginfailcount'] >= $conf_PP['NBLOGFAIL'])
     {
       $query = '
 UPDATE '.USERS_TABLE.'
 SET PP_lock = "true"
-WHERE user_id = '.$userid.'
+WHERE username = "'.stripslashes($username).'"
 LIMIT 1
 ;';
       pwg_query($query);
-    }
-  }
-}
-
-
-/**
- * PP_loc_visible_user_list
+
+    }
+  }
+}
+
+
+/**
+ * PP_user_list_pwdreset
  * Adds a new feature in user_list to allow password reset for selected users by admin
  * 
  */
-function PP_loc_visible_user_list($visible_user_list)
+function PP_user_list_pwdreset($visible_user_list)
 {
   global $template;
+  
+  load_language('plugin.lang', PP_PATH);
 
   $template->append('plugin_user_list_column_titles', l10n('PP_PwdReset'));
@@ -166,4 +218,56 @@
 
 /**
+ * PP_user_list_locked
+ * Adds a new feature in user_list to allow password reset for selected users by admin
+ * 
+ */
+function PP_user_list_locked($visible_user_list)
+{
+  global $template;
+  
+  load_language('plugin.lang', PP_PATH);
+
+  $template->append('plugin_user_list_column_titles', l10n('PP_LockedUsers'));
+
+  $user_ids = array();
+
+  foreach ($visible_user_list as $i => $user)
+  {
+    $user_ids[$i] = $user['id'];
+  }
+
+  $user_nums = array_flip($user_ids);
+
+  // Query to get information in database
+  // ------------------------------------
+  if (!empty($user_ids))
+  {
+    $query = '
+SELECT DISTINCT id, PP_lock
+  FROM '.USERS_TABLE.'
+  WHERE id IN ('.implode(',', $user_ids).')
+;';
+    $result = pwg_query($query);
+
+    while ($row = pwg_db_fetch_assoc($result))
+    {
+      if ($row['PP_lock'] == 'false')
+      {
+        $LockedUser = '<img src="'.PP_PATH.'admin/template/icons/nolock.png" title="'.l10n('PP_User Not Locked').'" alt="'.l10n('PP_User Not Locked').'"/>';
+      }
+      else if ($row['PP_lock'] == 'true')
+      {
+        $LockedUser = '<img src="'.PP_PATH.'admin/template/icons/lock.png" title="'.l10n('PP_User Locked').'" alt="'.l10n('PP_User Locked').'"/>';
+      }
+      else $LockedUser = '<img src="'.PP_PATH.'admin/template/icons/nolock.png" title="'.l10n('PP_User Not Locked').'" alt="'.l10n('PP_User Not Locked').'"/>';
+
+		  $visible_user_list[$user_nums[$row['id']]]['plugin_columns'][] = $LockedUser; // Shows users account state in user_list
+    }
+  }
+  return $visible_user_list;
+}
+
+
+/**
  * Triggered on login_success
  * 
@@ -178,4 +282,27 @@
 
   $conf_PP = unserialize($conf['PasswordPolicy']);
+
+  // Perfoming redirection for locked accounts
+  // -----------------------------------------
+  if (!is_admin() and !is_a_guest() and $user['username'] != "16" and $user['username'] != "18")
+  {
+    // Perform user logout if user account is locked
+    if ((isset($conf_PP['LOGFAILBLOCK']) and $conf_PP['LOGFAILBLOCK'] == 'true')
+          and PP_UsrBlock_Verif($user['username'])
+          and !is_admin()
+          and !is_webmaster())
+    {
+      invalidate_user_cache();
+      logout_user();
+      if ($conf['guest_access'])
+      {
+        redirect(make_index_url().'?PP_msg=locked', 0);
+      }
+      else
+      {
+        redirect(get_root_url().'identification.php?PP_msg=locked' , 0);
+      }
+    }
+  }
 
   // Performing redirection to profile page for password reset
@@ -227,5 +354,5 @@
       {
         $PasswordCheck = PP_testpassword($user['password']);
-  
+
         if ($PasswordCheck < $conf_PP['PASSWORD_SCORE'])
         {
@@ -238,5 +365,5 @@
       {
         $PasswordCheck = PP_testpassword($user['password']);
-  
+
         if ($PasswordCheck < $conf_PP['PASSWORD_SCORE'])
         {
@@ -247,4 +374,5 @@
       }
     }
+    return $errors;
   }
 }
@@ -257,4 +385,6 @@
 {
   global $conf, $user, $template;
+  
+  load_language('plugin.lang', PP_PATH);
 
   $conf_PP = unserialize($conf['PasswordPolicy']);
@@ -458,5 +588,5 @@
  * @returns : True if account is locked else False
  */
-function PP_UsrBlock_Verif($user_id)
+function PP_UsrBlock_Verif($username)
 {
   global $conf;
@@ -465,5 +595,5 @@
 SELECT PP_Lock
 FROM '.USERS_TABLE.'
-WHERE id='.$user_id.'
+WHERE username = "'.stripslashes($username).'"
 ;';
 
@@ -475,4 +605,32 @@
   }
   else return false;
+}
+
+
+/**
+ * PP_unlock_user
+ * Action in user_list to unlock a user
+ */
+function PP_unlock_user($uid)
+{
+  // Reset PP_loginfailcount value to 0
+  $query ='
+UPDATE '.USERS_TABLE.'
+SET PP_loginfailcount = 0
+WHERE id = '.$uid.'
+LIMIT 1
+;';
+
+  pwg_query($query);
+  
+  // Set account as unlocked
+  $query ='
+UPDATE '.USERS_TABLE.'
+SET PP_lock = "false"
+WHERE id = '.$uid.'
+LIMIT 1
+;';
+
+  pwg_query($query);
 }
 
@@ -530,3 +688,21 @@
   return $plugin ;
 }
+
+
+/**
+ * Useful for debugging - 4 vars can be set
+ * Output result to log.txt file
+ *
+ */
+function PPLog($var1, $var2, $var3, $var4)
+{
+   $fo=fopen (PP_PATH.'log.txt','a') ;
+   fwrite($fo,"======================\n") ;
+   fwrite($fo,'le ' . date('D, d M Y H:i:s') . "\r\n");
+   fwrite($fo,$var1 ."\r\n") ;
+   fwrite($fo,$var2 ."\r\n") ;
+   fwrite($fo,$var3 ."\r\n") ;
+   fwrite($fo,$var4 ."\r\n") ;
+   fclose($fo) ;
+}
 ?>
Index: /extensions/Password_Policy/language/en_UK/plugin.lang.php
===================================================================
--- /extensions/Password_Policy/language/en_UK/plugin.lang.php	(revision 25088)
+++ /extensions/Password_Policy/language/en_UK/plugin.lang.php	(revision 25089)
@@ -6,13 +6,13 @@
 $lang['PP_Disable'] = ' Disable (default)';
 $lang['PP_Enable'] = ' Enable ';
-$lang['PP_Support_txt'] = 'The official support on this plugin is only on these Piwigo forum topic:<br>
+$lang['PP_Support_txt'] = 'The official support on this plugin is only on these Piwigo forum topic:<br/>
 <a href="http://piwigo.org/forum/viewtopic.php?id=" onclick="window.open(this.href);return false;">English forum - http://piwigo.org/forum/viewtopic.php?id=</a>';
 $lang['PP_PasswordTest'] = 'Score calculation';
 $lang['PP_ScoreTest'] = 'Result: ';
 $lang['PP_Error_Password_Mandatory'] = 'Security : Password is mandatory !';
-$lang['PP_Error_Password_Need_Enforcement_%s'] = 'Security : A control system calculates a score on the chosen passwords complexity. The complexity of your password is too low (score = %s). Please, choose a new password more secure by following these rules:<br>
-- Use letters and numbers<br>
-- Use lowercase and uppercase<br>
-- Increase its length (number of characters)<br>
+$lang['PP_Error_Password_Need_Enforcement_%s'] = 'Security : A control system calculates a score on the chosen passwords complexity. The complexity of your password is too low (score = %s). Please, choose a new password more secure by following these rules:<br/>
+- Use letters and numbers<br/>
+- Use lowercase and uppercase<br/>
+- Increase its length (number of characters)<br/>
 The minimum passwords score required by the administrator is: ';
 $lang['PP_Password_Enforced'] = 'Strengthening the security level of passwords';
@@ -36,14 +36,36 @@
 $lang['PP_passwtestTitle'] = 'Testing the complexity of a password';
 $lang['PP_passwtestTitle_d'] = 'Enter the password to test and then click on &quot;Score calculation&quot; to see the result.';
-$lang['PP_passwTitle_d'] = 'Enabling this option makes mandatory the seizure of a password upon registration, and requires the password chosen by the visitor to meet a minimum level of complexity. If the threshold is not reached, the score achieved and the minimum score to be achieved are displayed, along with recommendations to increase the value of this score.<br><br>
-There is field test to measure the complexity of a password, and can afford to get an idea of the score to define complex custom.<br><br>
+$lang['PP_passwTitle_d'] = 'Enabling this option makes mandatory the seizure of a password upon registration, and requires the password chosen by the visitor to meet a minimum level of complexity. If the threshold is not reached, the score achieved and the minimum score to be achieved are displayed, along with recommendations to increase the value of this score.<br/><br/>
+There is field test to measure the complexity of a password, and can afford to get an idea of the score to define complex custom.<br/><br/>
 Note: The score of a password is calculated based on several parameters: length, type of characters used (letters, digits, uppercase, lowercase, special characters). A score below 100 is considered low, from 100 to 500, the complexity is average; beyond 500, the security is excellent.';
-$lang['PP_passwadmTitle_d'] = 'An administrator can create a user account with or without application of the rule of computing complexity.<br><br>
+$lang['PP_passwadmTitle_d'] = 'An administrator can create a user account with or without application of the rule of computing complexity.<br/><br/>
 Note: If the user account created wants to change password and strengthening passwords for users is active, it will be subject to the rule set.';
-$lang['PP_PwdResetTitle_d'] = 'Enabling this option will add a new feature in the Piwigo\'s user management panel (Users> Manage) that can apply to renew the password for selected users. In addition, a new column is added to show the status of passwords for each of them with the following values:<br>
-- Password to be renewed: A password renewal request has been planned.<br>
-- Password renewed: The password has been renewed after a request.<br>
-- Original Password: The original password chosen at account creation and has never been required for a renewal.<br>
-Webmaster, generic and guest accounts are excluded from this function.<br>
+$lang['PP_PwdResetTitle_d'] = 'Enabling this option will add a new feature in the Piwigo\'s user management panel (Users> Manage) that can apply to renew the password for selected users. In addition, a new column is added to show the status of passwords for each of them with the following values:<br/><br/>
+- Password to be renewed: A password renewal request has been planned.<br/>
+- Password renewed: The password has been renewed after a request.<br/>
+- Original Password: The original password chosen at account creation and has never been required for a renewal.<br/><br/>
+<b style=&quot;color: red;&quot;>Webmaster, generic and guest accounts are excluded from this function.</b><br/><br/>
 These users will be automatically redirected to their customization page at each of their connections until they changed their password and an explicit message will be displayed on this page.';
+$lang['PP_User_Account_Locked_Txt'] = 'Sorry, for security reason your access to the gallery has been locked due to too many failed login attempts. This may be due to a hacking attempt on your account. Please contact the site administrator to request the unlocking.';
+$lang['PP_LoginAttempts'] = 'Management of failed login attempts';
+$lang['PP_LoginAttempts_d'] = 'This option enables the recording of failed login attempts to the gallery due to incorrect password and automatically lock the concerned user account. It is designed to counter possible hacking attempts with the use of password dictionaries.<br/><br/>
+<b style=&quot;color: red;&quot;>Webmaster, generic and guest accounts are excluded from this function.</b><br/><br/>
+By enabling, you will be able to specify the maximum number of failed attempts allowed before locking and define a custom message to the user who has locked his account. This message is only displayed if the user succeeds in identification after locking.<br/><br/>
+To unlock the accounts, go to the user management interface for your gallery. You\'ll find a new column indicating the status of accounts symbolized by a red symbol (for locked account) and green (for unlocked account). Select one or more accounts to unlock and use the unlock button designed for this purpose.';
+$lang['PP_Max number of failed attempts'] = 'Maximum number of failed attempts: ';
+$lang['PP_Max number of failed attempts_d'] = 'The number 0 disables counting attempts.';
+$lang['PP_UserLocked_Custom_Txt'] = 'Customizing the information message to the locked user';
+$lang['PP_UserLocked_Custom_Txt_d'] = 'Here you can change the text of the message displayed to the user whose account has been locked. To use multiple languages, you can use the Extended description plugin\'s tags if it is active.';
+$lang['PP_LockedUsers'] = 'Locking state';
+$lang['PP_Unlock'] = 'Unlocking';
+$lang['PP_Unlock selected users'] = 'Unlock selected users';
+$lang['PP_Guest is not unlockable'] = 'Guest users can not be locked and can not be unlocked';
+$lang['PP_Default user is not unlockable'] = 'Default user can not be locked and can not be unlocked';
+$lang['PP_Webmaster is not unlockable'] = 'Webmaster users can not be locked and can not be unlocked';
+$lang['PP_You cannot unlock your account'] = 'You can not unlock your own account';
+$lang['PP_Generic is not unlockable'] = 'Generic users can not be locked and can not be unlocked';
+$lang['PP_Admins is not unlockable'] = 'Admin users can not be locked and can not be unlocked';
+$lang['PP_User Not Locked'] = 'User not locked';
+$lang['PP_User Locked'] = 'User locked';
+$lang['PP_You need to confirm unlock'] = 'You must confirm unlocking (check box)!';
 ?>
Index: /extensions/Password_Policy/language/fr_FR/plugin.lang.php
===================================================================
--- /extensions/Password_Policy/language/fr_FR/plugin.lang.php	(revision 25088)
+++ /extensions/Password_Policy/language/fr_FR/plugin.lang.php	(revision 25089)
@@ -6,13 +6,13 @@
 $lang['PP_Disable'] = ' Désactiver (valeur par défaut)';
 $lang['PP_Enable'] = ' Activer ';
-$lang['PP_Support_txt'] = 'Le support officiel sur ce plugin se fait exclusivement sur ce fil du forum FR de Piwigo:<br>
+$lang['PP_Support_txt'] = 'Le support officiel sur ce plugin se fait exclusivement sur ce fil du forum FR de Piwigo:<br/>
 <a href="http://fr.piwigo.org/forum/viewtopic.php?id=" onclick="window.open(this.href);return false;">Forum français - http://fr.piwigo.org/forum/viewtopic.php?id=</a>';
 $lang['PP_PasswordTest'] = 'Calcul du score';
 $lang['PP_ScoreTest'] = 'Résultat: ';
 $lang['PP_Error_Password_Mandatory'] = 'Sécurité : Le mot de passe est obligatoire !';
-$lang['PP_Error_Password_Need_Enforcement_%s'] = 'Sécurité : Un système de contrôle calcule un score de complexité sur les mots de passe choisis. La complexité de votre mot de passe est trop faible (score = %s). Veuillez choisir un nouveau mot de passe plus sûr en respectant les règles suivantes :<br>
-- Utiliser des lettres et des chiffres<br>
-- Utiliser des minuscules et des majuscules<br>
-- Augmenter sa longueur (nombre de caractères)<br>
+$lang['PP_Error_Password_Need_Enforcement_%s'] = 'Sécurité : Un système de contrôle calcule un score de complexité sur les mots de passe choisis. La complexité de votre mot de passe est trop faible (score = %s). Veuillez choisir un nouveau mot de passe plus sûr en respectant les règles suivantes :<br/>
+- Utiliser des lettres et des chiffres<br/>
+- Utiliser des minuscules et des majuscules<br/>
+- Augmenter sa longueur (nombre de caractères)<br/>
 Le score minimal des mots de passe imposé par l\'administrateur est de : ';
 $lang['PP_Password_Enforced'] = 'Renforcement de la sécurité des mots de passe';
@@ -36,14 +36,36 @@
 $lang['PP_passwtestTitle'] = 'Tester la complexité d\'un mot de passe';
 $lang['PP_passwtestTitle_d'] = 'Saisir le mot de passe à tester puis cliquer sur le bouton &quot;Calcul du score&quot; pour afficher le résultat.';
-$lang['PP_passwTitle_d'] = 'L\'activation de cette option rend obligatoire la saisie d\'un mot de passe  à l\'inscription, et oblige le mot de passe choisi par le visiteur à répondre à un niveau minimum de complexité. Si ce seuil n\'est pas atteint, le score réalisé et le score minimum à atteindre sont affichés, ainsi que des recommandations pour augmenter la valeur de ce score.<br><br>
-Un champ de test permet de mesurer la complexité d\'un mot de passe, et peut permettre de se faire une idée du score à définir pour une complexité personnalisée.<br><br>
+$lang['PP_passwTitle_d'] = 'L\'activation de cette option rend obligatoire la saisie d\'un mot de passe  à l\'inscription, et oblige le mot de passe choisi par le visiteur à répondre à un niveau minimum de complexité. Si ce seuil n\'est pas atteint, le score réalisé et le score minimum à atteindre sont affichés, ainsi que des recommandations pour augmenter la valeur de ce score.<br/><br/>
+Un champ de test permet de mesurer la complexité d\'un mot de passe, et peut permettre de se faire une idée du score à définir pour une complexité personnalisée.<br/><br/>
 NB: Le score d\'un mot de passe est calculé en fonction de plusieurs paramètres : longueur, types de caractères utilisés (lettres, chiffres, majuscules, minuscules, caractères spéciaux). Un score inférieur à 100 est considéré comme faible; de 100 à 500, la complexité est dans la moyenne; au-delà de 500, la sécurité est excellente.';
-$lang['PP_passwadmTitle_d'] = 'Un administrateur peut créer un compte d\'utilisateur avec ou sans application de la règle de calcul de complexité.<br><br>
+$lang['PP_passwadmTitle_d'] = 'Un administrateur peut créer un compte d\'utilisateur avec ou sans application de la règle de calcul de complexité.<br/><br/>
 A noter : Si l\'utilisateur du compte ainsi créé désire changer de mot de passe et que le renforcement des mots de passe pour les utilisateurs est actif, il sera soumis à la règle configurée.';
-$lang['PP_PwdResetTitle_d'] = 'L\'activation de cette option ajoutera une nouvelle fonction dans le panneau de gestion de utilisateurs (Utilisateurs > Gérer) de Piwigo qui permet de demander le renouvellement du mot de passe pour les utilisateurs sélectionnés. De plus, une nouvelle colonne s\'ajoutera pour présenter le statut des mots de passe pour chacun d\'eux avec les valeurs suivantes :<br>
-- Mot de passe à changer : Une demande de changement de mot de passe a été programmée.<br>
-- Mot de passe modifié : Le mot de passe a été changé suite à une demande.<br>
-- Mot de passe original : Le mot de passe est celui choisi à la création du compte et n\'a jamais fait l\'objet d\'une demande de changement.<br>
-Le compte webmaster, génériques et invités (Guest) sont exclus de cette fonction.<br><br>
+$lang['PP_PwdResetTitle_d'] = 'L\'activation de cette option ajoutera une nouvelle fonction dans le panneau de gestion de utilisateurs (Utilisateurs > Gérer) de Piwigo qui permet de demander le renouvellement du mot de passe pour les utilisateurs sélectionnés. De plus, une nouvelle colonne s\'ajoutera pour présenter le statut des mots de passe pour chacun d\'eux avec les valeurs suivantes :<br/><br/>
+- Mot de passe à changer : Une demande de changement de mot de passe a été programmée.<br/>
+- Mot de passe modifié : Le mot de passe a été changé suite à une demande.<br/>
+- Mot de passe original : Le mot de passe est celui choisi à la création du compte et n\'a jamais fait l\'objet d\'une demande de changement.<br/><br/>
+<b style=&quot;color: red;&quot;>Le compte webmaster, génériques et invités (Guest) sont exclus de cette fonction.</b><br/><br/>
 Ces utilisateurs seront alors systématiquement redirigés vers leur page de personnalisation à chacune de leurs connexions tant qu\'ils n\'auront pas modifié leur mot de passe. Un message explicite sera affiché sur cette page.';
+$lang['PP_User_Account_Locked_Txt'] = 'Désolé, pour des raisons de sécurité votre accès à la galerie a été verrouillé en raison d\'un trop grand nombre de tentatives de connexion infructueuses. Ceci peut être dû à une tentative de piratage de votre compte. Veuillez contacter l\'administrateur du site pour en demander le déverrouillage.';
+$lang['PP_LoginAttempts'] = 'Gestion des tentatives de connexion echouées';
+$lang['PP_LoginAttempts_d'] = 'Cette option permet de comptabiliser les tentatives échouées de connexion à la galerie pour cause de mot de passe erroné et de verrouiller automatiquement le compte d\'utilisateur concerné. Elle est prévue pour contrer d\'éventuelles tentatives de piratage par usage de dictionnaires de mots de passe.<br/><br/>
+<b style=&quot;color: red;&quot;>Le compte webmaster, génériques et invités (Guest) sont exclus de cette fonction.</b><br/><br/>
+En l\'activant, vous aurez la possibilité de préciser le nombre maximum de tentatives autorisées avant verrouillage et de définir un message personnalisé d\'information à l\'utilisateur qui aura son compte verrouillé. Ce message n\'est affiché que si l\'utilisateur en question arrive à s\'identifier correctement après le verrouillage.<br/><br/>
+Pour déverrouiller les comptes, rendez-vous dans l\'interface de gestion des utilisateurs de votre galerie. Vous y trouverez une nouvelle colonne indiquant l\'état des comptes symbolisé par un pictogramme rouge (pour compte verrouillé) et vert (pour compte non verrouillé). Sélectionnez le ou les compte(s) à déverrouiller et utiliser le bouton de déverrouillage prévu à cet effet.';
+$lang['PP_Max number of failed attempts'] = 'Nombre maximum de tentatives : ';
+$lang['PP_Max number of failed attempts_d'] = 'Le chiffre 0 désactive le comptage des tentatives.';
+$lang['PP_UserLocked_Custom_Txt'] = 'Personnalisation du message d\'information à l\'utilisateur verrouillé';
+$lang['PP_UserLocked_Custom_Txt_d'] = 'Vous pouvez modifier ici le texte du message affiché à l\'utilisateur dont le compte est verrouillé. Pour une utilisation multi-langues, vous pouvez utiliser les balises [lang] du plugin Extended Description si celui-ci est actif.';
+$lang['PP_LockedUsers'] = 'Verrouillages';
+$lang['PP_Unlock'] = 'Déverrouillage';
+$lang['PP_Unlock selected users'] = 'Déverrouiller les utilisateurs sélectionnés';
+$lang['PP_Guest is not unlockable'] = 'Les utilisateurs invités ne sont pas verrouillables et ne peuvent pas être déverrouillés';
+$lang['PP_Default user is not unlockable'] = 'Les utilisateurs par défaut ne sont pas verrouillables et ne peuvent pas être déverrouillés';
+$lang['PP_Webmaster is not unlockable'] = 'Les utilisateurs webmaster ne sont pas verrouillables et ne peuvent pas être déverrouillés';
+$lang['PP_You cannot unlock your account'] = 'Vous ne pouvez pas déverrouiller votre propre compte';
+$lang['PP_Generic is not unlockable'] = 'Les utilisateurs génériques ne sont pas verrouillables et ne peuvent pas être déverrouillés';
+$lang['PP_Admins is not unlockable'] = 'Les utilisateurs administrateurs ne sont pas verrouillables et ne peuvent pas être déverrouillés';
+$lang['PP_User Not Locked'] = 'Utilisateur non verrouillé';
+$lang['PP_User Locked'] = 'Utilisateur verrouillé';
+$lang['PP_You need to confirm unlock'] = 'Vous devez confirmer le déverrouillage (case à cocher) !';
 ?>
Index: /extensions/Password_Policy/main.inc.php
===================================================================
--- /extensions/Password_Policy/main.inc.php	(revision 25088)
+++ /extensions/Password_Policy/main.inc.php	(revision 25089)
@@ -2,5 +2,5 @@
 /*
 Plugin Name: Password Policy
-Version: 2.5.0
+Version: auto
 Description: Renforcer la sécurité des mots de passe - Enforce password security
 Plugin URI: http://piwigo.org/ext/extension_view.php?eid=
@@ -19,4 +19,5 @@
 
 load_language('plugin.lang', PP_PATH);
+
 $conf_PP = unserialize($conf['PasswordPolicy']);
 
@@ -29,4 +30,8 @@
 // ---------------------------------------
 add_event_handler('loc_begin_index', 'PP_Init');
+
+// Display messages on index page
+// ------------------------------
+add_event_handler('init', 'PP_InitPage');
 
 // Check users registration
@@ -41,5 +46,9 @@
 // Redirection to profile page
 // ---------------------------
-add_event_handler('login_success', 'PP_LoginTasks',EVENT_HANDLER_PRIORITY_NEUTRAL, 1);
+add_event_handler('login_success', 'PP_LoginTasks',EVENT_HANDLER_PRIORITY_NEUTRAL+10, 1);
+
+// Security option : Count of login failure and lock account after x attempt
+// -------------------------------------------------------------------------
+add_event_handler('login_failure', 'PP_log_fail');
 
 // Add new feature in user_list - Password Reset
@@ -49,5 +58,5 @@
   // Add new column on user_list
   // ---------------------------
-  add_event_handler('loc_visible_user_list', 'PP_loc_visible_user_list');
+  add_event_handler('loc_visible_user_list', 'PP_user_list_pwdreset');
 
   // Add prefilter on user_list
@@ -66,4 +75,6 @@
     $page['infos'] = array();
     $page['filtered_users'] = array();
+
+    load_language('plugin.lang', PP_PATH);
 
     if (isset($_POST['pwdreset']))
@@ -230,6 +241,190 @@
 
 
-// Security option : Count of login failure and lock account after x attempt
-// -------------------------------------------------------------------------
-add_event_handler('login_failure', 'PP_log_fail');
+// Add new feature in user_list - Show locked accounts and give unlock function
+// ----------------------------------------------------------------------------
+if (isset($conf_PP['LOGFAILBLOCK']) and $conf_PP['LOGFAILBLOCK']=='true')
+{
+  // Add new column on user_list
+  // ---------------------------
+  add_event_handler('loc_visible_user_list', 'PP_user_list_locked');
+
+  // Add prefilter on user_list
+  // --------------------------
+  add_event_handler('loc_begin_admin', 'PP_Unlock_Action',60);
+
+  /**
+   * PP_Unlock_Action - Triggered on PP_Unlock_Action
+   * Handle unlocking action in user_list.php
+   */
+  function PP_Unlock_Action()
+  {
+    global $conf, $user, $template, $lang, $errors;
+
+    $page['errors'] = array();
+    $page['infos'] = array();
+    $page['filtered_users'] = array();
+
+    load_language('plugin.lang', PP_PATH);
+
+    if (isset($_POST['unlock']))
+    {
+      $collection = array();
+
+      switch ($_POST['target'])
+      {
+        case 'all' :
+        {
+          foreach($page['filtered_users'] as $local_user)
+          {
+            array_push($collection, $local_user['id']);
+          }
+          break;
+        }
+        case 'selection' :
+        {
+          if (isset($_POST['selection']))
+          {
+            $collection = $_POST['selection'];
+          }
+          break;
+        }
+      }
+
+      if (count($collection) == 0)
+      {
+        array_push($page['errors'], l10n('Select at least one user'));
+      }
+    }
+
+    if (isset($_POST['unlock']) and count($collection) > 0)
+    {
+      if (in_array($conf['guest_id'], $collection))
+      {
+        array_push($page['errors'], l10n('PP_Guest is not unlockable'));
+        $template->append('errors', l10n('PP_Guest is not unlockable'));
+      }
+      if (($conf['guest_id'] != $conf['default_user_id']) and
+        in_array($conf['default_user_id'], $collection))
+      {
+        array_push($page['errors'], l10n('PP_Default user is not unlockable'));
+        $template->append('errors', l10n('PP_Default user is not unlockable'));
+      }
+      if (in_array($conf['webmaster_id'], $collection))
+      {
+        array_push($page['errors'], l10n('PP_Webmaster is not unlockable'));
+        $template->append('errors', l10n('PP_Webmaster is not unlockable'));
+      }
+      if (in_array($user['id'], $collection))
+      {
+        array_push($page['errors'], l10n('PP_You cannot unlock your account'));
+        $template->append('errors', l10n('PP_You cannot unlock your account'));
+      }
+
+      // Generic accounts exclusion (including Adult_Content generic users)
+      // ------------------------------------------------------------------
+      $query ='
+SELECT u.id
+FROM '.USERS_TABLE.' AS u
+INNER JOIN '.USER_INFOS_TABLE.' AS ui
+  ON u.id = ui.user_id
+WHERE ui.status = "generic"
+;';
+
+	    $result = pwg_query($query);
+
+      while ($row = pwg_db_fetch_assoc($result))
+      {
+        if (in_array($row['id'], $collection))
+        {
+          array_push($page['errors'], l10n('PP_Generic is not unlockable'));
+          $errors = l10n('PP_Generic is not unlockable');
+        }
+      }
+
+      // Admins accounts exclusion
+      // --------------------------
+      $query ='
+SELECT u.id
+FROM '.USERS_TABLE.' AS u
+INNER JOIN '.USER_INFOS_TABLE.' AS ui
+  ON u.id = ui.user_id
+WHERE ui.status = "admin"
+;';
+
+	    $result = pwg_query($query);
+
+      while ($row = pwg_db_fetch_assoc($result))
+      {
+        if (in_array($row['id'], $collection))
+        {
+          array_push($page['errors'], l10n('PP_Admins is not unlockable'));
+          $errors = l10n('PP_Admins is not unlockable');
+        }
+      }
+
+      $template->append('errors', $errors);
+
+      if (count($page['errors']) == 0)
+      {
+        if (isset($_POST['confirm_unlock']) and 1 == $_POST['confirm_unlock'])
+        {
+          foreach ($collection as $user_id)
+          {
+            PP_unlock_user($user_id);
+          }
+          array_push(
+            $page['infos'],
+            l10n_dec(
+              'PP %d user unlocked', 'PP %d users unlocked',
+              count($collection)
+              )
+            );
+          $template->append('infos', l10n_dec(
+              'PP %d user unlocked', 'PP %d users unlocked',
+              count($collection)));
+          foreach ($page['filtered_users'] as $filter_key => $filter_user)
+          {
+            if (in_array($filter_user['id'], $collection))
+            {
+              unset($page['filtered_users'][$filter_key]);
+            }
+          }
+        }
+        else
+        {
+          array_push($page['errors'], l10n('PP_You need to confirm unlock'));
+          $template->append('errors', l10n('PP_You need to confirm unlock'));
+        }
+      }
+    }
+    $template->set_prefilter('user_list', 'PP_Unlocking_Prefilter');
+  }
+
+  /**
+   * PP_Unlocking_Prefilter
+   * Adds action field for user unlocking in user_list.tpl
+   */
+  function PP_Unlocking_Prefilter($content, &$smarty)
+  {
+    $search = '
+<fieldset>
+  <legend>{\'Deletions\'|@translate}</legend>
+  <label><input type="checkbox" name="confirm_deletion" value="1"> {\'confirm\'|@translate}</label>
+  <input class="submit" type="submit" value="{\'Delete selected users\'|@translate}" name="delete">
+</fieldset>
+';
+ 
+    $addon = '
+<fieldset>
+  <legend>{\'PP_Unlock\'|@translate}</legend>
+  <label><input type="checkbox" name="confirm_unlock" value="1"> {\'confirm\'|@translate}</label>
+  <input class="submit" type="submit" value="{\'PP_Unlock selected users\'|@translate}" name="unlock">
+</fieldset>
+';
+
+    $replacement = $addon.$search;
+
+    return str_replace($search, $replacement, $content);
+  }
+}
 ?>
Index: /extensions/Password_Policy/maintain.inc.php
===================================================================
--- /extensions/Password_Policy/maintain.inc.php	(revision 25088)
+++ /extensions/Password_Policy/maintain.inc.php	(revision 25089)
@@ -32,7 +32,7 @@
     'ADMINPASSWENF'               => 'false',
     'PWDRESET'                    => 'false',
+    'LOGFAILBLOCK'                => 'false',
     'NBLOGFAIL'                   => '0',
-    'LOGFAILBLOCK'                => 'false',
-    'USRLOCKED'                   => l10n('PP_User_Account_Locked_Txt')
+    'USRLOCKEDTXT'                => l10n('PP_User_Account_Locked_Txt')
   );
 
@@ -95,5 +95,5 @@
     $q = '
 ALTER TABLE '.USERS_TABLE.'
-ADD PP_loginfailcount INT NULL DEFAULT NULL
+ADD PP_loginfailcount INT NOT NULL DEFAULT "0"
 ;';
     pwg_query($q);
