Index: /extensions/AStat/admin/astat_by_ip.tpl
===================================================================
--- /extensions/AStat/admin/astat_by_ip.tpl	(revision 3705)
+++ /extensions/AStat/admin/astat_by_ip.tpl	(revision 3706)
@@ -10,5 +10,5 @@
     {foreach from=$datarows key=name item=data}
       <tr class="StatTableRow">
-        <td style="white-space: nowrap">{$data.ASTAT_IP_GEOLOCALISATION} {$data.ASTAT_IP_ADRESS}</td>
+        <td style="white-space: nowrap"><a href="">{$data.ASTAT_IP_BLACKLIST} {$data.ASTAT_IP_GEOLOCALISATION} {$data.ASTAT_IP_ADRESS}</td>
         <td class="number">{$data.PAGES}</td>
         <td class="number">{$data.PICTURES}</td>
Index: /extensions/AStat/admin/astat_config.tpl
===================================================================
--- /extensions/AStat/admin/astat_config.tpl	(revision 3705)
+++ /extensions/AStat/admin/astat_config.tpl	(revision 3706)
@@ -20,4 +20,166 @@
   }
 
+  function isAValidIP(ip, strict)
+  {
+    returnedip=ip;
+    re = /^(\d{1,3}){1}(\.\d{1,3}){0,3}$/i;
+    if(re.test(ip))
+    {
+      num = ip.split('.');
+      for(i=0;i<num.length;i++)
+      {
+        if(num[i]>255) return(false);
+      }
+      if(!strict)
+      {
+        returnedip+="%";
+      }
+      for(j=4-i;j>0;j--)
+      {
+        returnedip+=".%";
+      }
+      return(returnedip);
+    }
+    else
+    {
+      return(false);
+    }
+  }
+
+  function makeOptionsFromList(list, options)
+  {
+    if(list=="")
+    {
+       return(false);
+    }
+
+    items=list.split(",");
+    for(i=0;i<items.length;i++)
+    {
+      opt = document.createElement("option");
+      opt.value=items[i];
+      opt.text=items[i];
+      options.appendChild(opt);
+    }
+  }
+
+  function makeListFromOptions(options)
+  {
+    list="";
+    for(i=0;i<options.length;i++)
+    {
+      (i!=0)?list+=",":"";
+      list+=options[i].value;
+    }
+    return(list);
+  }
+
+  function addIP()
+  {
+    ip=isAValidIP($("#iaddblackip").val(), true);
+
+    if(ip==false) return(false);
+
+    list = $("#iblacklistedip").val()+",";
+    if(list.indexOf(ip+",")==-1)
+    {
+      opt = document.createElement("option");
+      opt.value=ip;
+      opt.text=ip;
+      $("#iblacklistip")[0].appendChild(opt);
+      $("#iblacklistedip").val(makeListFromOptions($("#iblacklistip")[0].options));
+    }
+    $("#iaddblackip").val("");
+    checkButtons();
+  }
+
+  function delIP()
+  {
+    opt = $("#iblacklistip")[0];
+
+    i=0;
+    while(i<opt.options.length)
+    {
+      if(opt.options[i].selected)
+      {
+        opt.removeChild(opt.options[i]);
+      }
+      else
+      {
+        i++;
+      }
+    }
+    $("#iblacklistedip").val(makeListFromOptions(opt.options));
+    checkButtons();
+  }
+
+  function checkButtons()
+  {
+    $("#idelipbutton").attr('disabled', !($("#iblacklistip")[0].selectedIndex!=-1));
+    $("#iaddipbutton").attr('disabled', (isAValidIP($('#iaddblackip').val(),true)==false) );
+  }
+
+  function init()
+  {
+    makeOptionsFromList($("#iblacklistedip").val(), $("#iblacklistip")[0]);
+
+    $("#iaddblackip").bind("keyup focus", getIPList).bind("blur", hideIPList);
+    p = $("#iaddblackip").offset()
+    $("#iplist").css({
+      left:p.left+1,
+      top:p.top+$("#iaddblackip").outerHeight()-1
+    });
+    $("#iblacklistip").bind("change", checkButtons);
+    checkButtons();
+  }
+
+  function getIPList()
+  {
+    /*if($("#iaddblackip").val()=="")
+    {
+      ip="%";
+    }
+    else
+    {
+      ip=isAValidIP($("#iaddblackip").val(),false);
+    }
+    if(ip==false) return(false);*/
+    ip=$("#iaddblackip").val()+"%";
+
+    checkButtons();
+    $("html,input").css({cursor:"progress"});
+    $.get("{/literal}{$datas.ajaxurl}{literal}", {
+        ajaxfct:"astat_listip",
+        ipfilter:ip,
+        exclude:$("#iblacklistedip").val()
+      },
+      function (data)
+      {
+        $("#iplist").html(data).css({visibility:"visible"});
+        $("#iipsellist")
+          .bind("mouseover", function (event) {
+              $("#iaddblackip").unbind("blur");
+          } )
+          .bind("mouseout", function (event) {
+              $("#iaddblackip").bind("blur", hideIPList);
+          } )
+          .bind("blur", hideIPList)
+          .bind("change", function (event) {
+            $("#iaddblackip").val($("#iipsellist").val());
+            $("#iipsellist").trigger("blur").attr("selectedIndex", -1);
+            checkButtons();
+            hideIPList();
+          } ) ;
+        $("html,input").css({cursor:"default"});
+        $("input:text").css({cursor:"text"});
+      }
+    );
+  }
+
+  function hideIPList()
+  {
+    $("#iplist").css({visibility:"hidden"});
+  }
+
 </script>
 {/literal}
@@ -112,10 +274,10 @@
       <tr>
         <td>{'AStat_NbIPPerPages'|@translate}</td>
-        <td><input type="text" name="f_AStat_NpIPPerPages" value="{$datas.f_AStat_NpIPPerPages}" maxlength=4/></td>
+        <td colspan="2"><input type="text" name="f_AStat_NpIPPerPages" value="{$datas.f_AStat_NpIPPerPages}" maxlength=4/></td>
       </tr>
 
       <tr>
         <td>{'AStat_DefaultSortIP'|@translate}</td>
-        <td>
+        <td colspan="2">
           <select name="f_AStat_DefaultSortIP">
             {html_options values=$AStat_defaultsortip_list_values output=$AStat_defaultsortip_list_labels selected=$datas.AStat_defaultsortip_selected}
@@ -123,4 +285,29 @@
         </td>
       </tr>
+
+      <tr>
+        <td>{'AStat_BlackListedIP'|@translate}</td>
+        <td>
+          <input type="text" size="15" id="iaddblackip" style="width:200px;" autocomplete="off"/>
+          <input type="hidden" id="iblacklistedip" name="f_AStat_BlackListedIP" value="{$datas.f_AStat_BlackListedIP}"/>
+          <div id="iplist"></div>
+        </td>
+        <td>
+          <input type="button" value="{'AStat_AddIP'|@translate}" id="iaddipbutton" onclick="addIP();"/>
+        </td>
+      </tr>
+      <tr>
+        <td>&nbsp;</td>
+        <td>
+         <select multiple size="4" id="iblacklistip" style="width:200px;height:150px;">
+         </select>
+        </td>
+        <td>
+         <input type="button" value="{'AStat_DelIP'|@translate}" id="idelipbutton" onclick="delIP();"/>
+        </td>
+      </tr>
+
+
+
     </table>
   </fieldset>
@@ -190,4 +377,6 @@
 </form>
 
-
-
+<script type="text/javascript">
+  init();
+</script>
+
Index: /extensions/AStat/admin/astat_tools.tpl
===================================================================
--- /extensions/AStat/admin/astat_tools.tpl	(revision 3705)
+++ /extensions/AStat/admin/astat_tools.tpl	(revision 3706)
@@ -1,25 +1,56 @@
+{known_script id="jquery.ui" src=$ROOT_URL|@cat:"template-common/lib/ui/ui.core.packed.js"}
+{known_script id="jquery.ui.datepicker" src=$ROOT_URL|@cat:"template-common/lib/ui/ui.datepicker.packed.js"}
+{assign var="datepicker_language" value="template-common/lib/ui/i18n/ui.datepicker-"|@cat:$lang_info.code|@cat:".js"}
+
+{if "PHPWG_ROOT_PATH"|@constant|@cat:$datepicker_language|@file_exists}
+{known_script id="jquery.ui.datepicker-$lang_info.code" src=$ROOT_URL|@cat:$datepicker_language}
+{/if}
+
+{html_head}
+<link rel="stylesheet" type="text/css" href="{$ROOT_URL}template-common/lib/ui/ui.datepicker.css">
+{/html_head}
+
 {literal}
 <script type="text/javascript">
- 
+
  function enabled_purge()
  {
- 	var objconfirm = document.getElementById('purge_history_confirm');
- 	var objdate = document.getElementById('purge_history_date');
- 	var objbutton = document.getElementById('purge_history_button');
- 	var objtype0 = document.getElementById('purge_history_type0');
-	
+  re = /\d{2}/\d{2}/\d{4}/i;
 
-	if(((objconfirm.checked==true)&&(objtype0.checked==true)&&(objdate.value.length==10))||
-	   ((objconfirm.checked==true)&&(objtype0.checked==false)))
-	{ objbutton.disabled=false; } else { objbutton.disabled=true; }
+  if(($('#purge_history_confirm').attr('checked')&&$('#purge_history_type0').attr('checked')&&re.test($('#purge_history_date').val()))||
+     ($('#purge_history_confirm').attr('checked')&&!('#purge_history_type0').attr('checked')))
+  {
+    $('#purge_history_button').attr('disabled', true);
+  }
+  else
+  {
+    $('#purge_history_button').attr('disabled', false);
+  }
  }
 
  function enabled_deleted_picture()
  {
- 	var objconfirm = document.getElementById('deleted_picture_confirm_resync');
- 	var objbutton = document.getElementById('deleted_picture_resync_button');
+  var objconfirm = document.getElementById('deleted_picture_confirm_resync');
+  var objbutton = document.getElementById('deleted_picture_resync_button');
 
-	if(objconfirm.checked==true)
-	{ objbutton.disabled=false; } else { objbutton.disabled=true; }
+  if($('#deleted_picture_confirm_resync').attr('checked'))
+  {
+    $('deleted_picture_resync_button').attr('disabled', false);
+  }
+  else
+  {
+    $('deleted_picture_resync_button').attr('disabled', true);
+  }
+ }
+
+ function init()
+ {
+   $("#purge_history_date").datepicker({
+     dateFormat: 'dd/mm/yy',
+     constrainInput: true,
+     defaultDate:"-12m",
+     maxDate:0,
+     minDate:new Date({/literal}"{$datas.ASTAT_MINDATE}"{literal})
+   });
  }
 
@@ -27,9 +58,9 @@
 {/literal}
 
-{$datas.ASTAT_RESULT_OK} 
+{$datas.ASTAT_RESULT_OK}
 
 <fieldset class='formtable'>
-	<legend>{'AStat_tools_general_nfo'|@translate}</legend>
-	<p>{$datas.ASTAT_GENERAL_NFO}</p>
+  <legend>{'AStat_tools_general_nfo'|@translate}</legend>
+  <p>{$datas.ASTAT_GENERAL_NFO}</p>
 </fieldset>
 
@@ -51,55 +82,55 @@
 
 <fieldset class='formtable'>
-	<legend>{'AStat_tools_deleted_picture'|@translate}</legend>
-	<p>{'AStat_tools_deleted_picture_nfo0'|@translate}</p>
-	<p>{$datas.ASTAT_DELETED_PICTURE_NFO}</p>
-	<form method="post" action="" class="general">
-		{ if isset($datas.AStat_deleted_picture_submit0) and $datas.AStat_deleted_picture_submit0 == 'yes'}
+  <legend>{'AStat_tools_deleted_picture'|@translate}</legend>
+  <p>{'AStat_tools_deleted_picture_nfo0'|@translate}</p>
+  <p>{$datas.ASTAT_DELETED_PICTURE_NFO}</p>
+  <form method="post" action="" class="general">
+    { if isset($datas.AStat_deleted_picture_submit0) and $datas.AStat_deleted_picture_submit0 == 'yes'}
       <p class='formtable'>
         <input type="submit" value="{'AStat_tools_deleted_picture_apply'|@translate}" name="apply_tool_deleted_picture"/>
       </p>
-		{/if}
-	</form>
-	<hr/>
-	<p>{'AStat_tools_deleted_picture_nfo3'|@translate}</p>
-	
-	<form method="post" action="" class="general">
-		<p>
+    {/if}
+  </form>
+  <hr/>
+  <p>{'AStat_tools_deleted_picture_nfo3'|@translate}</p>
+
+  <form method="post" action="" class="general">
+    <p>
       <label>
         <input type="radio" value="prepare" name="fAStat_tools_deleted_picture_action" id="deleted_picture0" {$datas.ASTAT_DELETED_PICTURE_PREPARE} >&nbsp;
         {'AStat_tools_deleted_picture_prepare_action'|@translate}
       </label><br>
-		  <label>
+      <label>
         <input type="radio" value="apply" name="fAStat_tools_deleted_picture_action" id="deleted_picture1" {$datas.ASTAT_DELETED_PICTURE_DO_ACTION} >&nbsp;
-      	{'AStat_tools_deleted_picture_do_action'|@translate}{$datas.ASTAT_DELETED_PICTURE_NFO_NB}
+        {'AStat_tools_deleted_picture_do_action'|@translate}{$datas.ASTAT_DELETED_PICTURE_NFO_NB}
       </label>
     </p>
-		<p class='formtable'>
-			<input type="checkbox" id="deleted_picture_confirm_resync" onclick="enabled_deleted_picture();"/>
-			<input type="submit" value="{'AStat_tools_deleted_picture_do'|@translate}" name="apply_tool_deleted_picture_resync" id="deleted_picture_resync_button" disabled />
-		</p>
-	</form>
+    <p class='formtable'>
+      <input type="checkbox" id="deleted_picture_confirm_resync" onclick="enabled_deleted_picture();"/>
+      <input type="submit" value="{'AStat_tools_deleted_picture_do'|@translate}" name="apply_tool_deleted_picture_resync" id="deleted_picture_resync_button" disabled />
+    </p>
+  </form>
 </fieldset>
 
 
 <fieldset class='formtable'>
-	<legend>{'AStat_tools_deleted_category'|@translate}</legend>
-	<p>{'AStat_tools_deleted_category_nfo0'|@translate}</p>
-	<p>{$datas.ASTAT_DELETED_CATEGORY_NFO}</p>
-	<form method="post" action="" class="general">
-		{if isset($datas.AStat_deleted_category_submit0) and $datas.AStat_deleted_category_submit0=='yes'}
-		<p class='formtable'>
-			<input type="submit" value="{'AStat_tools_deleted_category_apply'|@translate}" name="apply_tool_deleted_category"/>
-		</p>
-		{/if}
-	</form>
+  <legend>{'AStat_tools_deleted_category'|@translate}</legend>
+  <p>{'AStat_tools_deleted_category_nfo0'|@translate}</p>
+  <p>{$datas.ASTAT_DELETED_CATEGORY_NFO}</p>
+  <form method="post" action="" class="general">
+    {if isset($datas.AStat_deleted_category_submit0) and $datas.AStat_deleted_category_submit0=='yes'}
+    <p class='formtable'>
+      <input type="submit" value="{'AStat_tools_deleted_category_apply'|@translate}" name="apply_tool_deleted_category"/>
+    </p>
+    {/if}
+  </form>
 </fieldset>
 
 
 <fieldset class='formtable'>
-	<legend>{'AStat_tools_purge_history'|@translate}</legend>
-	<p>{'AStat_tools_purge_history_nfo'|@translate}</p>
-	<form method="post" action="" class="general">
-		<p>
+  <legend>{'AStat_tools_purge_history'|@translate}</legend>
+  <p>{'AStat_tools_purge_history_nfo'|@translate}</p>
+  <form method="post" action="" class="general">
+    <p>
       <label>
         <input type="radio" value="bydate" name="fAStat_purge_history_type" id="purge_history_type0" checked onclick="enabled_purge();">&nbsp;
@@ -107,5 +138,5 @@
       </label>
 
-      <input type="text" id="purge_history_date" name="fAStat_purge_history_date" value="" maxlength=10 onchange="enabled_purge();"/><br>
+      <input type="text" id="purge_history_date" name="fAStat_purge_history_date" size="10" onchange="enabled_purge();"/><br>
 
       <label>
@@ -113,9 +144,15 @@
         {$datas.ASTAT_PURGE_HISTORY_IMAGE_NFO}
       </label>
-      <br>
+      <br/>
 
       <label>
         <input type="radio" value="bycategoryid0" name="fAStat_purge_history_type" id="purge_history_type2" onclick="enabled_purge();" {$datas.ASTAT_PURGE_HISTORY_CATEGORY_DISABLED}>&nbsp;
         {$datas.ASTAT_PURGE_HISTORY_CATEGORY_NFO}
+      </label>
+      <br/>
+
+      <label>
+        <input type="radio" value="byipid0" name="fAStat_purge_history_type" id="purge_history_type3" onclick="enabled_purge();" {$datas.ASTAT_PURGE_HISTORY_IP_DISABLED}>&nbsp;
+        {$datas.ASTAT_PURGE_HISTORY_IP_NFO}
       </label>
     </p>
@@ -126,5 +163,8 @@
     </p>
 
-	</form>
+  </form>
 </fieldset>
 
+<script type="text/javascript">
+  init();
+</script>
Index: /extensions/AStat/astat_aim.class.inc.php
===================================================================
--- /extensions/AStat/astat_aim.class.inc.php	(revision 3705)
+++ /extensions/AStat/astat_aim.class.inc.php	(revision 3706)
@@ -20,5 +20,5 @@
 
 class AStat_AIM extends common_plugin
-{ 
+{
   protected $css = null;
 
@@ -61,5 +61,6 @@
       'AStat_BarColor_Cat' => 'fff966',
       'AStat_DefaultSortIP' => 'page',    //page, ip, picture
-      'AStat_SeeTimeRequests' => 'false'
+      'AStat_SeeTimeRequests' => 'false',
+      'AStat_BlackListedIP' => ''    // ip blacklisted (separator : ",")
       );
 
@@ -96,6 +97,6 @@
   function generate_CSS()
   {
-    $text = ".AStatBar1, .AStatBar2, .AStatBar3, .AStatBar4, .AStatBarX { 
-      border:0px; 
+    $text = ".AStatBar1, .AStatBar2, .AStatBar3, .AStatBar4, .AStatBarX {
+      border:0px;
       height:8px;
       display: block;
@@ -105,6 +106,6 @@
       position:relative;
       }
-       .MiniSquare1, .MiniSquare2, .MiniSquare3, .MiniSquare4 { 
-      border:0px; 
+       .MiniSquare1, .MiniSquare2, .MiniSquare3, .MiniSquare4 {
+      border:0px;
       height:8px;
       width:8px;
@@ -119,8 +120,9 @@
        .MiniSquare1 { color:#".$this->my_config['AStat_BarColor_Pages'].";   }
        .MiniSquare2 { color:#".$this->my_config['AStat_BarColor_Img'].";  }
-       .MiniSquare3 { color:#".$this->my_config['AStat_BarColor_IP']."; } 
-       .MiniSquare4 { color:#".$this->my_config['AStat_BarColor_Cat']."; } 
+       .MiniSquare3 { color:#".$this->my_config['AStat_BarColor_IP']."; }
+       .MiniSquare4 { color:#".$this->my_config['AStat_BarColor_Cat']."; }
        .StatTableRow:hover { background-color:#".$this->my_config['AStat_MouseOverColor']."; }
-       .formtable, .formtable P { text-align:left; display:block; } 
+       .formtable, .formtable P { text-align:left; display:block; }
+       .formtable tr { vertical-align:top; }
        .window_thumb {
       position:absolute;
@@ -133,10 +135,10 @@
       z-index:100;
       overflow:hidden;
-      visibility:hidden; }  
+      visibility:hidden; }
         .img_thumb {
       border: solid 3px #ffffff;
       background: #000000;
       margin:0px;
-      padding:0px; }  
+      padding:0px; }
         .time_request {
       font-size:83%;
@@ -146,4 +148,7 @@
       table.littlefont th { padding:3px; }
       table.littlefont td { padding:0px;padding-left:3px;padding-right:3px; }
+      #iplist { visibility:hidden; position:absolute; width:200px; z-index:1000; }
+      .iipsellistitem { float:right; }
+      #iipsellist { width:100%; font-family:monospace; }
     ";
 
Index: /extensions/AStat/astat_aip.class.inc.php
===================================================================
--- /extensions/AStat/astat_aip.class.inc.php	(revision 3705)
+++ /extensions/AStat/astat_aip.class.inc.php	(revision 3706)
@@ -17,12 +17,14 @@
 include_once('astat_aim.class.inc.php');
 include_once(PHPWG_ROOT_PATH.'admin/include/tabsheet.class.php');
+include_once(PHPWG_PLUGINS_PATH.'grum_plugins_classes-2/ajax.class.inc.php');
 
 class AStat_AIP extends AStat_AIM
-{ 
-  protected $tabsheet; 
+{
+  protected $tabsheet;
   protected $list_periods = array('global', 'all', 'year', 'month', 'day');
   protected $list_sortcat = array('page', 'picture', 'nbpicture');
   protected $list_sortimg = array('picture', 'catname');
   protected $list_sortip = array('page', 'picture', 'ip');
+  protected $ajax;
 
   protected $catfilter;    //filter on categories
@@ -56,4 +58,6 @@
                           l10n('AStat_tools'),
                           $this->page_link.'&amp;fAStat_tabsheet=tools');
+
+    $this->ajax = new Ajax();
   }
 
@@ -78,4 +82,6 @@
   {
     global $template;
+
+    $this->return_ajax_content();
 
     $template->set_filename('plugin_admin_content', dirname(__FILE__)."/admin/astat_admin.tpl");
@@ -102,4 +108,9 @@
     elseif($_REQUEST['fAStat_tabsheet']=='stats_by_ip')
     {
+      if(isset($_REQUEST['fAStat_IP_BL']))
+      {
+        $this->add_ip_to_filter($_REQUEST['fAStat_IP_BL']);
+      }
+
       $this->display_stats_by_ip(
           $_REQUEST['fAStat_year'],
@@ -167,4 +178,34 @@
   --------------------------------------------------------------------------- */
 
+  /*
+    return ajax content
+  */
+  protected function return_ajax_content()
+  {
+    global $ajax, $template;
+
+    if(isset($_REQUEST['ajaxfct']))
+    {
+      //$this->debug("AJAXFCT:".$_REQUEST['ajaxfct']);
+      $result="<p class='errors'>An error has occured</p>";
+      switch($_REQUEST['ajaxfct'])
+      {
+        case 'astat_listip':
+          if(!isset($_REQUEST['ipfilter'])) $_REQUEST['ipfilter']="";
+          if(!isset($_REQUEST['exclude'])) $_REQUEST['exclude']="";
+          $result=$this->ajax_listip($_REQUEST['ipfilter'], $_REQUEST['exclude']);
+          break;
+      }
+      //$template->
+      $this->ajax->return_result($result);
+    }
+  }
+
+
+
+
+
+
+
   private function init_request()
   {
@@ -292,5 +333,5 @@
 
     if($day!="")
-    { 
+    {
       $sql_groupdef="HOUR(time) as GId,";
       $sql_select="select HOUR(time) as GId, ";
@@ -299,5 +340,5 @@
       $sql_order=" order by GId asc";
 
-      for($i=0;$i<24;$i++) 
+      for($i=0;$i<24;$i++)
       {
         $returned[$i] = array(
@@ -313,6 +354,6 @@
       }
     }
-    elseif($month!="") 
-    { 
+    elseif($month!="")
+    {
       $sql_groupdef="DAY(date) as GId,";
       $sql_select="select DAY(date) as GId, ";
@@ -323,5 +364,5 @@
       $delta = 1;
       $NbDays = strftime('%d', mktime(0,0,0,$month+1,0,$year));
-      for($i=0;$i<$NbDays;$i++) 
+      for($i=0;$i<$NbDays;$i++)
       {
         $returned[$i] = array(
@@ -337,6 +378,6 @@
       }
     }
-    elseif($year!="") 
-    { 
+    elseif($year!="")
+    {
       $sql_groupdef="MONTH(date) as GId,";
       $sql_select="select MONTH(date) as GId, ";
@@ -346,5 +387,5 @@
 
       $delta = 1;
-      for($i=0;$i<12;$i++) 
+      for($i=0;$i<12;$i++)
       {
         $returned[$i] = array(
@@ -360,5 +401,5 @@
       }
     }
-    elseif($total!="Y") 
+    elseif($total!="Y")
     {
       $sql_groupdef="YEAR(date) as GId,";
@@ -379,7 +420,13 @@
     }
 
+    if($this->my_config['AStat_BlackListedIP']!="")
+    {
+      ($sql_where=="")?$sql_where=" where ":$sql_where.=" AND ";
+      $sql_where .= " NOT ".$this->make_IP_where_clause($this->my_config['AStat_BlackListedIP']);
+    }
+
     $sql_max=", (select max(n.MaxPages) as MaxPages, max(n.MaxIP) as MaxIP, max(n.MaxImg) as MaxImg
         from (select ".$sql_groupdef." count(id) as MaxPages, count(distinct IP) as MaxIP, count(image_id) as MaxImg
-            from ".HISTORY_TABLE.$sql_where.$sql_group.") as n) as n "; 
+            from ".HISTORY_TABLE.$sql_where.$sql_group.") as n) as n ";
     $sql=$sql_select.$sql.$sql_nfomax.$sql_from.$sql_max.$sql_where.$sql_group.$sql_order;
 
@@ -394,5 +441,5 @@
       { $returned[$row["GId"]-$delta] = $row; }
     }
-    
+
     return($returned);
   } //stat by period
@@ -423,18 +470,18 @@
     $sql_where = "";
     $sql_group=" group by IP_USER ";
-    $sql_order=" order by ".$sortlist[$sortip]." "; 
+    $sql_order=" order by ".$sortlist[$sortip]." ";
     $sql_limit=" limit ".(($pagenumber-1)* $nbipperpage).", ".$nbipperpage;
 
 
-    if($day!="") 
-    { 
+    if($day!="")
+    {
       $sql_where=" where YEAR(date) = $year and MONTH(date) = $month and DAY(date) = $day ";
     }
-    elseif($month!="") 
-    { 
+    elseif($month!="")
+    {
       $sql_where=" where YEAR(date) = $year and MONTH(date) = $month ";
     }
-    elseif($year!="") 
-    { 
+    elseif($year!="")
+    {
       $sql_where=" where YEAR(date) = $year ";
     }
@@ -451,7 +498,13 @@
     }
 
+    if($this->my_config['AStat_BlackListedIP']!="")
+    {
+      ($sql_where=="")?$sql_where=" where ":$sql_where.=" AND ";
+      $sql_where .= " NOT ".$this->make_IP_where_clause($this->my_config['AStat_BlackListedIP']);
+    }
+
     $sql_max=", (select max(n.MaxPages) as MaxPages, max(n.MaxImg) as MaxImg
-        from (select if(".HISTORY_TABLE.".user_id = 2, IP, if(".USERS_TABLE.".username is null, '[".l10n("AStat_deleted_user")."]', ".USERS_TABLE.".username)) as IP_USER, count(".HISTORY_TABLE.".id) as MaxPages, count(image_id) as MaxImg 
-            from ".HISTORY_TABLE." LEFT JOIN ".USERS_TABLE." ON ".HISTORY_TABLE.".user_id = ".USERS_TABLE.".id ".$sql_where.$sql_group.") as n) as n "; 
+        from (select if(".HISTORY_TABLE.".user_id = 2, IP, if(".USERS_TABLE.".username is null, '[".l10n("AStat_deleted_user")."]', ".USERS_TABLE.".username)) as IP_USER, count(".HISTORY_TABLE.".id) as MaxPages, count(image_id) as MaxImg
+            from ".HISTORY_TABLE." LEFT JOIN ".USERS_TABLE." ON ".HISTORY_TABLE.".user_id = ".USERS_TABLE.".id ".$sql_where.$sql_group.") as n) as n ";
     $sql=$sql_select.$sql.$sql_nfomax.$sql_from.$sql_max.$sql_where.$sql_group.$sql_order.$sql_limit;
 
@@ -475,5 +528,5 @@
       $returned[1] = -1;
     }
-    
+
     return($returned);
   } //stat by ip
@@ -483,5 +536,5 @@
       %Pages
       %Images
-      by : 
+      by :
         Categories
         Categories/years
@@ -505,5 +558,5 @@
 
     if($show_thumb=='true')
-    { 
+    {
       $sql_thumb = ', '.IMAGES_TABLE.'.path as ThumbPath, '.IMAGES_TABLE.'.file as ThumbFile, '.IMAGES_TABLE.'.tn_ext as Extension';
       $sql_fromthumb = "LEFT JOIN ".IMAGES_TABLE." ON ic2.representative_picture_id = ".IMAGES_TABLE.".id  ";
@@ -514,7 +567,7 @@
       $sql_fromthumb = "";
     }
-    
+
     $sql_from = " from (".HISTORY_TABLE." LEFT JOIN ".CATEGORIES_TABLE." ON ".CATEGORIES_TABLE.".id = ".HISTORY_TABLE.".category_id),
-(select category_id as catid, count(image_id) as nb_images, representative_picture_id 
+(select category_id as catid, count(image_id) as nb_images, representative_picture_id
  from ".IMAGE_CATEGORY_TABLE.", ".CATEGORIES_TABLE."
  where ".CATEGORIES_TABLE.".id = ".IMAGE_CATEGORY_TABLE.".category_id group by category_id) as ic2 ";
@@ -522,17 +575,17 @@
     $sql_group=" group by category_id, section ";
     $sql_group2="";
-    $sql_order=" order by ".$sortlist[$sortcat]; 
+    $sql_order=" order by ".$sortlist[$sortcat];
     $sql_limit=" limit ".(($pagenumber-1)* $nbipperpage).", ".$nbipperpage;
 
-    if($day!="") 
-    { 
+    if($day!="")
+    {
       $sql_where=" where YEAR(date) = $year and MONTH(date) = $month and DAY(date)= $day ";
     }
-    elseif($month!="") 
-    { 
+    elseif($month!="")
+    {
       $sql_where=" where YEAR(date) = $year and MONTH(date) = $month ";
     }
-    elseif($year!="") 
-    { 
+    elseif($year!="")
+    {
       $sql_where=" where YEAR(date) = $year ";
     }
@@ -552,4 +605,8 @@
     $sql_where .= "  ic2.catid = ".HISTORY_TABLE.".category_id ";
 
+    if($this->my_config['AStat_BlackListedIP']!="")
+    {
+      $sql_where .= " AND NOT ".$this->make_IP_where_clause($this->my_config['AStat_BlackListedIP']);
+    }
 
     $sql=$sql_select.$sql.$sql_thumb.$sql_from.$sql_fromthumb.$sql_max.$sql_where.$sql_group.$sql_order.$sql_limit;
@@ -574,5 +631,5 @@
       $returned[1] = -1;
     }
-    
+
     return($returned);
   } // stats per categories
@@ -582,5 +639,5 @@
       Num of view per image
       %view on period
-      by : 
+      by :
         Images
         Images/years
@@ -605,5 +662,5 @@
         ".IMAGES_TABLE.".path as ThumbPath, ".IMAGES_TABLE.".file as ThumbFile,
         MaxImg2.somme as NbVuesMax, ".IMAGES_TABLE.".tn_ext as Extension ";
-    
+
     $sql_from = " from ((".HISTORY_TABLE." LEFT JOIN ".IMAGES_TABLE." ON
   ".IMAGES_TABLE.".id = ".HISTORY_TABLE.".image_id) LEFT JOIN ".CATEGORIES_TABLE."
@@ -613,17 +670,17 @@
     $sql_where = " where ".HISTORY_TABLE.".image_id is not null ";
     $sql_group=" group by image_id, category_id ";
-    $sql_order=" order by ".$sortlist[$sortimg]; 
+    $sql_order=" order by ".$sortlist[$sortimg];
     $sql_limit=" limit ".(($pagenumber-1)* $nbipperpage).", ".$nbipperpage;
 
-    if($day!="") 
-    { 
+    if($day!="")
+    {
       $sql_where.=" and YEAR(date) = $year and MONTH(date) = $month and DAY(date)= $day ";
     }
-    elseif($month!="") 
-    { 
+    elseif($month!="")
+    {
       $sql_where.=" and YEAR(date) = $year and MONTH(date) = $month ";
     }
-    elseif($year!="") 
-    { 
+    elseif($year!="")
+    {
       $sql_where.=" and YEAR(date) = $year ";
     }
@@ -631,9 +688,9 @@
 
     if($ip!="")
-    { 
+    {
       $sql_where.=" and ( ((IP = '$ip') and ( user_id = 2 ))  or (".USERS_TABLE.".username = '$ip') )";
-      $sql_from_ip=" LEFT JOIN ".USERS_TABLE." ON ".USERS_TABLE.".id = ".HISTORY_TABLE.".user_id ";   
-    }
-    
+      $sql_from_ip=" LEFT JOIN ".USERS_TABLE." ON ".USERS_TABLE.".id = ".HISTORY_TABLE.".user_id ";
+    }
+
     if($this->catfilter!="")
     {
@@ -643,4 +700,11 @@
     }
 
+    if($this->my_config['AStat_BlackListedIP']!="")
+    {
+      ($sql_where=="")?$sql_where=" where ":$sql_where.=" AND ";
+      $sql_where .= " NOT ".$this->make_IP_where_clause($this->my_config['AStat_BlackListedIP']);
+    }
+
+
     $sql_max=", (select count(image_id) as somme from ".HISTORY_TABLE.$sql_from_ip.$sql_where.") as MaxImg, (select count(image_id) as somme from ".HISTORY_TABLE.$sql_from_ip.$sql_where." and ".HISTORY_TABLE.".image_id is not null group by image_id order by somme desc limit 0,1) as MaxImg2 ";
 
@@ -666,5 +730,5 @@
       $returned[1] = -1;
     }
-    
+
     return($returned);
   } //stat by images
@@ -719,29 +783,29 @@
 
     /* period label + navigation links */
-    if($day!="") 
-    { 
+    if($day!="")
+    {
       $template_datas["PERIOD_LABEL"] = l10n("AStat_period_label_hours");
       $template_datas["L_STAT_TITLE"] = $this->format_link(l10n("AStat_period_label_global"), $a_links["global"])." / ".$this->format_link(l10n("AStat_period_label_all"), $a_links["all"])." / ".$this->format_link($year, $a_links["year"])." / ".$this->format_link(l10n("AStat_month_of_year_".$month), $a_links["month"])." / ".l10n("AStat_day_of_week_".date("w",mktime(0, 0, 0, $month, $day, $year)))." $day";
     }
-    elseif($month!="") 
-    { 
+    elseif($month!="")
+    {
       $template_datas["PERIOD_LABEL"] = l10n("AStat_period_label_days");
       $template_datas["L_STAT_TITLE"] = $this->format_link(l10n("AStat_period_label_global"), $a_links["global"])." / ".$this->format_link(l10n("AStat_period_label_all"), $a_links["all"])." / ".$this->format_link($year, $a_links["year"])." / ".l10n("AStat_month_of_year_".$month);
     }
-    elseif($year!="") 
-    { 
+    elseif($year!="")
+    {
       $template_datas["PERIOD_LABEL"] = l10n("AStat_period_label_months");
       $template_datas["L_STAT_TITLE"] = $this->format_link(l10n("AStat_period_label_global"), $a_links["global"])." / ".$this->format_link(l10n("AStat_period_label_all"), $a_links["all"])." / ".$year;
     }
-    elseif($total!="Y") 
-    { 
+    elseif($total!="Y")
+    {
       $template_datas["PERIOD_LABEL"] = l10n("AStat_period_label_years");
       $template_datas["L_STAT_TITLE"] = $this->format_link(l10n("AStat_period_label_global"), $a_links["global"])." / ".l10n("AStat_period_label_all");
     }
-    else 
-    { 
+    else
+    {
       $template_datas["PERIOD_LABEL"] = l10n("AStat_period_label_all");
       $template_datas["L_STAT_TITLE"] = l10n("AStat_period_label_global");
-    } 
+    }
     $template_datas["MAX_WIDTH"] = $max_width+10;
     $template_datas["ASTAT_NFO_STAT"] = l10n("AStat_Nfo_Period");
@@ -763,6 +827,6 @@
       }
       else
-      { 
-        $width_pages = 0; 
+      {
+        $width_pages = 0;
         $width_img = 0;
         $width_ip = 0;
@@ -770,5 +834,5 @@
       }
 
-    
+
       if($day!="")
       { // si jours sÃ©lectionnÃ©, heures affichÃ©es
@@ -787,5 +851,5 @@
         $value_img=$img_links["day"].$stats[$i]["GId"];
       }
-      elseif($year!="") 
+      elseif($year!="")
       { // si annÃ©e sÃ©lectionnÃ©e, mois affichÃ©s
         $value = l10n("AStat_month_of_year_".$stats[$i]["GId"]);
@@ -803,7 +867,7 @@
         $value_img=$img_links["year"].$stats[$i]["GId"];
       }
-      else 
-      { 
-        $value=l10n("AStat_period_label_all"); 
+      else
+      {
+        $value=l10n("AStat_period_label_all");
         $link=$this->page_link."&amp;fAStat_defper=N&amp;fAStat_all=N";
         $value_ip=$ip_links["all"];
@@ -897,29 +961,29 @@
 
     /* periode label + navigation links */
-    if($day!="") 
-    { 
+    if($day!="")
+    {
       $dir_links = $this->format_link(l10n("AStat_period_label_global"), $a_links["global"])." / ".$this->format_link(l10n("AStat_period_label_all"), $a_links["all"])." / ".$this->format_link($year, $a_links["year"])." / ".$this->format_link(l10n("AStat_month_of_year_".$month), $a_links["month"])." / ".l10n("AStat_day_of_week_".date("w",mktime(0, 0, 0, $month, $day, $year)))." $day";
       $page_link=$ip_links["day"];
       $img_link=$img_links["day"];
     }
-    elseif($month!="") 
-    { 
+    elseif($month!="")
+    {
       $dir_links = $this->format_link(l10n("AStat_period_label_global"), $a_links["global"])." / ".$this->format_link(l10n("AStat_period_label_all"), $a_links["all"])." / ".$this->format_link($year, $a_links["year"])." / ".l10n("AStat_month_of_year_".$month);
       $page_link=$ip_links["month"];
       $img_link=$img_links["month"];
     }
-    elseif($year!="") 
-    { 
+    elseif($year!="")
+    {
       $dir_links = $this->format_link(l10n("AStat_period_label_global"), $a_links["global"])." / ".$this->format_link(l10n("AStat_period_label_all"), $a_links["all"])." / ".$year;
       $page_link=$ip_links["year"];
       $img_link=$img_links["year"];
     }
-    else 
-    { 
+    else
+    {
       $dir_links = $this->format_link(l10n("AStat_period_label_global"), $a_links["global"])." / ".l10n("AStat_period_label_all");
       $page_link=$ip_links["all"];
       $img_link=$img_links["all"];
     }
-    
+
 
     if($nbpages>1) { $plural="s"; } else { $plural=""; }
@@ -927,6 +991,6 @@
     for($i=1;$i<=$nbpages;$i++)
     {
-      if($i==$pagenumber) 
-      { $pages_links.=" $i "; } 
+      if($i==$pagenumber)
+      { $pages_links.=" $i "; }
       else
       {
@@ -934,5 +998,5 @@
       }
     }
-    
+
     $template_datas["L_STAT_TITLE"] = $dir_links;
     $template_datas["MAX_WIDTH"]=$max_width+10;
@@ -955,11 +1019,11 @@
     {
         if($stats[$i]["MaxPages"] > 0)
-      { 
-        $width_pages = ceil(($stats[$i]["NbPages"] * $max_width) / $stats[$i]["MaxPages"] ); 
+      {
+        $width_pages = ceil(($stats[$i]["NbPages"] * $max_width) / $stats[$i]["MaxPages"] );
         $width_img = ceil(($stats[$i]["NbImg"] * $max_width) / $stats[$i]["MaxPages"] );
       }
       else
-      { 
-        $width_pages = 0; 
+      {
+        $width_pages = 0;
         $width_img = 0;
       }
@@ -970,13 +1034,29 @@
         $ip_geolocalisation='<a href="http://www.geoiptool.com/fr/?IP='.$stats[$i]["IP_USER"].'" title="Geo IP localisation" target="_blank">['.l10n('AStat_IP_geolocalisation').'] </a>';
         $ip_adress='<a href="http://www.ripe.net/whois?form_type=simple&amp;full_query_string=&amp;searchtext='.$stats[$i]["IP_USER"].'+&amp;do_search=Search" title="Ripe Whois" target="_blank">'.$stats[$i]["IP_USER"].'</a>';
-      }
-      else 
+        $ip_blacklist=$page_link."&amp;fAStat_IP_BL=".$stats[$i]["IP_USER"];
+
+        if($pagenumber>1)
+        {
+          $ip_blacklist.="&amp;fAStat_page_number=$pagenumber";
+        }
+
+        if($sortip!="page")
+        {
+          $ip_blacklist.="&amp;fAStat_SortIP=$sortip";
+        }
+
+        $ip_blacklist=$this->format_link("[".l10n('AStat_IP_blacklist')."]", $ip_blacklist);
+      }
+      else
       {
         $ip_geolocalisation='';
         $ip_adress=$stats[$i]["IP_USER"];
-      }
+        $ip_blacklist='';
+      }
+
 
 
       $template_datarows[]=array(
+        'ASTAT_IP_BLACKLIST' => $ip_blacklist,
         'ASTAT_IP_GEOLOCALISATION' => $ip_geolocalisation,
         'ASTAT_IP_ADRESS' => $ip_adress,
@@ -1030,25 +1110,25 @@
 
     /* make navigation links */
-    if($day!="") 
-    { 
+    if($day!="")
+    {
       $dir_links = $this->format_link(l10n("AStat_period_label_global"), $a_links["global"])." / ".$this->format_link(l10n("AStat_period_label_all"), $a_links["all"])." / ".$this->format_link($year, $a_links["year"])." / ".$this->format_link(l10n("AStat_month_of_year_".$month), $a_links["month"])." / ".l10n("AStat_day_of_week_".date("w",mktime(0, 0, 0, $month, $day, $year)))." $day";
       $page_link=$cat_links["day"];
     }
-    elseif($month!="") 
-    { 
+    elseif($month!="")
+    {
       $dir_links = $this->format_link(l10n("AStat_period_label_global"), $a_links["global"])." / ".$this->format_link(l10n("AStat_period_label_all"), $a_links["all"])." / ".$this->format_link($year, $a_links["year"])." / ".l10n("AStat_month_of_year_".$month);
       $page_link=$cat_links["month"];
     }
-    elseif($year!="") 
-    { 
+    elseif($year!="")
+    {
       $dir_links = $this->format_link(l10n("AStat_period_label_global"), $a_links["global"])." / ".$this->format_link(l10n("AStat_period_label_all"), $a_links["all"])." / ".$year;
       $page_link=$cat_links["year"];
     }
-    else 
-    { 
+    else
+    {
       $dir_links = $this->format_link(l10n("AStat_period_label_global"), $a_links["global"])." / ".l10n("AStat_period_label_all");
       $page_link=$cat_links["all"];
     }
-    
+
 
     if($nbpages>1)
@@ -1064,5 +1144,5 @@
     for($i=1;$i<=$nbpages;$i++)
     {
-      if($i==$pagenumber) 
+      if($i==$pagenumber)
       {
         $pages_links.=" $i ";
@@ -1096,7 +1176,7 @@
       $width_pages = ceil(($stats[$i]["PctPages"] * $max_width)/100);
       $width_img = ceil(($stats[$i]["PctImg"] * $max_width)/100 );
-      
+
       if($showthumb=='true')
-      { 
+      {
         $filethumb=$this->change_file_ext($stats[$i]["ThumbFile"], $stats[$i]["Extension"]);
         $filethumb=str_replace($stats[$i]["ThumbFile"],"thumbnail/".$conf['prefix_thumbnail'].$filethumb,$stats[$i]["ThumbPath"]); }
@@ -1109,5 +1189,5 @@
       { $category = $this->format_link($stats[$i]["IdCat"], PHPWG_ROOT_PATH."index.php?/category/".$stats[$i]["category_id"]); }
       else
-      { 
+      {
         $category = "<i>".l10n('AStat_section_label').' : ';
         if(l10n('AStat_section_'.$stats[$i]["IdCat"])!='AStat_section_'.$stats[$i]["IdCat"])
@@ -1142,5 +1222,5 @@
 
   /* ------------------------------------------------------------------------------------------
-      display stats for images 
+      display stats for images
       ------------------------------------------------------------------------------------------ */
   function display_stats_by_image($year, $month, $day, $max_width, $nbipperpage, $pagenumber,$showthumb, $sortimg, $ip, $seetimerequest)
@@ -1176,21 +1256,21 @@
 
     /* navigation links */
-    if($day!="") 
-    { 
+    if($day!="")
+    {
       $dir_links = $this->format_link(l10n("AStat_period_label_global"), $a_links["global"])." / ".$this->format_link(l10n("AStat_period_label_all"), $a_links["all"])." / ".$this->format_link($year, $a_links["year"])." / ".$this->format_link(l10n("AStat_month_of_year_".$month), $a_links["month"])." / ".l10n("AStat_day_of_week_".date("w",mktime(0, 0, 0, $month, $day, $year)))." $day";
       $page_link=$img_links["day"];
     }
-    elseif($month!="") 
-    { 
+    elseif($month!="")
+    {
       $dir_links = $this->format_link(l10n("AStat_period_label_global"), $a_links["global"])." / ".$this->format_link(l10n("AStat_period_label_all"), $a_links["all"])." / ".$this->format_link($year, $a_links["year"])." / ".l10n("AStat_month_of_year_".$month);
       $page_link=$img_links["month"];
     }
-    elseif($year!="") 
-    { 
+    elseif($year!="")
+    {
       $dir_links = $this->format_link(l10n("AStat_period_label_global"), $a_links["global"])." / ".$this->format_link(l10n("AStat_period_label_all"), $a_links["all"])." / ".$year;
       $page_link=$img_links["year"];
     }
-    else 
-    { 
+    else
+    {
       $dir_links = $this->format_link(l10n("AStat_period_label_global"), $a_links["global"])." / ".l10n("AStat_period_label_all");
       $page_link=$img_links["all"];
@@ -1201,5 +1281,5 @@
       $dir_links.= " [IP : $ip]";
     }
-    
+
 
     if($nbpages>1) { $plural="s"; } else { $plural=""; }
@@ -1207,6 +1287,6 @@
     for($i=1;$i<=$nbpages;$i++)
     {
-      if($i==$pagenumber) 
-      { $pages_links.=" $i "; } 
+      if($i==$pagenumber)
+      { $pages_links.=" $i "; }
       else
       {
@@ -1236,11 +1316,11 @@
     for($i=0;$i<count($stats);$i++)
     {
-      $width_pages = ceil(($stats[$i]["NbVues"] * $max_width)/$stats[$i]["NbVuesMax"] ); 
+      $width_pages = ceil(($stats[$i]["NbVues"] * $max_width)/$stats[$i]["NbVuesMax"] );
       $width_img = ceil(($stats[$i]["PctImg"] * $max_width)/100 );
-      
+
       if($showthumb=='true')
       {
         $filethumb=$this->change_file_ext($stats[$i]["ThumbFile"], $stats[$i]["Extension"]);
-        $filethumb=str_replace($stats[$i]["ThumbFile"],"thumbnail/".$conf['prefix_thumbnail'].$filethumb,$stats[$i]["ThumbPath"]); 
+        $filethumb=str_replace($stats[$i]["ThumbFile"],"thumbnail/".$conf['prefix_thumbnail'].$filethumb,$stats[$i]["ThumbPath"]);
       }
       else
@@ -1258,5 +1338,5 @@
         }
         else
-        { 
+        {
           if($stats[$i]["ThumbFile"]!="")
           {
@@ -1270,5 +1350,5 @@
       }
       else
-      { 
+      {
         $image_links = "<i>".l10n('AStat_section_label').' : ';
         if(l10n('AStat_section_'.$stats[$i]["CatName"])!='AStat_section_'.$stats[$i]["CatName"])
@@ -1281,5 +1361,5 @@
         }
 
-        $image_links.="</i> / "; 
+        $image_links.="</i> / ";
 
         if($stats[$i]["ImgName"]!="")
@@ -1288,5 +1368,5 @@
         }
         else
-        { 
+        {
           if($stats[$i]["ThumbFile"]!="")
           {
@@ -1320,4 +1400,14 @@
 
 
+  private function add_ip_to_filter($ip)
+  {
+    if(strpos($this->my_config['AStat_BlackListedIP'].",", $ip.",")===false)
+    {
+      ($this->my_config['AStat_BlackListedIP']!='')?$this->my_config['AStat_BlackListedIP'].=",":"";
+      $this->my_config['AStat_BlackListedIP'].=$ip;
+      $this->save_config();
+    }
+  }
+
 
   /*
@@ -1330,10 +1420,10 @@
     $save_status=false;
 
-    if(isset($_POST['submit'])) 
+    if(isset($_POST['submit']))
     {
       if(!is_adviser())
       {
-        reset($this->my_config); 
-        while (list($key, $val) = each($this->my_config)) 
+        reset($this->my_config);
+        while (list($key, $val) = each($this->my_config))
         {
           if(isset($_POST['f_'.$key]))
@@ -1364,9 +1454,12 @@
 
     //standards inputs zones
-    reset($this->my_config); 
-    while (list($key, $val) = each($this->my_config)) 
+    reset($this->my_config);
+    while (list($key, $val) = each($this->my_config))
     {
       $template_datas["f_".$key]=$val;
     }
+
+    //
+    $template_datas['ajaxurl']=$this->page_link;
 
     // define selected item for lists zones
@@ -1395,6 +1488,6 @@
     }
     // default ip order
-    reset($this->list_sortip); 
-    while (list($key, $val) = each($this->list_sortip)) 
+    reset($this->list_sortip);
+    while (list($key, $val) = each($this->list_sortip))
     {
       $template_list_values['sortip'][]=$val;
@@ -1434,5 +1527,5 @@
 
 
-  /* 
+  /*
     display tools page
   */
@@ -1448,12 +1541,12 @@
 
     // >> PURGE HISTORY --------------------------------------------------------
-    if(isset($_POST['apply_tool_purge_history'])) 
+    if(isset($_POST['apply_tool_purge_history']))
     {
       if(!is_adviser())
       {
-        $action_result['action']='AStat_tools_purge_history';     
-        $action_result['result']='false';     
+        $action_result['action']='AStat_tools_purge_history';
+        $action_result['result']='false';
         $action_result['msg']='AStat_tools_result_ko';
-        
+
         $ok_to_purge=true;
         if($_REQUEST['fAStat_purge_history_type']=='bydate')
@@ -1466,25 +1559,29 @@
 
           $purge_date=mktime(0,0,0,$date[1],$date[0],$date[2]);
-          $fdate=date("Y-m-d", mktime(0,0,0,$date[1],$date[0],$date[2]));
+          $fparam=date("Y-m-d", mktime(0,0,0,$date[1],$date[0],$date[2]));
 
           if(date("d/m/Y", $purge_date)!=$_REQUEST['fAStat_purge_history_date'])
-          {     
-            $action_result['nfo']='AStat_tools_invalid_date';     
+          {
+            $action_result['nfo']='AStat_tools_invalid_date';
             $ok_to_purge=false;
           }
           elseif(date("Ymd", $purge_date)>=date("Ymd"))
           {
-            $action_result['nfo']='AStat_tools_invalid_date2';      
+            $action_result['nfo']='AStat_tools_invalid_date2';
             $ok_to_purge=false;
           }
         }
+        elseif($_REQUEST['fAStat_purge_history_type']=='byipid0')
+        {
+          $fparam=$this->my_config['AStat_BlackListedIP'];
+        }
         else
         {
-          $fdate="";
+          $fparam="";
         }
 
         if($ok_to_purge)
         {
-          $result=$this->do_purge_history( $fdate, $_REQUEST['fAStat_purge_history_type']);
+          $result=$this->do_purge_history( $fparam, $_REQUEST['fAStat_purge_history_type']);
           if($result)
           {
@@ -1542,5 +1639,5 @@
       }
     }
-    // 
+    //
     elseif(isset($_POST['apply_tool_deleted_picture']))
     {
@@ -1566,5 +1663,5 @@
 
     // >> DELETED_CATEGORY -----------------------------------------------------
-    elseif(isset($_POST['apply_tool_deleted_category'])) 
+    elseif(isset($_POST['apply_tool_deleted_category']))
     {
       if(!is_adviser())
@@ -1587,7 +1684,7 @@
     }
     // << DELETED_CATEGORY -----------------------------------------------------
-      
+
     // >> DELETED USER ---------------------------------------------------------
-    elseif(isset($_POST['apply_tool_deleted_user'])) 
+    elseif(isset($_POST['apply_tool_deleted_user']))
     {
       if(!is_adviser())
@@ -1615,10 +1712,10 @@
     $table_exists=$this->verify_AStat_picture_table_status();
     if($table_exists==true)
-    { 
+    {
       $template_datas["ASTAT_DELETED_PICTURE_DO_ACTION"] = "checked";
       $template_datas["ASTAT_DELETED_PICTURE_PREPARE"] = "disabled";
     }
     else
-    { 
+    {
       $template_datas["ASTAT_DELETED_PICTURE_PREPARE"]="checked";
       $template_datas["ASTAT_DELETED_PICTURE_DO_ACTION"]="disabled";
@@ -1632,8 +1729,8 @@
     {
       $list='';
-      for($i=0;$i<count($nfo[2]);$i++) 
-      { 
+      for($i=0;$i<count($nfo[2]);$i++)
+      {
         if($nfo[2][$i][0]>1) { $s='s'; } else { $s=''; }
-        $list.="<li>image_id #".$nfo[2][$i][1]." : ".$nfo[2][$i][0]." ".l10n("AStat_event$s")."</li>"; 
+        $list.="<li>image_id #".$nfo[2][$i][1]." : ".$nfo[2][$i][0]." ".l10n("AStat_event$s")."</li>";
       }
       $template_datas["ASTAT_DELETED_PICTURE_NFO"] = sprintf(l10n('AStat_tools_deleted_picture_nfo1'), $nfo[0], $nfo[1], $list);
@@ -1644,5 +1741,5 @@
     {
       $template_datas["ASTAT_DELETED_PICTURE_NFO"] = l10n('AStat_tools_deleted_picture_nfo2');
-    } 
+    }
     // << DISPLAY DELETED_PICTURE NFO ------------------------------------------
 
@@ -1672,8 +1769,8 @@
     {
       $list='';
-      for($i=0;$i<count($nfo[2]);$i++) 
-      { 
+      for($i=0;$i<count($nfo[2]);$i++)
+      {
         if($nfo[2][$i][0]>1) { $s='s'; } else { $s=''; }
-        $list.="<li>user_id #".$nfo[2][$i][1]." : ".$nfo[2][$i][0]." ".l10n("AStat_event$s")."</li>"; 
+        $list.="<li>user_id #".$nfo[2][$i][1]." : ".$nfo[2][$i][0]." ".l10n("AStat_event$s")."</li>";
       }
       $template_datas["ASTAT_DELETED_USER_NFO"] = sprintf(l10n('AStat_tools_deleted_user_nfo1'), $nfo[0], $nfo[1], $list);
@@ -1683,5 +1780,5 @@
     {
       $template_datas["ASTAT_DELETED_USER_NFO"] = l10n('AStat_tools_deleted_user_nfo2');
-    } 
+    }
     // << DISPLAY DELETED USER NFO ---------------------------------------------
 
@@ -1692,23 +1789,30 @@
     {
       $template_datas["ASTAT_GENERAL_NFO"] = sprintf(l10n('AStat_tools_general_nfo_nfo'),
-              $nfo[0], 
+              $nfo[0],
               $this->formatoctet($nfo[3]+$nfo[4], "A", " ", 2, true),
               $this->formatoctet($nfo[3], "A", " ", 2, true),
               $this->formatoctet($nfo[4], "A", " ", 2, true),
-              date(l10n('AStat_date_time_format'), strtotime($nfo[2])), 
+              date(l10n('AStat_date_time_format'), strtotime($nfo[2])),
               date(l10n('AStat_date_time_format'), strtotime($nfo[1])) );
+      $template_datas["ASTAT_MINDATE"]=date("m/d/Y",strtotime($nfo[2]));
     }
 
     $nfo=$this->purge_history_count_imageid0();
     $template_datas["ASTAT_PURGE_HISTORY_IMAGE_NFO"] = sprintf(l10n('AStat_tools_purge_history_imageid0'), $nfo);
-    if($nfo==0) 
-    { 
+    if($nfo==0)
+    {
       $template_datas["ASTAT_PURGE_HISTORY_IMAGE_DISABLED"] = " disabled ";
     }
     $nfo=$this->purge_history_count_categoryid0();
     $template_datas["ASTAT_PURGE_HISTORY_CATEGORY_NFO"] = sprintf(l10n('AStat_tools_purge_history_categoryid0'), $nfo);
-    if($nfo==0) 
-    { 
+    if($nfo==0)
+    {
       $template_datas["ASTAT_PURGE_HISTORY_CATEGORY_DISABLED"] = " disabled ";
+    }
+    $nfo=$this->purge_history_count_ipid0();
+    $template_datas["ASTAT_PURGE_HISTORY_IP_NFO"] = sprintf(l10n('AStat_tools_purge_history_ipid0'), $nfo[1], $nfo[0]);
+    if($nfo[0]==0)
+    {
+      $template_datas["ASTAT_PURGE_HISTORY_IP_DISABLED"] = " disabled ";
     }
     // << GENERAL NFO ----------------------------------------------------------
@@ -1752,7 +1856,7 @@
     allow to force HISTORY_TABLE.user_id at  2 (guest) for records with user ident
     doesn't exist anymore in the USERS_TABLE
-    
+
     Two usages :
-      - analyse : return infos about records wich need to be updated 
+      - analyse : return infos about records wich need to be updated
           * number of users
           * number of records in HISTORY_TABLE
@@ -1763,5 +1867,5 @@
   {
     $returned = array(-1,0,'');
-    
+
     if($mode=='analyse')
     {
@@ -1781,5 +1885,5 @@
           $returned[1]+=$row[0];
         }
-        
+
       }
     }
@@ -1797,5 +1901,5 @@
 
   /*
-    tools : deleted_picture 
+    tools : deleted_picture
     analyse history to find deleted pictures
     Two functions :
@@ -1809,5 +1913,5 @@
   {
     $returned = array(-1,0,'');
-    
+
     if($mode=='analyse')
     {
@@ -1828,5 +1932,5 @@
           $returned[1]+=$row[0];
         }
-        
+
       }
     }
@@ -1834,6 +1938,6 @@
     {
       $sql="UPDATE ".HISTORY_TABLE."
-        SET image_id = 0 
-        WHERE ".HISTORY_TABLE.".image_id > 0 
+        SET image_id = 0
+        WHERE ".HISTORY_TABLE.".image_id > 0
           AND ".HISTORY_TABLE.".image_id NOT IN (SELECT id FROM ".IMAGES_TABLE.")";
       $result=pwg_query($sql);
@@ -1861,5 +1965,5 @@
   {
     $returned = array(-1,0,'');
-    
+
     if($mode=='analyse')
     {
@@ -1880,5 +1984,5 @@
           $returned[1]+=$row[0];
         }
-        
+
       }
     }
@@ -1886,6 +1990,6 @@
     {
       $sql="UPDATE ".HISTORY_TABLE."
-        SET category_id = NULL, section = 'deleted_cat'  
-        WHERE ".HISTORY_TABLE.".category_id > 0 
+        SET category_id = NULL, section = 'deleted_cat'
+        WHERE ".HISTORY_TABLE.".category_id > 0
           AND ".HISTORY_TABLE.".category_id NOT IN (SELECT id FROM ".CATEGORIES_TABLE.")";
       $result=pwg_query($sql);
@@ -1901,5 +2005,5 @@
 
   /*
-    tools : general_nfo 
+    tools : general_nfo
     return infos about historic
       0 : nulber of records
@@ -1912,5 +2016,5 @@
   {
     $returned = array(-1,'','',0,0);
-  
+
     $sql="SELECT count(id) AS NbRecord, MAX(concat(date,' ', time)) AS LastDate, MIN(concat(date,' ', time)) AS FirstDate ";
     $sql.=" FROM ".HISTORY_TABLE;
@@ -1919,7 +2023,7 @@
     {
       $row = mysql_fetch_array($result);
-      if(is_array($row)) 
-      { 
-        $returned = $row; 
+      if(is_array($row))
+      {
+        $returned = $row;
         $sql="SHOW TABLE STATUS LIKE '".HISTORY_TABLE."';";
         $result=pwg_query($sql);
@@ -1936,14 +2040,14 @@
 
   /*
-    tools : do_purge_history 
+    tools : do_purge_history
     do a purge of history table :
     - $purgetype='bydate' : purge all record wich date is less than given date
     - $purgetype='byimageid0' : with and image_id = 0
   ------------------------------------------------------------------------------------ */
-  private function do_purge_history($date, $purgetype)
+  private function do_purge_history($param, $purgetype)
   {
     if($purgetype=='bydate')
     {
-      $sql="DELETE FROM ".HISTORY_TABLE." WHERE date < '$date'";
+      $sql="DELETE FROM ".HISTORY_TABLE." WHERE date < '$param'";
     }
     elseif($purgetype=='byimageid0')
@@ -1955,6 +2059,10 @@
       $sql="DELETE FROM ".HISTORY_TABLE." WHERE category_id is null and section='deleted_cat'";
     }
-    else 
-    { 
+    elseif($purgetype=='byipid0')
+    {
+      $sql="DELETE FROM ".HISTORY_TABLE." WHERE ".$this->make_IP_where_clause($param);
+    }
+    else
+    {
       return(false);
     }
@@ -1965,5 +2073,5 @@
       $sql="OPTIMIZE TABLE ".HISTORY_TABLE;
       $result=pwg_query($sql);
-      return($result);    
+      return($result);
     }
     return(false);
@@ -1994,9 +2102,37 @@
   }
 
-
-  /*
-    tools : deleted_picture 
-    > verify_AStat_picture_table_status : 
-    > prepare_AStat_picture_table : 
+  private function purge_history_count_ipid0()
+  {
+    if($this->my_config['AStat_BlackListedIP']!="")
+    {
+      $list=explode(',', $this->my_config['AStat_BlackListedIP']);
+    }
+    else
+    {
+      $list=array();
+    }
+
+    $returned=array(0,count($list));
+
+    if($this->my_config['AStat_BlackListedIP']!='')
+    {
+      $sql="SELECT COUNT(id)
+            FROM ".HISTORY_TABLE."
+            WHERE ".$this->make_IP_where_clause($this->my_config['AStat_BlackListedIP']);
+      $result=pwg_query($sql);
+      if($result)
+      {
+        $row=mysql_fetch_array($result);
+        $returned[0]=$row[0];
+      }
+    }
+    return($returned);
+  }
+
+
+  /*
+    tools : deleted_picture
+    > verify_AStat_picture_table_status :
+    > prepare_AStat_picture_table :
   */
   private function verify_AStat_picture_table_status()
@@ -2020,6 +2156,6 @@
     global $prefixeTable;
 
-    $sql="CREATE TABLE ".$prefixeTable."AStat_picture (PRIMARY KEY (id), KEY ifile(file)) 
-      SELECT id, file 
+    $sql="CREATE TABLE ".$prefixeTable."AStat_picture (PRIMARY KEY (id), KEY ifile(file))
+      SELECT id, file
       FROM ".IMAGES_TABLE;
     $result=pwg_query($sql);
@@ -2027,5 +2163,5 @@
     {
       return(true);
-    } 
+    }
     return(false);
   }
@@ -2035,10 +2171,10 @@
     global $prefixeTable;
     $returned=array(false,0,0);
-    
+
     if($this->verify_AStat_picture_table_status())
     {
       $sql="SELECT count(DISTINCT ".$prefixeTable."AStat_picture.id), count(".HISTORY_TABLE.".id)
-        FROM ".HISTORY_TABLE.", ".$prefixeTable."AStat_picture 
-        WHERE ".HISTORY_TABLE.".image_id = ".$prefixeTable."AStat_picture.id 
+        FROM ".HISTORY_TABLE.", ".$prefixeTable."AStat_picture
+        WHERE ".HISTORY_TABLE.".image_id = ".$prefixeTable."AStat_picture.id
         AND ".HISTORY_TABLE.".image_id NOT IN (SELECT id FROM ".IMAGES_TABLE.")
         ORDER BY ".$prefixeTable."AStat_picture.id";
@@ -2064,8 +2200,8 @@
 
     $sql="CREATE TABLE ".$prefixeTable."AStat_picture2 (PRIMARY KEY (OldId))
-      SELECT AStat_tmp.id as OldId , ".IMAGES_TABLE.".id as NewId, ".IMAGES_TABLE.".storage_category_id as NewCatId 
+      SELECT AStat_tmp.id as OldId , ".IMAGES_TABLE.".id as NewId, ".IMAGES_TABLE.".storage_category_id as NewCatId
       FROM (SELECT DISTINCT ".$prefixeTable."AStat_picture.*
-        FROM ".HISTORY_TABLE.", ".$prefixeTable."AStat_picture 
-        WHERE ".HISTORY_TABLE.".image_id = ".$prefixeTable."AStat_picture.id 
+        FROM ".HISTORY_TABLE.", ".$prefixeTable."AStat_picture
+        WHERE ".HISTORY_TABLE.".image_id = ".$prefixeTable."AStat_picture.id
         AND ".HISTORY_TABLE.".image_id NOT IN (SELECT id FROM ".IMAGES_TABLE.")
         ORDER BY ".$prefixeTable."AStat_picture.id
@@ -2077,5 +2213,5 @@
       $sql="UPDATE ".HISTORY_TABLE.", ".$prefixeTable."AStat_picture2, ".IMAGES_TABLE."
         SET ".HISTORY_TABLE.".image_id = ".$prefixeTable."AStat_picture2.NewId,
-            ".HISTORY_TABLE.".category_id = ".$prefixeTable."AStat_picture2.NewCatId 
+            ".HISTORY_TABLE.".category_id = ".$prefixeTable."AStat_picture2.NewCatId
         WHERE ".$prefixeTable."AStat_picture2.OldId = ".HISTORY_TABLE.".image_id";
       $result=pwg_query($sql);
@@ -2088,5 +2224,5 @@
       $sql="DROP TABLE IF EXISTS ".$prefixeTable."AStat_picture";
       $result=pwg_query($sql);
-    } 
+    }
     return($returned);
   }
@@ -2146,5 +2282,5 @@
 
 
-  /* 
+  /*
     this function make SELECT "WHERE" clause for filter list
 
@@ -2173,5 +2309,5 @@
 
 
-  /* 
+  /*
     format text : <a href="$link">$value</a>
   */
@@ -2187,10 +2323,10 @@
   {  //basic test, maybe a pcre will be more appropriate...
     $tmp=explode('.', $ip);
-    if(count($tmp)!=4) 
+    if(count($tmp)!=4)
     { return (false); }
 
-    for($i=0;$i<4;$i++) 
-    { 
-      if(!is_numeric($tmp[$i])) { return (false); } 
+    for($i=0;$i<4;$i++)
+    {
+      if(!is_numeric($tmp[$i])) { return (false); }
     }
     return (true);
@@ -2203,9 +2339,9 @@
   {  //filename can be <filename.truc.jpeg> for example
     $tmp = explode('.', $file);
-    if(count($tmp)>1) { $tmp[count($tmp)-1] = $newext; }  
-    return implode('.', $tmp);     
+    if(count($tmp)>1) { $tmp[count($tmp)-1] = $newext; }
+    return implode('.', $tmp);
   }
 
-  /* 
+  /*
       format number $octets with unit
       $format = "A" : auto
@@ -2229,5 +2365,5 @@
     { $format="M"; }
     else
-    { $format="G"; }  
+    { $format="G"; }
     }
     switch($format)
@@ -2244,7 +2380,7 @@
     case "G":
       $unite="Go"; $div=1024000000;
-      break;  
-    }
-    
+      break;
+    }
+
     $retour=number_format($octets/$div, $prec, '.', $thsep);
     if($unitevis)
@@ -2253,4 +2389,58 @@
   }
 
+  private function make_IP_where_clause($list)
+  {
+    $returned="";
+
+    $tmp=explode(",", $list);
+    foreach($tmp as $key=>$val)
+    {
+      if($returned!="") { $returned.=" OR "; }
+      $returned.=" IP LIKE '".$val."' ";
+    }
+    if($returned!="")
+    {
+      $returned ="(".$returned.")";
+    }
+    return($returned);
+  }
+
+  /* ---------------------------------------------------------------------------
+   * AJAX functions
+   * ------------------------------------------------------------------------- */
+  protected function ajax_listip($filter, $exclude)
+  {
+    $sql="SELECT IP, COUNT(id) as NbEvents FROM ".HISTORY_TABLE;
+
+    $where=array();
+    if($filter!="")
+    {
+      $where[]=" IP LIKE '".$filter."' ";
+    }
+    if($exclude!="")
+    {
+      $where[]=" NOT ".$this->make_IP_where_clause($exclude);
+    }
+    if(count($where)>0)
+    {
+      $sql.=" WHERE ".implode(" AND ", $where);
+    }
+    $sql.=" GROUP BY IP ORDER BY NbEvents desc, IP asc LIMIT 0,100";
+
+    $list="<select multiple id='iipsellist'>";
+    $result=pwg_query($sql);
+    if($result)
+    {
+      while($row=mysql_fetch_array($result))
+      {
+        $list.="<option value='".$row['IP']."'>".$row['IP'].str_repeat("&nbsp;", 15-strlen($row['IP']))."&nbsp;&nbsp;&nbsp;&nbsp;(".$row['NbEvents'].")</option>";
+      }
+    }
+    $list.="</select>";
+
+    return($list);
+  }
+
+
 } // AStat_AI class
 
Index: /extensions/AStat/language/en_UK/plugin.lang.php
===================================================================
--- /extensions/AStat/language/en_UK/plugin.lang.php	(revision 3705)
+++ /extensions/AStat/language/en_UK/plugin.lang.php	(revision 3706)
@@ -108,5 +108,5 @@
 $lang['AStat_sortip_page'] = 'Number of pages seen';
 $lang['AStat_RefIPLabel'] = 'User / IP Adress';
-$lang['AStat_DefaultSortIP'] = 'Default sort'; 
+$lang['AStat_DefaultSortIP'] = 'Default sort';
 $lang['AStat_general_config'] = 'Global settings';
 $lang['AStat_SeeTimeRequests'] = 'Show time execution of requests';
@@ -162,11 +162,11 @@
 $lang['AStat_tools_purge_history_nfo'] = "This function will clean the history from all past event from a given date and will optimize the table.<br><b>Operation cannot be reversed</b>.";
 $lang['AStat_tools_purge_history_apply'] = "Clean";
-$lang['AStat_tools_purge_history_date'] = "To purge all events prior (DD/MM/AAAA) ";
-$lang['AStat_tools_purge_history_imageid0'] = "To purge all events associated to deleted images (%s events in history)"; 
-$lang['AStat_tools_purge_history_categoryid0'] = "To purge events associated to deleted categories (%s events in history)"; 
+$lang['AStat_tools_purge_history_date'] = "To purge all events prior ";
+$lang['AStat_tools_purge_history_imageid0'] = "To purge all events associated to deleted images (%s events in history)";
+$lang['AStat_tools_purge_history_categoryid0'] = "To purge events associated to deleted categories (%s events in history)";
 $lang['AStat_tools_result_ko'] = "An error has occurred. Action has not been correctly performed";
-$lang['AStat_tools_invalid_date'] = "Given date is not valid !"; 
-$lang['AStat_tools_invalid_date2'] = "Given date is upper or equal to date of the day"; 
-$lang['AStat_tools_deleted_picture_error0'] = "Priming has failed"; 
+$lang['AStat_tools_invalid_date'] = "Given date is not valid !";
+$lang['AStat_tools_invalid_date2'] = "Given date is upper or equal to date of the day";
+$lang['AStat_tools_deleted_picture_error0'] = "Priming has failed";
 $lang['AStat_tools_deleted_picture_error1'] = "New association of events in the history has failed";
 $lang['AStat_tools_deleted_picture_ok0'] = "Priming of the table succeeded, files can be reorganized on the server";
@@ -204,4 +204,10 @@
 $lang['AStat_gpc2_not_installed'] = "Plugin 'Grum Plugins Classes 2' (release >= %s) is needed to install AStat";
 
+//--- >> v2.1.0 >>
+$lang['AStat_BlackListedIP'] = "Blacklisted IP";
+$lang['AStat_AddIP'] = "Add to the blacklist";
+$lang['AStat_DelIP'] = "Delete from the blacklist";
+$lang['AStat_tools_purge_history_ipid0'] = "To purge events associated with IP from the blacklist (%s items in the blacklist, %s events in history)"
+$lang['AStat_IP_blacklist'] = "Add to the blacklist";
 
 ?>
Index: /extensions/AStat/language/es_ES/plugin.lang.php
===================================================================
--- /extensions/AStat/language/es_ES/plugin.lang.php	(revision 3705)
+++ /extensions/AStat/language/es_ES/plugin.lang.php	(revision 3706)
@@ -110,5 +110,5 @@
 $lang['AStat_sortip_page'] = 'Número de páginas vistas';
 $lang['AStat_RefIPLabel'] = 'Utilizador / Dirección IP';
-$lang['AStat_DefaultSortIP'] = 'Selección por defecto'; 
+$lang['AStat_DefaultSortIP'] = 'Selección por defecto';
 $lang['AStat_general_config'] = 'Configuración general';
 $lang['AStat_SeeTimeRequests'] = 'Ver la duración de ejecución de las demandas';
@@ -158,20 +158,20 @@
 $lang['AStat_section_old_deleted_cat'] = 'Antigua categoría que no ha podido ser importada';
 $lang['AStat_tools_general_nfo_nfo'] = "<ul><li>%s acontecimientos están presentes en la reseña histórica</li>
-						<li>La mesa pesa %s (Mesa: %s ; Índice: %s)</li>
-						<li>Data del primer acontecimiento : %s</li>
-						<li>Data del último acontecimiento : %s</li>";
+            <li>La mesa pesa %s (Mesa: %s ; Índice: %s)</li>
+            <li>Data del primer acontecimiento : %s</li>
+            <li>Data del último acontecimiento : %s</li>";
 $lang['AStat_tools_purge_history'] = "Purga de la reseña histórica";
 $lang['AStat_tools_purge_history_nfo'] = "Esta función efectúa sobre la reseña histórica una purga de todos los acontecimientos anteriores a una fecha dada y optimiza la mesa. <br><b>Esta acción irreversible</b>.";
 $lang['AStat_tools_purge_history_apply'] = "Aplicar la purga";
-$lang['AStat_tools_purge_history_date'] = "Purgar los acontecimientos anteriores a (JJ/MM/AAAA) "; 
-$lang['AStat_tools_purge_history_imageid0'] = "Purgar los acontecimientos asociados con imágenes suprimidas  (%s acontecimientos en la reseña histórica)"; 
-$lang['AStat_tools_purge_history_categoryid0'] = "Purger les évènements associés à des catégories supprimées (%s acontecimientos en la reseña histórica)"; 
+$lang['AStat_tools_purge_history_date'] = "Purgar los acontecimientos anteriores a (JJ/MM/AAAA) ";
+$lang['AStat_tools_purge_history_imageid0'] = "Purgar los acontecimientos asociados con imágenes suprimidas (%s acontecimientos en la reseña histórica)";
+$lang['AStat_tools_purge_history_categoryid0'] = "Purgar los acontecimientos asociados con categorías suprimidas (%s acontecimientos en la reseña histórica)";
 $lang['AStat_tools_result_ko'] = "Un error sobrevino, la acción no pudo ser correctamente llevada";
-$lang['AStat_tools_invalid_date'] = "La fecha informada es inválida !"; 
-$lang['AStat_tools_invalid_date2'] = "¡ La fecha informada es superior o igual a la fecha del día!"; 
-$lang['AStat_tools_deleted_picture_error0'] = "La preparación fue suspendido"; 
-$lang['AStat_tools_deleted_picture_error1'] = "El réassocation de los acontecimientos de la reseña histórica fue suspendido"; 
-$lang['AStat_tools_deleted_picture_ok0'] = "La preparación de la mesa tuvo éxito, usted puede proceder a la reorganización de los ficheros sobre el servidor"; 
-$lang['AStat_tools_deleted_picture_ok1'] = "El réassocation de los acontecimientos de la reseña histórica tuvo éxito"; 
+$lang['AStat_tools_invalid_date'] = "La fecha informada es inválida !";
+$lang['AStat_tools_invalid_date2'] = "¡ La fecha informada es superior o igual a la fecha del día!";
+$lang['AStat_tools_deleted_picture_error0'] = "La preparación fue suspendido";
+$lang['AStat_tools_deleted_picture_error1'] = "El réassocation de los acontecimientos de la reseña histórica fue suspendido";
+$lang['AStat_tools_deleted_picture_ok0'] = "La preparación de la mesa tuvo éxito, usted puede proceder a la reorganización de los ficheros sobre el servidor";
+$lang['AStat_tools_deleted_picture_ok1'] = "El réassocation de los acontecimientos de la reseña histórica tuvo éxito";
 
 $lang['AStat_tools_deleted_picture_nfo3'] = "Cuando imágenes son desplazadas físicamente por un repertorio, la sincronización suprime las imágenes de la base y las reinserta con una nueva referencia. Una imagen trasladada deja pues en los acontecimientos de la reseña histórica un rastro con referencias que no existen más.</p><p><i>AStat</i> Permite :<ul><li>de memorizar las referencias de imágenes <u><i>delantera</i></u> De proceder a la sincronización </li><li>de reasociar en los acontecimientos de la reseña histórica las buenas referencias de una imagen <u><i>Después</i></u> haber procedido a una sincronización</li></ul></p><p><b>Atención !</b><br><ul><li> si imágenes diferentes tienen los mismos nombres de ficheros, el resultado es imprevisible</li><li>este carácter funcional no puede tratar las imágenes para las cuales el fichero sobre el servidor fue renomé</li></ul>";
@@ -183,5 +183,5 @@
 
 
-$lang['AStat_tools_deleted_category'] = 'Poner al día en la reseña histórica las referencias de categorías'; 
+$lang['AStat_tools_deleted_category'] = 'Poner al día en la reseña histórica las referencias de categorías';
 $lang['AStat_tools_deleted_category_nfo0'] = "Afecta para los acontecimientos de la reseña histórica référencants categorías que no existen más, la sección <i>Categoría suprimida</i>."; // v1.3.0
 $lang['AStat_tools_deleted_category_nfo1'] = "%s categorías inexistentes son presentes en los acontecimientos de la reseña histórica e impactent %s acontecimientos : <ul>%s</ul>";
@@ -205,4 +205,9 @@
 $lang['AStat_gpc2_not_installed'] = "El plugin ' Grum Plugins Classes 2' (version >= %s) es necesario para que AStat pueda estar instalado";
 
-
+//--- >> v2.1.0 >>
+$lang['AStat_BlackListedIP'] = "Lista negra de direcciones IP";
+$lang['AStat_AddIP'] = "Añadir a la lista negra";
+$lang['AStat_DelIP'] = "Eliminar de la lista negra";
+$lang['AStat_tools_purge_history_ipid0'] = "Para purgar los acontecimientos asociados con la IP de la lista negra (%s elementos en la lista negra, %s acontecimientos en la reseña histórica)";
+$lang['AStat_IP_blacklist'] = "Poner en lista negra";
 ?>
Index: /extensions/AStat/language/fr_FR/plugin.lang.php
===================================================================
--- /extensions/AStat/language/fr_FR/plugin.lang.php	(revision 3705)
+++ /extensions/AStat/language/fr_FR/plugin.lang.php	(revision 3706)
@@ -110,5 +110,5 @@
 $lang['AStat_sortip_page'] = 'Nombre de pages vues';
 $lang['AStat_RefIPLabel'] = 'Utilisateur / Adresse IP';
-$lang['AStat_DefaultSortIP'] = 'Tri par défaut'; 
+$lang['AStat_DefaultSortIP'] = 'Tri par défaut';
 $lang['AStat_general_config'] = 'Configuration générale';
 $lang['AStat_SeeTimeRequests'] = 'Voir la durée d\'exécution des requêtes';
@@ -158,20 +158,20 @@
 $lang['AStat_section_old_deleted_cat'] = 'Ancienne catégorie n\'ayant pu être importée';
 $lang['AStat_tools_general_nfo_nfo'] = "<ul><li>%s évènements sont présents dans l'historique</li>
-						<li>La table pèse %s (Table: %s ; Index: %s)</li>
-						<li>Date du premier évènement : %s</li>
-						<li>Date du dernier évènement : %s</li>";
+            <li>La table pèse %s (Table: %s ; Index: %s)</li>
+            <li>Date du premier évènement : %s</li>
+            <li>Date du dernier évènement : %s</li>";
 $lang['AStat_tools_purge_history'] = "Purge de l'historique";
 $lang['AStat_tools_purge_history_nfo'] = "Cette fonction effectue sur l'historique une purge de tous les évènements antérieurs à une date donnée et optimise la table. <br><b>Cette action irréversible</b>.";
 $lang['AStat_tools_purge_history_apply'] = "Appliquer la purge";
-$lang['AStat_tools_purge_history_date'] = "Purger les évènements antérieurs à (JJ/MM/AAAA) "; 
-$lang['AStat_tools_purge_history_imageid0'] = "Purger les évènements associés à des images supprimées (%s évènements dans l'historique)"; 
-$lang['AStat_tools_purge_history_categoryid0'] = "Purger les évènements associés à des catégories supprimées (%s évènements dans l'historique)"; 
+$lang['AStat_tools_purge_history_date'] = "Purger les évènements antérieurs à ";
+$lang['AStat_tools_purge_history_imageid0'] = "Purger les évènements associés à des images supprimées (%s évènements dans l'historique)";
+$lang['AStat_tools_purge_history_categoryid0'] = "Purger les évènements associés à des catégories supprimées (%s évènements dans l'historique)";
 $lang['AStat_tools_result_ko'] = "Une erreur est survenue, l'action n'a pu être correctement menée";
-$lang['AStat_tools_invalid_date'] = "La date renseignée est invalide !"; 
-$lang['AStat_tools_invalid_date2'] = "La date renseignée est supérieure ou égale à la date du jour !"; 
-$lang['AStat_tools_deleted_picture_error0'] = "La préparation a échouée"; 
-$lang['AStat_tools_deleted_picture_error1'] = "La réassocation des évènements de l'historique a échouée"; 
-$lang['AStat_tools_deleted_picture_ok0'] = "La préparation de la table a réussie, vous pouvez procéder à la réorganisation des fichiers sur le serveur"; 
-$lang['AStat_tools_deleted_picture_ok1'] = "La réassocation des évènements de l'historique a réussie"; 
+$lang['AStat_tools_invalid_date'] = "La date renseignée est invalide !";
+$lang['AStat_tools_invalid_date2'] = "La date renseignée est supérieure ou égale à la date du jour !";
+$lang['AStat_tools_deleted_picture_error0'] = "La préparation a échouée";
+$lang['AStat_tools_deleted_picture_error1'] = "La réassocation des évènements de l'historique a échouée";
+$lang['AStat_tools_deleted_picture_ok0'] = "La préparation de la table a réussie, vous pouvez procéder à la réorganisation des fichiers sur le serveur";
+$lang['AStat_tools_deleted_picture_ok1'] = "La réassocation des évènements de l'historique a réussie";
 
 $lang['AStat_tools_deleted_picture_nfo3'] = "Lorsque des images sont déplacées physiquement d'un répertoire, la synchronisation supprime les images de la base et les réinsère avec une nouvelle référence. Une image déplacée laisse donc dans les évènements de l'historique une trace avec des références qui n'existent plus.</p><p><i>AStat</i> permet :<ul><li>de mémoriser les références d'images <u><i>avant</i></u> de procéder à la synchronisation </li><li>de réassocier dans les évènements de l'historique les bonnes références d'une image <u><i>après</i></u> avoir procédé à une synchronisation</li></ul></p><p><b>Attention !</b><br><ul><li>si des images différentes ont les mêmes noms de fichiers, le résultat est imprévisible</li><li>cette fonctionnalité ne peut pas traiter les images pour lesquelles le fichier sur le serveur a été renomé</li></ul>";
@@ -183,5 +183,5 @@
 
 
-$lang['AStat_tools_deleted_category'] = 'Mettre à jour dans l\'historique les références de catégories'; 
+$lang['AStat_tools_deleted_category'] = 'Mettre à jour dans l\'historique les références de catégories';
 $lang['AStat_tools_deleted_category_nfo0'] = "Affecte pour les évènements de l'historique référencants des catégories qui n'existent plus, la section <i>Catégorie supprimée</i>."; // v1.3.0
 $lang['AStat_tools_deleted_category_nfo1'] = "%s catégories inexistantes sont présentes dans les évènements de l'historique et impactent %s évènements : <ul>%s</ul>";
@@ -205,4 +205,10 @@
 $lang['AStat_gpc2_not_installed'] = "Le plugin 'Grum Plugins Classes 2' (version >= %s) est nécessaire pour que AStat puisse être installé";
 
+//--- >> v2.1.0 >>
+$lang['AStat_BlackListedIP'] = "Liste noire des adresses IP";
+$lang['AStat_AddIP'] = "Ajouter à la liste";
+$lang['AStat_DelIP'] = "Supprimer de la liste";
+$lang['AStat_tools_purge_history_ipid0'] = "Purger les évènements associés aux IP de la liste noire (%s éléments dans la liste noire, %s évènements dans l'historique)";
+$lang['AStat_IP_blacklist'] = "Mettre sur liste noire";
 
 ?>
Index: /extensions/AStat/main.inc.php
===================================================================
--- /extensions/AStat/main.inc.php	(revision 3705)
+++ /extensions/AStat/main.inc.php	(revision 3706)
@@ -2,5 +2,5 @@
 /*
 Plugin Name: AStat.2
-Version: 2.0.5
+Version: 2.1.0
 Description: Statistiques avancées / Advanced statistics
 Plugin URI: http://phpwebgallery.net/ext/extension_view.php?eid=172
@@ -21,17 +21,32 @@
 :: HISTORY
 
-2.0.0       - 13/05/07  - release for piwigo 2.0
-2.0.1       - 09/03/01  - bug corrected (can't open file because plugin directory
-                          was hardcoded...)
-2.0.2       - 09/03/08  - bug referenced
-                            english forum : http://piwigo.org/forum/viewtopic.php?pid=105990#p105990
-                            french forum  : http://fr.piwigo.org/forum/viewtopic.php?pid=107205#p107205
-                          SQL request for stat by categories works with mySQL 4.1.22 and not with mySQL 5
-2.0.3       - 28/03/08  - bug referenced
-                            french forum  : http://fr.piwigo.org/forum/viewtopic.php?pid=107236#p107236
-                          SQL request for stat by categories works with mySQL 4.1.22 and not with mySQL 5
-2.0.4       - 21/05/09  - bug on tools 
-                            it was not possible to use tools to manage deleted items
-2.0.5       - 07/07/09  - bug in code - invalid character on line 2194
+
+| release | date       |
+| 2.0.0   | 2007/05/07 | * release for piwigo 2.0
+| 2.0.1   | 2008/03/01 | * bug corrected (can't open file because plugin directory
+|         |            |   was hardcoded...)
+| 2.0.2   | 2008/03/09 | * bug referenced
+|         |            |    english forum : http://piwigo.org/forum/viewtopic.php?pid=105990#p105990
+|         |            |    french forum  : http://fr.piwigo.org/forum/viewtopic.php?pid=107205#p107205
+|         |            |    SQL request for stat by categories works with mySQL 4.1.22 and not with mySQL 5
+| 2.0.3   | 2008/03/28 | * bug referenced
+|         |            |   french forum  : http://fr.piwigo.org/forum/viewtopic.php?pid=107236#p107236
+|         |            |   SQL request for stat by categories works with mySQL 4.1.22 and not with mySQL 5
+| 2.0.4   | 2009/05/21 | * bug on tools
+|         |            |   it was not possible to use tools to manage deleted items
+| 2.0.5   | 2009/07/07 | * bug in code - invalid character on line 2194
+| 2.1.0   | 2009/07/28 | * add a blacklist for IP and use it for stats
+|         |            | * new tools
+|         |            |    - possibility to purge history on blacklisted IP address
+|         |            |    - use of jQuery datepicker for purge date
+|         |            |
+|         |            |
+|         |            |
+|         |            |
+|         |            |
+|         |            |
+|         |            |
+
+
 
 :: TO DO
@@ -55,5 +70,5 @@
 define('ASTAT_PATH' , PHPWG_PLUGINS_PATH . ASTAT_DIR . '/');
 
-define('ASTAT_VERSION' , '2.0.5'); // => ne pas oublier la version dans l'entête !!
+define('ASTAT_VERSION' , '2.1.0'); // => ne pas oublier la version dans l'entête !!
 
 global $prefixeTable;
