Index: inc/admin/class.dc.filter.php
===================================================================
--- inc/admin/class.dc.filter.php	(revision 886)
+++ 	(revision )
@@ -1,796 +1,0 @@
-<?php
-# -- BEGIN LICENSE BLOCK ---------------------------------------
-#
-# This file is part of Dotclear 2.
-#
-# Copyright (c) 2003-2011 Olivier Meunier & Association Dotclear
-# Licensed under the GPL version 2.0 license.
-# See LICENSE file or
-# http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
-#
-# -- END LICENSE BLOCK -----------------------------------------
-
-
-/**
-@ingroup DC_CORE
-@nosubgrouping
-@brief Interface to add extra data to filterset.
-
-Note : the instance will be cloned to ensure dual filter edit fields / displayed fields
-Be sure to enable correct object cloning then, using the __clone method
-*/
-interface dcFilterExtraInterface {
-	/**
-	Return extra data to display in right column
-	
-	@param	core		<b>dcCore</b>		Dotclear core reference
-	@param	form_prefix	<b>string</b>		form prefix to use for parameters
-	*/
-	public function getFormContent();
-
-	/**
-	Set dedicated data from form submission 
-	(either $_GET or $_POST depending on the context
-	
-	@param	data		<b>array</b>		Data to retrieve information from
-	*/
-	public function initializeFromData($data);
-	
-	/**
-	Save data to configuration
-	*/
-	public function save();
-	
-	/**
-	Load data from configuration
-	*/
-	public function load();
-		
-	/**
-	Update query parameters with given settings
-	@param	params		<b>ArrayObject</b>		Params being sent to query
-	*/
-	public function applyFilters($params);
-	
-	
-	/**
-	Update parameters that will be used for inter-forms communications
-	(either query string or hidden fields).
-	The associative has to be updated with $param['key']='value'
-	@param	params		<b>ArrayObject</b>		Params to update
-	*/
-	public function updateRequestParams($params);
-}
-
-/**
-@ingroup DC_CORE
-@nosubgrouping
-@brief Dotclear FilterSet class.
-
-Dotclear FilterSet handles filters and columns when displaying items lists.
-*/
-class dcFilterSet {
-
-	protected $lfilters;		/// <b>array</b> lists of defined filters
-	protected $efilters;		/// <b>array</b> lists of defined filters
-	protected $form_prefix;		/// <b>string</b> displayed form prefix
-	protected $action; 			/// <b>string</b> form action page
-	protected $hideform;		/// <b>boolean</b> start form display hidden by default or not
-	protected $lextra;			/// <b>string</b> columns form
-	protected $eextra;			/// <b>string</b> columns form
-	protected $name;			/// <b>string</b> fieldset name
-	
-	/**
-	Inits dcFilterSet object
-	
-	@param	core		<b>dcCore</b>		Dotclear core reference
-	@param	form_prefix	<b>string</b>		form prefix to use for parameters
-	*/
-	public function __construct($name,$action,$form_prefix="f_") {
-		$this->name = $name;
-		$this->form_prefix=$form_prefix;
-		$this->lfilters = new ArrayObject();
-		$this->efilters = new ArrayObject();
-		$this->action = $action;
-		$this->lextra = null;
-		$this->eextra = null;
-		$this->filtered = false;
-	}
-	
-	/**
-	Adds a new filter to list
-	
-	@param	filter		<b>dcFilter</b>		the filter to add
-	*/
-	public function addFilter (Filter $filter) {
-		$filter->setFormPrefix($this->form_prefix);
-		$filter->setFilterSet($this);
-		$this->efilters[$filter->id] = $filter;
-		$this->lfilters[$filter->id] = clone $filter;
-		return $this;
-	}
-	
-	/**
-	Saves user filters to preferences
-	*/
-	protected function saveFilters() {
-		$ser = array();
-		$ws = $GLOBALS['core']->auth->user_prefs->addWorkspace('filters');
-		$data = new ArrayObject();
-		$data= $this->getFiltersAsParams($this->efilters);
-		$ws->put($this->name,serialize($data->getArrayCopy()),'string');
-		return $data;
-	}
-	
-	/**
-	Loads user filters from preferences
-	*/
-	protected function loadFilters() {
-		$ws = $GLOBALS['core']->auth->user_prefs->addWorkspace('filters');
-		$data = (!is_null($ws->{$this->name})) ? unserialize($ws->{$this->name}) : array();
-		if (is_array($data))
-			return $data;
-		else
-			return array();
-	}
-	
-	/**
-	Updates filters values according to form_data
-	To be called before any call to display() or getForm()
-	
-	@param	form_data	<b>array</b>	form values (usually $_GET or $_POST)
-	*/
-	protected function initializeFromData ($filters, $extra, $form_data) {
-		$this->hideform = true;
-		foreach ($filters as $filter) {
-			$filter->initializeFromData ($form_data);
-		}
-		if ($extra != null) {
-			$extra-> initializeFromData ($form_data);
-		}
-	}
-	
-	/**
-	Defines additional form in layout (right column)
-	
-	@param	html	<b>string</b>		the code to add
-	*/
-	public function setExtra($extra)
-	{
-		$this->lextra = $extra;
-		$this->eextra = clone $extra;
-		
-	}
-	
-	/**
-	Returns form fields as hidden fields
-	
-	@return	<b>string</b>	the corresponding html code
-	*/
-	public function getFormFieldsAsHidden() {
-		$ret='';
-		$arr = new ArrayObject();
-		foreach ($this->lfilters as $f) {
-			if ($f->isEnabled())
-				$f->updateRequestParams($arr);
-		}
-		if ($this->lextra != null) {
-			$this->lextra->updateRequestParams($arr);
-		}
-		foreach ($arr as $k=>$v) {
-			$ret.= form::hidden(array($k),$v);
-		}
-		$queryParams = $this->getFiltersAsParams($this->lfilters);
-		if ($this->lextra != null) {
-			$this->lextra->updateRequestParams($queryParams);
-		}
-		$ret .= form::hidden(array($this->form_prefix."query"), http_build_query($queryParams));
-		return $ret;
-	}
-
-	/**
-	Sets up filterset from $get and $post parameters
-	
-	*/
-	public function setup($get,$post) {
-		/* Use cases :
-			(1) $post not empty for formfilter fields :
-				* efilters are set from $post
-				* lfilters are set from $get
-				* keep filters div shown
-			(2) $post empty : 
-				* both efilters and lfilters are set from $get
-				* hide filter div
-		*/
-		$action = false;
-		$allowed_actions = array('clear_filters','add','del_','apply','reset');
-		// Fetch each $post parameter to see whether filters are concerned.
-		// Only 1 action at a time is allowed.
-		foreach ($post as $k => $v) {
-			if (strpos($k,$this->form_prefix)===0) {
-				$tmp = substr($k,strlen($this->form_prefix));
-				foreach ($allowed_actions as $a) {
-					if (strpos($tmp,$a)===0) {
-						$action = $tmp;
-						break;
-					}
-				}
-			}
-		}
-		if ($action !== false) {
-			// Use case (1)
-			if ($action != 'clear_filters' && $action != 'reset')  {
-				$this->initializeFromData($this->efilters,$this->eextra, $post);
-				if ($action == 'add'){
-					if (isset($post['add_filter']) 
-						&& isset($this->efilters[$post['add_filter']])) {
-					$this->efilters[$post['add_filter']]->add();
-					}
-				} elseif (strpos($action,'del_') === 0) {
-					$count = preg_match('#del_(.+)_([0-9]+)#',$action,$match);
-					if (($count == 1) && isset($this->efilters[$match[1]])) {
-						$this->efilters[$match[1]]->remove($match[2]);
-					}
-				} elseif ($action=="apply") {
-					$data = $this->saveFilters();
-					if ($this->eextra != null) {
-						$this->eextra->save();
-					}
-					$query = http_build_query($data,'','&');
-					if ($query != '') {
-						$query = (strpos($this->action,'?') === false ? '?' : '&').$query;
-					}
-					http::redirect($this->action.$query);
-					exit;
-				}
-			}
-			if (isset($post[$this->form_prefix."query"])) {
-				parse_str($post[$this->form_prefix."query"],$out);
-				$this->initializeFromData($this->lfilters,$this->lextra, $out);
-				if ($action == 'reset') {
-					$this->initializeFromData($this->efilters,$this->eextra, $out);
-				}
-			}
-			$this->hideform=false;
-		} else {
-			// Use case (2)
-			$load_from_settings = true;
-			foreach($get as $k=>$v) {
-				if (strpos($k,$this->form_prefix)===0) {
-					$load_from_settings=false;
-					break;
-				}
-			}
-			if ($load_from_settings) {
-				$get = new ArrayObject(array_merge($this->loadFilters(),$get));
-			}
-			$this->initializeFromData($this->efilters, $this->eextra, $get);
-			$this->initializeFromData($this->lfilters, $this->lextra, $get);
-
-		}
-	}
-	/**
-	Retrieves filterset generated form
-	
-	@param	method	<b>string</b>		form method to use (default: "get")
-	*/
-	public function getForm() {
-		$ret = '';
-		
-		if ($this->hideform) {
-			$formclass = ' class="hidden"';
-			$toggleclass = '';
-		} else {
-			$formclass='';
-			$toggleclass = ' class="opened"';
-		}
-		
-		$ret .= '<p>'.
-			'<a href="#" id="toggle-filters"'.$toggleclass.'>'.
-			__('Toggle filters and display options').
-			'</a></p>'.
-			'<div class="two-cols">'.
-			'<form id="filters" action="'.$this->action.'" method="post"'.$formclass.'>'.
-			'<div class="col70">'.
-			'<h3>'.__('Entries filters').'</h3>'.
-			'<table summary="'.__('Query filters').'" id="tfilters">'.
-			'<tbody>';
-		$count=0;
-		$form_combo=array();
-		$form_combo['-']='';
-		if (count($this->efilters)) {
-			foreach ($this->efilters as $filter) {
-				if ($filter->isEnabled()) {
-					$ret .= $filter->getFormLine();
-				}
-				$form_combo[$filter->name]=$filter->id;
-				$count++;
-			}
-		}
-		$ret .= '</tbody></table>'.
-			'<h3 class="margintop">'.__('Add a filter').'</h3>'.
-			'<p id="available_filters">'.
-			form::combo("add_filter",$form_combo).
-			'<input type="submit" value=" + " title="'.__('Add this filter').'" name="'.$this->form_prefix.'add" />'.
-			'</p>'.
-			'<p class="clear"><input class="delete" type="submit" value="'.__('Delete all filters').'" name="'.
-			$this->form_prefix.'clear_filters" />'.
-			'&nbsp;<input  type="submit" value="'.__('Reset').'" name="'.
-			$this->form_prefix.'reset" /></p>'.
-			'</div>';
-		if ($this->eextra != '') {
-			$ret .=
-				'<div class="col30">'.
-				$this->eextra->getFormContent().
-				'</div>';
-		}
-		$queryParams = $this->getFiltersAsParams($this->lfilters);
-		if ($this->lextra != null) {
-			$this->lextra->updateRequestParams($queryParams);
-		}
-		
-		$ret .=
-			'<p class="clear margintop">'.
-			'<input type="submit" value="'.__('Apply filters and display options').
-			'" name="'.$this->form_prefix.'apply" /></p>'.
-			form::hidden(array($this->form_prefix."query"),http_build_query($queryParams)).
-			$GLOBALS['core']->formNonce().
-			'</form>'.
-			'</div>';
-		return $ret;
-	}
-	/**
-	Retrieves the filters values as parameters
-	
-	@param	filters	<b>array</b>	list of concerned filters
-	
-	@return	<b>array</b>	the array of parameters
-
-	*/
-
-	protected function getFiltersAsParams($filters) {
-		$arr = new ArrayObject();
-		foreach ($filters as $f) {
-			if ($f->isEnabled())
-				$f->updateRequestParams($arr);
-		}
-		return $arr;
-	}
-	
-	public function getFiltersText() {
-		$ret = '<p>'.__('Currently applied filters :').'</p><ul>';
-		foreach ($this->lfilters as $f) {
-			if ($f->isEnabled())
-				$ret .= '<li>'.$f->getAsText().'</li>'."\n";
-		}
-		$ret .= '</ul>';
-		return $ret;
-	}
-	
-	/**
-	Displays required fieldset http header
-	To be called in page header, of course.
-	*/
-	public function header() {
-		$ret = dcPage::jsLoad('js/filters.js');
-		foreach($this->efilters as $f) {
-			$ret .= $f->header();
-		}
-		return $ret;
-	}
-	
-	
-	/**
-	Displays the fieldset
-	*/
-	public function display() {
-		echo $this->getForm();
-	}
-
-	/**
-	Applies fieldset and return resulting parameters for request
-	
-	@param	method	<b>string</b>		form method to use (default: "get")
-	@param	method	<b>string</b>		form method to use (default: "get")
-	
-	*/
-	public function applyFilters($params) {
-		foreach ($this->lfilters as $filter) {
-			if ($filter->isEnabled()) {
-				$filter->applyFilter($params);
-				$this->filtered = true;
-			}
-		}
-		if ($this->lextra != null) {
-			$this->lextra->applyFilters($params);
-		}
-		return $this->filtered;
-	}
-	
-	public function getDelName($field_id,$pos) {
-		return $this->form_prefix.'del_'.$field_id.'_'.$pos;
-	}
-}
-
-
-/**
-@ingroup DC_CORE
-@nosubgrouping
-@brief abstract filter class.
-
-Dotclear Filter handles administration filters for each list
-A filter fills in a parameter array, as defined in dcBlog class
-*/
-abstract class Filter {
-	public $filterset;			///<b>string</b> filterset parent
-	public $id;					///<b>string</b> field id (local to fieldset)
-	public $name;				///<b>string</b> filter name
-	public $desc;				///<b>string</b> field description
-	protected $request_param;	///<b>string</b> resulting parameter array key
-	protected $enabled;			///<b>string</b> true if filter is enabled
-	protected $values;			///<b>array</b> possible filter values
-	public $field_id;			///<b>string</b> field id (global to the page)
-	
-	/**
-	Inits Filter object
-	
-	@param	id		<b>string</b>	field id
-	@param	form_prefix	<b>string</b>		form prefix to use for parameters
-	*/
-	public function __construct ($id,$name,$desc,$request_param) {
-		$this->id = $id;
-		$this->name=$name;
-		$this->desc = $desc;
-		$this->request_param = $request_param;
-		$this->enabled=false;
-		$this->values = array();
-		$this->field_id = $this->id;
-	}
-	
-	/**
-	Defines the filterset containing this filter
-	
-	@param	prefix		<b>dcFilterset</b>	the filterset
-	*/
-	public function setFilterSet($fs) {
-		$this->filterset = $fs;
-	}
-	
-	/**
-	Defines form prefix for filter
-	
-	@param	prefix		<b>string</b>	the form prefix
-	*/
-	public function setFormPrefix($prefix) {
-		$this->field_id = $prefix.$this->id;
-	}
-	
-	/**
-	Get a field id
-	
-	@param	pos		<b>integer</b>	position of field, in case of multiple field (0 if only 1 field set, default value)
-	@return	<b>string</b> The field ID
-	*/
-	protected function getFieldId($pos=0) {
-		if ($pos == 0) {
-			return $this->field_id;
-		} else {
-			return $this->field_id.'_'.$pos;
-		}
-	}
-	
-	/**
-	Tells whether the filter is enabled or not
-	
-	@return	<b>boolean</b> true if enabled, false otherwise
-	*/
-	public function isEnabled() {
-		return $this->enabled;
-	}
-	
-	/**
-	Adds the current filter to the list
-	*/
-	public function add() {
-		// By default here, only 1 value allowed. Simply enable the filter
-		$this->enabled = true;
-	}
-	
-	/**
-	Removes a value from filter
-	*/
-	public function remove($pos) {
-		if (isset($this->values[$pos])) {
-			array_splice($this->values,$pos,1);
-			$this->enabled = (count($this->values)!=0);
-		}
-	}
-	
-	/**
-	Returns HTML code for form field
-	
-	@param	pos		<b>integer</b>	position of the field to display 
-									(in case of multiple values)
-	@return <b>string</b> the html code
-	*/
-	abstract protected function getFormFields($pos=0);
-	
-	/**
-	Extract values from data (data being an array, such as $_GET or $_POST)
-	
-	@param	$data	<b>array</b>	data to parse
-	@return	<b>array</b>	field values
-	
-	*/
-	protected function getValuesFromData($data) {
-		$count=0;
-		$arr = array();
-		while (isset($data[$this->getFieldId($count)])) {
-			$arr[$count] = $data[$this->getFieldId($count)];
-			$count++;
-		}
-		return $arr;
-	}
-	
-	public function initializeFromData($form_data) {
-		$this->values = $this->getValuesFromData($form_data);
-		$this->enabled = (count($this->values)!=0);
-	}
-	
-	/**
-	Returns HTML code for the hole filter lines
-	
-	@return <b>string</b> the html code
-	*/
-	
-	public function getFormLine() {
-		$ret='';
-		for ($cur=0; $cur < count($this->values); $cur++) {
-			$ret .= '<tr class="'.$this->id.'">';
-			$del_id = $this->filterset->getDelName($this->id,$cur);
-			$ret .= '<td><input id="'.$del_id.'" class="delete" '.
-					'type="submit" title="Delete the following filter : " value=" - " name="'.$del_id.'"/></td>'.
-					$this->getFormFields($cur);
-			$ret .= '</tr>';
-		}
-		return $ret;
-	}
-	
-	public function updateRequestParams($arr) {
-		for ($cur=0; $cur < count($this->values); $cur++) {
-			$arr[$this->getFieldId($cur)]=$this->values[$cur];
-		}
-	}
-	
-	/**
-	Convert filter values into a $param filter, used for the upcoming SQL request
-	
-	@param <b>ArrayObject</b> the parameters array to enrich
-	*/
-	public function applyFilter($params) {
-	}
-	
-	public function setValues($value) {
-		$this->values = $value;
-	}
-	
-	public function getValue() {
-		return $this->values;
-	}
-
-	public function header() {
-		return '';
-	}
-	
-	public abstract function getAsText();
-
-	
-}
-
-/**
-@ingroup DC_CORE
-@nosubgrouping
-@brief abstract filter class.
-
-Handle combo filter on admin side. Can be single or multi-valued
-*/
-class comboFilter extends Filter {
-	protected $options;
-	protected $default;
-	protected $no_value;
-	protected $verb;
-	protected $extra;
-	
-	public function __construct($id,$name,$desc,$request_param,$options,$extra=array()) {
-		parent::__construct($id,$name,$desc,$request_param);
-		$this->options = $options;
-		$this->extra = $extra;
-		$this->verb = "is";
-		$this->values=array();
-	}
-	
-	protected function getValuesFromData($data) {
-		$val = parent::getValuesFromData($data);
-		if (isset($data[$this->field_id.'_v'])) {
-			$verb = $data[$this->field_id.'_v'];
-		} else {
-			$verb = "is";
-		}
-		$arr = array(
-			'values' => $val,
-			'verb' => $verb
-		);
-		return $arr;
-	}
-	
-	public function add() {
-		parent::add();
-		if (isset($this->extra['singleval']) && (count($this->values) > 0))
-			return;
-		$this->values[]=current($this->options);
-	}
-	
-	public function getType() {
-		return "combo";
-	}
-
-	public function serialize() {
-		$data = parent::serialize();
-		$data['verb'] = $this->verb;
-		return $data;
-	}
-	
-	public function unserialize ($data) {
-		parent::unserialize($data);
-		$this->verb = $data['verb'];
-	}
-	
-	public function initializeFromData($form_data) {
-		$arr = $this->getValuesFromData($form_data);
-		$this->values = $arr['values'];
-		$this->verb = $arr['verb'];
-		$this->enabled = (count($this->values) != 0);
-	}
-
-	public function getFormFields($pos=0) {
-		if ($pos == 0) {
-			$ret = '<td id="'.$this->getFieldId($pos).'" title="'.$this->desc.'" class="filter-title">'.
-				''.$this->desc.' : </td>'.
-				'<td>'.
-				form::combo($this->field_id.'_v',
-					array(__('is')=>'is',__('is not')=>'isnot'),$this->verb,'','',
-					false,'title="'.sprintf(__('%s is or is not'),$this->desc).'"').
-				'</td>';
-		} else {
-			$ret = '<td id="'.$this->getFieldId($pos).'" title="or" colspan="2" class="or">'.
-				__('or').' : </td>';
-		};
-		$ret .= '<td>'.form::combo($this->getFieldId($pos),$this->options,$this->values[$pos],
-			'','',false,'title="'.__('Choose an option').'"').'</td>';
-		return $ret;
-	}
-	
-	public function updateRequestParams($arr) {
-		parent::updateRequestParams($arr);
-		
-		$arr[$this->field_id.'_v']=$this->verb;
-	}
-	
-	public function applyFilter($params) {
-		$attr = $this->request_param;
-		if ($this->verb != "is") {
-			$params[$attr."_not"] = true;
-		}
-		if (isset($this->extra['singleval']))
-			$params[$attr]=$this->values[0];
-		else
-			$params[$attr]=$this->values;
-}
-	
-	public function getValues() {
-		return array_merge($this->values,array($this->field_id.'_v',$this->verb));
-	}
-	
-	public function getAsText() {
-		$arr=array();
-		foreach ($this->values as $value) {
-			$arr[]=array_search($value,$this->options);
-		}
-		return sprintf("%s %s %s",$this->desc,$this->verb,join(',',$arr));
-	}
-}
-
-
-class categoryFilter extends comboFilter {
-	public function getAsText() {
-		$arr=array();
-		foreach ($this->values as $value) {
-			$cat=array_search($value,$this->options);
-			$arr[]=preg_replace("#^.* ([^ ]+) .*$#",'$1',$cat);
-		}
-		return sprintf("%s %s %s",$this->desc,$this->verb,join(',',$arr));
-	}
-}
-/**
-@ingroup DC_CORE
-@nosubgrouping
-@brief abstract filter class.
-
-Handle boolean filter on admin side.
-*/
-class booleanFilter extends Filter {
-	protected $options;
-	
-	public function __construct($id,$name,$desc,$request_param,$options) {
-		parent::__construct($id,$name,$desc,$request_param);
-		$this->options = $options;
-		$this->values=array();
-	}
-	
-	
-	public function getType() {
-		return "boolean";
-	}
-	public function add() {
-		parent::add();
-		$this->values=current($this->options);
-	}
-
-	public function getFormFields($pos=0) {
-		return '<td colspan="2">'.$this->desc.'</td><td>'.
-			form::combo($this->getFieldId($pos),$this->options,$this->values[$pos],
-				'','',false,'title="'.__('Choose an option').'"').'</td>';
-	}
-	
-	public function applyFilter($params) {
-		$params[$this->request_param]=$this->values[0];
-	}
-	
-	public function getAsText() {
-		return sprintf("%s %s",$this->desc,$this->values[0]);
-	}
-}
-
-
-class textFilter extends Filter {
-	protected $size;
-	protected $max;
-	
-	public function __construct($id,$name,$desc,$request_param,$size,$max) {
-		parent::__construct($id,$name,$desc,$request_param);
-		$this->size = $size;
-		$this->max = $max;
-		$this->values=array();
-	}
-	
-	
-	public function getType() {
-		return "text";
-	}
-	public function add() {
-		parent::add();
-		$this->values[]='';
-	}
-
-	public function getFormFields($pos=0) {
-		return '<td colspan="2">'.$this->desc.'</td><td>'.
-			form::field($this->getFieldId($pos),$this->size,$this->max,html::escapeHTML($this->values[0])).
-			'</td>';
-	}
-	
-	public function applyFilter($params) {
-		$params[$this->request_param]=$this->values[0];
-	}
-	
-	public function setValues($value) {
-		parent::setValues(array($value));
-	}
-
-	public function getAsText() {
-		return sprintf("%s %s",$this->desc,$this->values[0]);
-	}
-	
-}
-
-
-?>
Index: inc/admin/class.dc.list.php
===================================================================
--- inc/admin/class.dc.list.php	(revision 805)
+++ 	(revision )
@@ -1,1140 +1,0 @@
-<?php
-# -- BEGIN LICENSE BLOCK ---------------------------------------
-#
-# This file is part of Dotclear 2.
-#
-# Copyright (c) 2003-2011 Olivier Meunier & Association Dotclear
-# Licensed under the GPL version 2.0 license.
-# See LICENSE file or
-# http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
-#
-# -- END LICENSE BLOCK -----------------------------------------
-if (!defined('DC_RC_PATH')) { return; }
-
-/**
-@ingroup DC_CORE
-@nosubgrouping
-@brief Dotclear items pager class.
-
-Dotclear items pager handles pagination for every admin list.
-*/
-class adminItemsPager extends pager
-{
-	public function getLinks()
-	{
-		$htmlText = '';
-		$htmlStart = '';
-		$htmlEnd = '';
-		$htmlPrev = '';
-		$htmlNext = '';
-		$htmlDirectAccess = '';
-		$htmlHidden = '';
-		
-		$this->setURL();
-		
-		# Page text
-		$htmlText = '<span>'.sprintf(__('Page %s over %s'),$this->env,$this->nb_pages).'</span>';
-		
-		# Previous page
-		if($this->env != 1) {
-			$htmlPrev = '<a href="'.sprintf($this->page_url,$this->env-1).'" class="prev">'.
-			$htmlPrev .= $this->html_prev.'</a>';
-		}
-		
-		# Next page
-		if($this->env != $this->nb_pages) {
-			$htmlNext = '<a href="'.sprintf($this->page_url,$this->env+1).'" class="next">';
-			$htmlNext .= $this->html_next.'</a>';
-		}
-		
-		# Start
-		if($this->env != 1) {
-			$htmlStart = '<a href="'.sprintf($this->page_url,1).'" class="start">'.
-			$htmlStart .= $this->html_start.'</a>';
-		}
-		
-		# End
-		if($this->env != $this->nb_pages) {
-			$htmlEnd = '<a href="'.sprintf($this->page_url,$this->nb_pages).'" class="end">'.
-			$htmlEnd .= $this->html_end.'</a>';
-		}
-		
-		# Direct acces
-		$htmlDirectAccess = 
-			'<span>'.__('Direct access to page').'&nbsp;'.
-			form::field(array('page'),3,3,$this->env).'&nbsp;'.
-			'<input type="submit" value="'.__('ok').'" />'.
-			'</span>';
-			
-		# Hidden fields
-		foreach ($_GET as $k => $v) {
-			if ($k != $this->var_page) {
-				$htmlHidden .= form::hidden(array($k),$v);
-			}
-		}
-		
-		$res =
-			'<form method="get" action="'.$this->base_url.'"><p>'.
-			$htmlStart.
-			$htmlPrev.
-			$htmlText.
-			$htmlNext.
-			$htmlEnd.
-			$htmlDirectAccess.
-			$htmlHidden.
-			'</p></form>';
-		
-		return $this->nb_elements > 0 ? $res : '';
-	}
-}
-
-/**
-@ingroup DC_CORE
-@nosubgrouping
-@brief Dotclear items column class.
-
-Dotclear items column handles each column object use in adminItemsList class.
-*/
-class adminItemsColumn
-{
-	protected $core;		/// <b>object</b> dcCore object
-	protected $id;			/// <b>string</b> ID of defined column
-	protected $alias;		/// <b>string</b> ID of defined column
-	protected $title;		/// <b>string</b> Title of defined column
-	protected $callback;	/// <b>array</b> Callback calls to display defined column
-	protected $html;		/// <b>array</b> Extra HTML for defined column
-	
-	/**
-	Inits Generic column object
-	
-	@param	id		<b>string</b>		Column id
-	@param	alias	<b>string</b>		Column alias for SQL
-	@param	title	<b>string</b>		Column title (for table headers)
-	@param	callback	<b>array</b>		Column callback (used for display)
-	@param	html		<b>array</b>		Extra html (used for table headers)
-	@param	sortable	<b>boolean</b>		Defines if the column can be sorted or not
-	@param	listable	<b>boolean</b>		Defines if the column can be listed or not
-	@param	can_hide	<b>boolean</b>		Defines if the column can be hidden or not
-	*/
-	public function __construct($id,$alias,$title,$callback,$html = null,$sortable = true,$listable = true,$can_hide = true)
-	{
-		if (!is_string($id) || $id === '') {
-			throw new Exception(__('Invalid column ID'));
-		}
-		
-		if (!is_string($title)) {
-			throw new Exception(__('Invalid column title'));
-		}
-		
-		if (is_null($html) || !is_array($html)) {
-			$html = array();
-		}
-		
-		if (!is_bool($sortable)) {
-			$sortable = true;
-		}
-		
-		if (!is_bool($listable)) {
-			$listable = true;
-		}
-		
-		if (!is_bool($can_hide)) {
-			$can_hide = true;
-		}
-		
-		if (!is_string($alias) && $sortable) {
-			throw new Exception(__('Invalid column alias'));
-		}
-		
-		try {
-			if (!is_array($callback) || count($callback) < 2) {
-				throw new Exception(__('Callback parameter should be an array'));
-			}
-			$r = new ReflectionClass($callback[0]);
-			$f = $r->getMethod($callback[1]);
-			$p = $r->getParentClass();
-			$find_parent = false;
-			
-			while (!$p) {
-				if ($p->name == 'adminItemsList') {
-					$find_parent = true;
-				}
-				else {
-					$p->getParentClass();
-				}
-			}
-			
-			if (!$p || !$f) {
-				throw new Exception(__('Callback class should be inherited of adminItemsList class'));
-			}
-		}
-		catch (Exception $e) {
-			throw new Exception(sprintf(__('Invalid column callback: %s'),$e->getMessage()));
-		}
-		
-		$this->id = $id;
-		$this->alias = $alias;
-		$this->title = $title;
-		$this->callback = $callback;
-		$this->html = $html;
-		$this->sortable = $sortable;
-		$this->listable = $listable;
-		$this->can_hide = $can_hide;
-		$this->visibility = true;
-	}
-	
-	/**
-	Gets information of defined column
-	
-	@param	info		<b>string</b>		Column info to retrive
-	
-	@return	<b>mixed</b>	The information requested, null otherwise
-	*/
-	public function getInfo($info)
-	{
-		return property_exists(get_class($this),$info) ? $this->{$info} : null;
-	}
-	
-	/**
-	Sets visibility of defined column
-	
-	@param	visibility	<b>boolean</b>		Column visibility
-	*/
-	public function setVisibility($visibility)
-	{
-		if (is_bool($visibility) && $this->can_hide) {
-			$this->visibility = $visibility;
-		}
-		else {
-			$this->visibility = true;
-		}
-	}
-	
-	/**
-	Returns visibility status of defined column
-	
-	@return	<b>boolean</b>		true if column is visible, false otherwise
-	*/
-	public function isVisible()
-	{
-		return $this->visibility;
-	}
-	
-	/**
-	Returns if the defined column can be sorted
-	
-	@return	<b>boolean</b>		true if column is sortable, false otherwise
-	*/
-	public function isSortable()
-	{
-		return $this->sortable;
-	}
-	
-	/**
-	Returns if the defined column can be listed
-	
-	@return	<b>boolean</b>		true if column is listable, false otherwise
-	*/
-	public function isListable()
-	{
-		return $this->listable;
-	}
-	
-	/**
-	Returns if the defined column can be hidden
-	
-	@return	<b>boolean</b>	true if column can be hidden, false otherwise
-	*/
-	public function canHide()
-	{
-		return $this->can_hide;
-	}
-}
-
-/**
-@ingroup DC_CORE
-@nosubgrouping
-@brief abstract items list class.
-
-Dotclear items list handles administration lists
-*/
-abstract class adminItemsList implements dcFilterExtraInterface
-{
-	protected $core;
-	protected $rs;
-	protected $rs_count;
-	protected $columns;
-	protected $sortby;
-	protected $order;
-	protected $nb_per_page;
-	protected $page;
-	protected $default_sortby;
-	protected $default_order;
-	
-	/*
-	Sets columns of defined list
-	*/
-	abstract function setColumns();
-	
-	/**
-	Inits List object
-	
-	@param	core		<b>dcCore</b>		dcCore object
-	*/
-	public function __construct($core)
-	{
-		
-		$this->core = $core;
-		$this->context = get_class($this);
-		$this->columns = new ArrayObject();
-		$this->form_prefix = 'col_';
-		
-		$this->html_prev = __('prev');
-		$this->html_next = __('next');
-		$this->html_start = __('start');
-		$this->html_end = __('end');
-		
-		$this->nb_per_page = 10;
-		
-		$this->setColumns();
-		
-		# --BEHAVIOR-- adminItemsListConstruct
-		$core->callBehavior('adminItemsListConstruct',$this);
-		
-	}
-	
-	public function __clone() {
-		$arr = new ArrayObject();
-		foreach ($this->columns as $k=>$v) {
-			$arr[$k]= clone $v;
-		}
-		$this->columns = $arr;
-	}
-	/**
-	Apply limit, sortby and order filters to items parameters
-	
-	@param	params	<b>array</b>	Items parameters
-	
-	@return	<b>array</b>		Items parameters
-	*/
-	public function applyFilters($params)
-	{
-		if (!is_null($this->sortby) && !is_null($this->order)) {
-			if (
-				isset($this->columns[$this->sortby]) &&
-				in_array($this->order,array('asc','desc'))
-			) {
-				$params['order'] = $this->columns[$this->sortby]->getInfo('alias').' '.$this->order;
-			}
-		}
-		
-		$params['limit'] = array((($this->page-1)*$this->nb_per_page),$this->nb_per_page);
-		
-		return $params;
-	}
-	
-	/**
-	Sets items and items counter
-	
-	@param	rs		<b>recordSet</b>	Items recordSet to display
-	@param	rs_count	<b>int</b>		Total items number
-	*/
-	public function setItems($rs,$rs_count)
-	{
-		$this->rs = $rs;
-		$this->rs_count = $rs_count;
-	}
-	
-	/**
-	Returns HTML code form used to choose which column to display
-	
-	@return	<b>string</b>		HTML code form
-	*/
-	public function getFormContent()
-	{
-		$block = 
-		'<h3>'.__('Displayed information').'</h3>'.
-		'<ul>%s</ul>';
-		
-		$list = array();
-		$sortby_combo = array();
-		foreach ($this->columns as $k => $v) {
-			$col_id = $this->form_prefix.$k;
-			$col_label = sprintf('<label for="%s">%s</label>',$col_id,$v->getInfo('title'));
-			$col_html = sprintf('<li class="line">%s</li>',$col_label.form::checkbox($col_id,1,$v->isVisible(),null,null,!$v->canHide()));
-			
-			if ($v->isListable()) {
-				array_push($list,$col_html);
-			}
-			$sortby_combo[$v->getInfo('title')] = $k;
-		}
-		$order_combo = array(
-				__('Descending') => 'desc',
-				__('Ascending') => 'asc'
-		);
-		
-		$nb_per_page = !is_null($this->nb_per_page) ? $this->nb_per_page : 10;
-		
-		return
-		sprintf($block,implode('',$list)).
-		'<p><label for="nb_per_page">'.__('Items per page:').
-		'</label>&nbsp;'.form::field('nb_per_page',3,3,$nb_per_page).
-		'</p>'.
-		'<p><label for="sortby">'.__('Sort by:').
-		'</label>&nbsp;'.form::combo('sortby',$sortby_combo,$this->sortby).
-		'</p>'.
-		'<p><label for="order">'.__('Order:').
-		'</label>&nbsp;'.form::combo('order',$order_combo,$this->order).
-		'</p>';
-	}
-	
-	public function updateRequestParams($params) {
-		if (!is_null($this->sortby) && isset($this->columns[$this->sortby])) {
-			$params['sortby'] = $this->columns[$this->sortby]->getInfo('alias');
-		}
-		if (!is_null($this->order)) {
-			$params['order'] = $this->order;
-		}
-		if (!is_null($this->nb_per_page)) {
-			$params['nb_per_page'] = $this->nb_per_page;
-		}
-		if (!is_null($this->page)) {
-			$params['page'] = $this->page;
-		}
-		foreach ($this->columns as $k => $v) {
-			if($v->isVisible())
-				$params[$this->form_prefix.$k] = 1;
-		}
-
-	}
-	
-	/**
-	Returns HTML hidden fields for list options
-	
-	@return	<b>string</b>		HTML hidden fields
-	*/
-	public function getFormFieldsAsHidden()
-	{
-		$res = '';
-		
-		if (!is_null($this->sortby)) {
-			$res .= form::hidden(array('sortby'),$this->sortby);
-		}
-		if (!is_null($this->order)) {
-			$res .= form::hidden(array('order'),$this->order);
-		}
-		if (!is_null($this->nb_per_page)) {
-			$res .= form::hidden(array('nb_per_page'),$this->nb_per_page);
-		}
-		if (!is_null($this->page)) {
-			$res .= form::hidden(array('page'),$this->page);
-		}
-		
-		return $res;
-	}
-	
-	/**
-	Returns HTML code list to display
-	
-	@param	enclose_block	<b>string</b>		HTML wrapper of defined list
-	
-	@return	<b>string</b>		HTML code list
-	*/
-	public function display($enclose_block = '')
-	{
-		if ($this->rs->isEmpty())
-		{
-			echo '<p><strong>'.__('No item').'</strong></p>';
-		}
-		else
-		{
-			$pager = new adminItemsPager($this->page,$this->rs_count,$this->nb_per_page,10);
-			$pager->html_prev = $this->html_prev;
-			$pager->html_next = $this->html_next;
-			$pager->html_start = $this->html_start;
-			$pager->html_end = $this->html_end;
-			$pager->var_page = 'page';
-			
-			$html_block =
-			'<table class="maximal clear">'.
-			$this->getCaption($this->page).
-			'<thead><tr>';
-			
-			foreach ($this->columns as $k => $v) {
-				if ($v->isVisible()) {
-					$title = $v->getInfo('title');
-					if ($v->isSortable()) {
-						$title = sprintf('<a href="%2$s">%1$s</a>',$title,$this->getSortLink($v));
-					}
-					$html_extra = '';
-					foreach ($v->getInfo('html') as $i => $j) {
-						$html_extra = $i.'="'.implode(' ',$j).'"';
-					}
-					$html_extra = !empty($html_extra) ? ' '.$html_extra : '';
-					$html_block .= sprintf('<th scope="col"%2$s>%1$s</th>',$title,$html_extra);
-				}
-			}
-			
-			$html_block .=
-			'</tr></thead>'.
-			'<tbody>%s</tbody>'.
-			'</table>';
-			
-			if ($enclose_block) {
-				$html_block = sprintf($enclose_block,$html_block);
-			}
-			
-			echo '<div class="pagination">'.$pager->getLinks().'</div>';
-			
-			$blocks = explode('%s',$html_block);
-			
-			echo $blocks[0];
-			
-			while ($this->rs->fetch())
-			{
-				echo $this->displayLine();
-			}
-			
-			echo $blocks[1];
-			
-			echo '<div class="pagination">'.$pager->getLinks().'</div>';
-		}
-	}
-	
-	/**
-	Adds column to defined list
-	
-	@param	id		<b>string</b>		Column id
-	@param	alias	<b>string</b>		Column alias for SQL
-	@param	title	<b>string</b>		Column title (for table headers)
-	@param	callback	<b>array</b>		Column callback (used for display)
-	@param	html		<b>string</b>		Extra html (used for table headers)
-	@param	sortable	<b>boolean</b>		Defines if the column can be sorted or not
-	@param	listable	<b>boolean</b>		Defines if the column can be listed or not
-	@param	can_hide	<b>boolean</b>		Defines if the column can be hidden or not
-	*/
-	protected function addColumn($id,$alias,$title,$callback,$html = null,$sortable = true,$listable = true,$can_hide = true)
-	{
-		try {
-			if (is_null($html) || !is_array($html)) {
-				$html = array();
-			}
-			if ($this->sortby === $alias && !is_null($this->order)) {
-				if (array_key_exists('class',$html)) {
-					array_push($html['class'],$this->order);
-				}
-				else {
-					$html['class'] = array($this->order);
-				}
-			}
-			$c = new adminItemsColumn($id,$alias,$title,$callback,$html,$sortable,$listable,$can_hide);
-			$this->columns[$id] = $c;
-		}
-		catch (Exception $e) {
-			if (DC_DEBUG) {
-				$this->core->error->add(sprintf('[%s] %s',$id,$e->getMessage()));
-			}
-		}
-	}
-	
-	/**
-	Returns default caption text
-	
-	@return	<b>string</b>		Default caption
-	*/
-	protected function getDefaultCaption()
-	{
-		return;
-	}
-	
-	/**
-	Returns default HTML code line
-	
-	@return	<b>string</b>		Default HTMl code line
-	*/
-	protected function getDefaultLine()
-	{
-		return '<tr class="line">%s</tr>';
-	}
-	
-	/**
-	Loads list from user settings
-	*/
-	public function load() {
-		$ws = $this->core->auth->user_prefs->addWorkspace('lists');
-		$user_pref = !is_null($ws->{$this->context.'_opts'}) ? unserialize($ws->{$this->context.'_opts'}) : array();
-		$this->sortby = array_key_exists('sortby',$user_pref) ? $user_pref['sortby'] : $this->default_sortby;
-		$this->order = array_key_exists('order',$user_pref) ? $user_pref['order'] : $this->default_order;
-		$this->nb_per_page = array_key_exists('nb_per_page',$user_pref) ? $user_pref['nb_per_page'] : 10;
-		$user_pref = !is_null($ws->{$this->context.'_col'}) ? unserialize($ws->{$this->context.'_col'}) : array();
-		foreach ($this->columns as $k => $v) {
-			$visibility =  array_key_exists($k,$user_pref) ? $user_pref[$k] : true;
-			$v->setVisibility($visibility);
-		}
-		if ($this->sortby != null && !isset($this->columns[$this->sortby])) {
-			// No alias found
-			$this->sortby=$this->default_sortby;
-			$this->order=$this->default_order;
-		}
-
-	}
-	
-	/**
-	Saves list to user settings
-	*/
-	public function save() {
-		$ws = $this->core->auth->user_prefs->addWorkspace('lists');
-		$user_pref = !is_null($ws->{$this->context.'_opts'}) ? unserialize($ws->{$this->context.'_opts'}) : array();
-		$user_pref['order'] = $this->order;
-		$user_pref['nb_per_page'] = $this->nb_per_page;
-		$user_pref['sortby'] = $this->sortby;
-		
-		$this->core->auth->user_prefs->lists->put($this->context.'_opts',serialize($user_pref),'string');
-		
-		$user_pref = !is_null($ws->{$this->context.'_col'}) ? unserialize($ws->{$this->context.'_col'}) : array();
-		
-		foreach ($this->columns as $k => $v) {
-			$user_pref[$k] = $v->isVisible();
-		}
-		$this->core->auth->user_prefs->lists->put($this->context.'_col',serialize($user_pref),'string');
-	}
-	
-	/**
-	Set dedicated data from form submission 
-	(either $_GET or $_POST depending on the context
-	
-	@param	data		<b>array</b>		Data to retrieve information from
-	*/
-	public function initializeFromData ($data)
-	{
-		$load_from_settings = true;
-		foreach ($data as $k=>$v) {
-			if (strpos($k,$this->form_prefix) === 0) {
-				$load_from_settings = false;
-			}
-		}
-		if ($load_from_settings) {
-			$this->load();
-		}
-		# Sortby
-		$this->sortby = array_key_exists('sortby',$data) ? $data['sortby'] : $this->sortby;
-		$this->order = array_key_exists('order',$data) ? $data['order'] : $this->order;
-		$this->nb_per_page = array_key_exists('nb_per_page',$data) ? (integer) $data['nb_per_page'] : $this->nb_per_page;
-		# Page
-		$this->page = array_key_exists('page',$data) ? (integer) $data['page'] : 1;
-		if ($load_from_settings)
-			return;
-		foreach ($this->columns as $k => $v) {
-			$key = $this->form_prefix.$k;
-			$visibility = !array_key_exists($key,$data) ? false : true;
-			$v->setVisibility($visibility);
-		}
-	}
-	
-	/**
-	Returns HTML code for each line of defined list
-	
-	@return	<b>string</b>		HTML code line
-	*/
-	private function displayLine()
-	{
-		$res = '';
-		
-		foreach ($this->columns as $k => $v) {
-			if ($v->isVisible()) {
-				$c = $v->getInfo('callback');
-				$res .= $this->{$c[1]}();
-			}
-		}
-		
-		return sprintf($this->getDefaultLine(),$res);
-	}
-	
-	/**
-	Returns caption of defined list
-	
-	@param	page			<b>string|int</b>	Current page
-	
-	@return	<b>string</b>		HTML caption tag
-	*/
-	private function getCaption($page)
-	{
-		$caption = $this->getDefaultCaption();
-		
-		if (!empty($caption)) {
-			$caption = sprintf(
-				'<caption>%s - %s</caption>',
-				$caption,sprintf(__('Page %s'),$page)
-			);
-		}
-		
-		return $caption;
-	}
-	
-	/**
-	Returns link to sort the defined column
-	
-	@param	c		<b>adminGenericColumn</b>	Current column
-	
-	@return	<b>string</b>		HTML link
-	*/
-	private function getSortLink($c)
-	{
-		$order = 'desc';
-		if (!is_null($this->sortby) && $this->sortby === $c->getInfo('id')) {
-			if (!is_null($this->order) && $this->order === $order) {
-				$order = 'asc';
-			}
-		}
-		
-		$args = $_GET;
-		$args['sortby'] = $c->getInfo('id');
-		$args['order'] = $order;
-		
-		array_walk($args,create_function('&$v,$k','$v=$k."=".$v;'));
-		
-		$url = $_SERVER['PHP_SELF'];
-		$url .= '?'.implode('&amp;',$args);
-		
-		return $url;
-	}
-}
-
-/**
-@ingroup DC_CORE
-@nosubgrouping
-@brief abstract posts list class.
-
-Handle posts list on admin side
-*/
-class adminPostList extends adminItemsList
-{
-	public function setColumns()
-	{
-		$this->addColumn('title','post_title',__('Title'),array('adminPostList','getTitle'),array('class' => array('maximal')),true,true,false);
-		$this->addColumn('date','post_dt',__('Date'),array('adminPostList','getDate'));
-		$this->addColumn('datetime','post_dt',__('Date and time'),array('adminPostList','getDateTime'));
-		$this->addColumn('category','cat_title',__('Category'),array('adminPostList','getCategory'));
-		$this->addColumn('author','user_id',__('Author'),array('adminPostList','getAuthor'));
-		$this->addColumn('comments','nb_comment',__('Comments'),array('adminPostList','getComments'));
-		$this->addColumn('trackbacks','nb_trackback',__('Trackbacks'),array('adminPostList','getTrackbacks'));
-		$this->addColumn('status','post_status',__('Status'),array('adminPostList','getStatus'));
-		$this->default_sortby = 'datetime';
-		$this->default_order = 'desc';
-
-	}
-	
-	protected function getDefaultCaption()
-	{
-		return __('Entries list');
-	}
-	
-	protected function getDefaultLine()
-	{
-		return
-		'<tr class="line'.($this->rs->post_status != 1 ? ' offline' : '').'"'.
-		' id="p'.$this->rs->post_id.'">%s</tr>';
-	}
-	
-	protected function getTitle()
-	{
-		return
-		'<th scope="row" class="maximal">'.
-		form::checkbox(array('entries[]'),$this->rs->post_id,'','','',!$this->rs->isEditable()).'&nbsp;'.
-		'<a href="'.$this->core->getPostAdminURL($this->rs->post_type,$this->rs->post_id).'">'.
-		html::escapeHTML($this->rs->post_title).'</a></th>';
-	}
-	
-	protected function getDate()
-	{
-		return '<td class="nowrap">'.dt::dt2str(__('%Y-%m-%d'),$this->rs->post_dt).'</td>';
-	}
-	
-	protected function getDateTime()
-	{
-		return '<td class="nowrap">'.dt::dt2str(__('%Y-%m-%d %H:%M'),$this->rs->post_dt).'</td>';
-	}
-	
-	protected function getCategory()
-	{
-		if ($this->core->auth->check('categories',$this->core->blog->id)) {
-			$cat_link = '<a href="category.php?id=%s">%s</a>';
-		} else {
-			$cat_link = '%2$s';
-		}
-		
-		if ($this->rs->cat_title) {
-			$cat_title = sprintf($cat_link,$this->rs->cat_id,
-			html::escapeHTML($this->rs->cat_title));
-		} else {
-			$cat_title = __('None');
-		}
-		
-		return '<td class="nowrap">'.$cat_title.'</td>';
-	}
-	
-	protected function getAuthor()
-	{
-		return '<td class="nowrap">'.$this->rs->user_id.'</td>';
-	}
-	
-	protected function getComments()
-	{
-		return '<td class="nowrap">'.$this->rs->nb_comment.'</td>';
-	}
-	
-	protected function getTrackbacks()
-	{
-		return '<td class="nowrap">'.$this->rs->nb_trackback.'</td>';
-	}
-	
-	protected function getStatus()
-	{
-		$img = '<img alt="%1$s" title="%1$s" src="images/%2$s" />';
-		switch ($this->rs->post_status) {
-			case 1:
-				$img_status = sprintf($img,__('published'),'check-on.png');
-				break;
-			case 0:
-				$img_status = sprintf($img,__('unpublished'),'check-off.png');
-				break;
-			case -1:
-				$img_status = sprintf($img,__('scheduled'),'scheduled.png');
-				break;
-			case -2:
-				$img_status = sprintf($img,__('pending'),'check-wrn.png');
-				break;
-		}
-		
-		$protected = '';
-		if ($this->rs->post_password) {
-			$protected = sprintf($img,__('protected'),'locker.png');
-		}
-		
-		$selected = '';
-		if ($this->rs->post_selected) {
-			$selected = sprintf($img,__('selected'),'selected.png');
-		}
-		
-		$attach = '';
-		$nb_media = $this->rs->countMedia();
-		if ($nb_media > 0) {
-			$attach_str = $nb_media == 1 ? __('%d attachment') : __('%d attachments');
-			$attach = sprintf($img,sprintf($attach_str,$nb_media),'attach.png');
-		}
-		
-		return '<td class="nowrap status">'.$img_status.' '.$selected.' '.$protected.' '.$attach.'</td>';
-	}
-}
-
-/**
-@ingroup DC_CORE
-@nosubgrouping
-@brief abstract mini posts list class.
-
-Handle mini posts list on admin side (used for link popup)
-*/
-class adminPostMiniList extends adminPostList
-{
-	public function setColumns()
-	{
-		$this->addColumn('title','post_title',__('Title'),array('adminPostMiniList','getTitle'),array('class' => array('maximal')),true,true,false);
-		$this->addColumn('date','post_dt',__('Date'),array('adminPostList','getDate'));
-		$this->addColumn('author','user_id',__('Author'),array('adminPostList','getAuthor'));
-		$this->addColumn('status','post_status',__('Status'),array('adminPostList','getStatus'));
-	}
-	
-	protected function getTitle() 
-	{
-		return
-		'<th scope="row" class="maximal">'.
-		form::checkbox(array('entries[]'),$this->rs->post_id,'','','',!$this->rs->isEditable()).'&nbsp;'.
-		'<a href="'.$this->core->getPostAdminURL($this->rs->post_type,$this->rs->post_id).'" '.
-		'title="'.html::escapeHTML($this->rs->getURL()).'">'.
-		html::escapeHTML($this->rs->post_title).'</a></td>';
-	}
-}
-
-/**
-@ingroup DC_CORE
-@nosubgrouping
-@brief abstract comments list class.
-
-Handle comments list on admin side
-*/
-class adminCommentList extends adminItemsList
-{
-	public function setColumns()
-	{
-		$this->addColumn('title','post_title',__('Title'),array('adminCommentList','getTitle'),array('class' => array('maximal')),true,true,false);
-		$this->addColumn('date','comment_dt',__('Date'),array('adminCommentList','getDate'));
-		$this->addColumn('author','comment_author',__('Author'),array('adminCommentList','getAuthor'));
-		$this->addColumn('type','comment_trackback',__('Type'),array('adminCommentList','getType'));
-		$this->addColumn('status','comment_status',__('Status'),array('adminCommentList','getStatus'));
-		$this->addColumn('edit',null,'',array('adminCommentList','getEdit'),null,false,false,false);
-		$this->default_sortby = 'date';
-		$this->default_order = 'desc';
-	}
-	
-	protected function getDefaultCaption()
-	{
-		return __('Comments list');
-	}
-	
-	protected function getDefaultLine()
-	{
-		return
-		'<tr class="line'.($this->rs->comment_status != 1 ? ' offline' : '').'"'.
-		' id="c'.$this->rs->comment_id.'">%s</tr>';
-	}
-	
-	protected function getTitle()
-	{
-		$post_url = $this->core->getPostAdminURL($this->rs->post_type,$this->rs->post_id);
-		
-		return
-		'<th scope="row" class="maximal">'.
-		form::checkbox(array('comments[]'),$this->rs->comment_id,'','','',0).'&nbsp;'.
-		'<a href="'.$post_url.'">'.
-		html::escapeHTML($this->rs->post_title).'</a>'.
-		($this->rs->post_type != 'post' ? ' ('.html::escapeHTML($this->rs->post_type).')' : '').'</th>';
-	}
-	
-	protected function getDate()
-	{
-		return '<td class="nowrap">'.dt::dt2str(__('%Y-%m-%d %H:%M'),$this->rs->comment_dt).'</td>';
-	}
-	
-	protected function getAuthor()
-	{
-		global $author, $status, $sortby, $order, $nb_per_page;
-		
-		$author_url =
-		'comments.php?n='.$nb_per_page.
-		'&amp;status='.$status.
-		'&amp;sortby='.$sortby.
-		'&amp;order='.$order.
-		'&amp;author='.rawurlencode($this->rs->comment_author);
-		
-		$comment_author = html::escapeHTML($this->rs->comment_author);
-		if (mb_strlen($comment_author) > 20) {
-			$comment_author = mb_strcut($comment_author,0,17).'...';
-		}
-		
-		return '<td class="nowrap"><a href="'.$author_url.'">'.$comment_author.'</a></td>';
-	}
-	
-	protected function getType()
-	{
-		return '<td class="nowrap">'.($this->rs->comment_trackback ? __('trackback') : __('comment')).'</td>';
-	}
-	
-	protected function getStatus()
-	{
-		$img = '<img alt="%1$s" title="%1$s" src="images/%2$s" />';
-		switch ($this->rs->comment_status) {
-			case 1:
-				$img_status = sprintf($img,__('published'),'check-on.png');
-				break;
-			case 0:
-				$img_status = sprintf($img,__('unpublished'),'check-off.png');
-				break;
-			case -1:
-				$img_status = sprintf($img,__('pending'),'check-wrn.png');
-				break;
-			case -2:
-				$img_status = sprintf($img,__('junk'),'junk.png');
-				break;
-		}
-		
-		return '<td class="nowrap status">'.$img_status.'</td>';
-	}
-	
-	protected function getEdit()
-	{
-		$comment_url = 'comment.php?id='.$this->rs->comment_id;
-		
-		return
-		'<td class="nowrap status"><a href="'.$comment_url.'">'.
-		'<img src="images/edit-mini.png" alt="" title="'.__('Edit this comment').'" /></a></td>';
-	}
-}
-
-/**
-@ingroup DC_CORE
-@nosubgrouping
-@brief abstract users list class.
-
-Handle users list on admin side
-*/
-class adminUserList extends adminItemsList
-{
-	public function setColumns()
-	{
-		$this->addColumn('username','U.user_id',__('Username'),array('adminUserList','getUserName'),array('class' => array('maximal')),true,true,false);
-		$this->addColumn('firstname','user_firstname',__('First name'),array('adminUserList','getFirstName'),array('class' => array('nowrap')));
-		$this->addColumn('lastname','user_name',__('Last name'),array('adminUserList','getLastName'),array('class' => array('nowrap')));
-		$this->addColumn('displayname','user_displayname',__('Display name'),array('adminUserList','getDisplayName'),array('class' => array('nowrap')));
-		$this->addColumn('entries','nb_post',__('Entries'),array('adminUserList','getEntries'),array('class' => array('nowrap')));
-		$this->default_sortby='lastname';
-		$this->default_order='asc';
-	}
-	
-	protected function getDefaultCaption()
-	{
-		return __('Users list');
-	}
-	
-	protected function getUserName()
-	{
-		$img = '<img alt="%1$s" title="%1$s" src="images/%2$s" />';
-		$img_status = '';
-		
-		$p = $this->core->getUserPermissions($this->rs->user_id);
-		
-		if (isset($p[$this->core->blog->id]['p']['admin'])) {
-			$img_status = sprintf($img,__('admin'),'admin.png');
-		}
-		if ($this->rs->user_super) {
-			$img_status = sprintf($img,__('superadmin'),'superadmin.png');
-		}
-		
-		return
-		'<th scope="row" class="maximal">'.form::hidden(array('nb_post[]'),(integer) $this->rs->nb_post).
-		form::checkbox(array('user_id[]'),$this->rs->user_id).'&nbsp;'.
-		'<a href="user.php?id='.$this->rs->user_id.'">'.
-		$this->rs->user_id.'</a>&nbsp;'.$img_status.'</th>';
-	}
-	
-	protected function getFirstName()
-	{
-		return '<td class="nowrap">'.$this->rs->user_firstname.'</td>';
-	}
-	
-	protected function getLastName()
-	{
-		return '<td class="nowrap">'.$this->rs->user_name.'</td>';
-	}
-	
-	protected function getDisplayName()
-	{
-		return '<td class="nowrap">'.$this->rs->user_displayname.'</td>';
-	}
-	
-	protected function getEntries()
-	{
-		return
-		'<td class="nowrap"><a href="posts.php?user_id='.$this->rs->user_id.'">'.
-		$this->rs->nb_post.'</a></td>';
-	}
-}
-
-/**
-@ingroup DC_CORE
-@nosubgrouping
-@brief abstract blogs list class.
-
-Handle blogs list on admin side
-*/
-class adminBlogList extends adminItemsList
-{
-	public function setColumns()
-	{
-		$this->addColumn('blogname','UPPER(blog_name)',__('Blog name'),array('adminBlogList','getBlogName'),array('class' => array('maximal')),true,true,false);
-		$this->addColumn('lastupdate','blog_upddt',__('Last update'),array('adminBlogList','getLastUpdate'),array('class' => array('nowrap')));
-		$this->addColumn('entries',null,__('Entries'),array('adminBlogList','getEntries'),array('class' => array('nowrap')),false);
-		$this->addColumn('blogid','B.blog_id',__('Blog ID'),array('adminBlogList','getBlogId'),array('class' => array('nowrap')));
-		$this->addColumn('action',null,'',array('adminBlogList','getAction'),array('class' => array('nowrap')),false);
-		$this->addColumn('status','blog_status',__('status'),array('adminBlogList','getStatus'),array('class' => array('nowrap')));
-		$this->default_sortby='lastupdate';
-		$this->default_order='desc';
-	}
-	
-	protected function getDefaultCaption()
-	{
-		return __('Blogs list');
-	}
-	
-	protected function getBlogName()
-	{
-		return
-		'<th scope="row" class="maximal"><a href="index.php?switchblog='.$this->rs->blog_id.'" '.
-		'title="'.sprintf(__('Switch to blog %s'),$this->rs->blog_id).'">'.
-		html::escapeHTML($this->rs->blog_name).'</a></th>';
-	}
-	
-	protected function getLastUpdate()
-	{
-		$offset = dt::getTimeOffset($this->core->auth->getInfo('user_tz'));
-		$blog_upddt = dt::str(__('%Y-%m-%d %H:%M'),strtotime($this->rs->blog_upddt) + $offset);
-	
-		return '<td class="nowrap">'.$blog_upddt.'</td>';
-	}
-	
-	protected function getEntries()
-	{
-		return '<td class="nowrap">'.$this->core->countBlogPosts($this->rs->blog_id).'</td>';
-	}
-	
-	protected function getBlogId()
-	{
-		return '<td class="nowrap">'.html::escapeHTML($this->rs->blog_id).'</td>';
-	}
-	
-	protected function getAction()
-	{
-		$edit_link = '';
-		$blog_id = html::escapeHTML($this->rs->blog_id);
-	
-		if ($GLOBALS['core']->auth->isSuperAdmin()) {
-			$edit_link = 
-			'<a href="blog.php?id='.$blog_id.'" '.
-			'title="'.sprintf(__('Edit blog %s'),$blog_id).'">'.
-			__('edit').'</a>';
-		}
-		
-		return '<td class="nowrap">'.$edit_link.'</td>';
-	}
-	
-	protected function getStatus()
-	{
-		$img_status = $this->rs->blog_status == 1 ? 'check-on' : 'check-off';
-		$txt_status = $GLOBALS['core']->getBlogStatus($this->rs->blog_status);
-		$img_status = sprintf('<img src="images/%1$s.png" alt="%2$s" title="%2$s" />',$img_status,$txt_status);
-		
-		return '<td class="status">'.$img_status.'</td>';
-	}
-}
-
-/**
-@ingroup DC_CORE
-@nosubgrouping
-@brief abstract blogs permissions list class.
-
-Handle blogs permissions list on admin side
-*/
-class adminBlogPermissionsList extends adminBlogList
-{
-	public function setColumns()
-	{
-		$this->addColumn('blogid','B.blog_id',__('Blog ID'),array('adminBlogPermissionsList','getBlogId'),array('class' => array('nowrap')),false,true,false);
-		$this->addColumn('blogname','UPPER(blog_name)',__('Blog name'),array('adminBlogPermissionsList','getBlogName'),array('class' => array('maximal')),false);
-		$this->addColumn('entries',null,__('Entries'),array('adminBlogList','getEntries'),array('class' => array('nowrap')),false);
-		$this->addColumn('status','blog_status',__('status'),array('adminBlogList','getStatus'),array('class' => array('nowrap')),false);
-	}
-	
-	protected function getBlogId()
-	{
-		return
-		'<th scope="row" class="nowrap">'.
-		form::checkbox(array('blog_id[]'),$this->rs->blog_id,'','','',false,'title="'.__('select').' '.$this->rs->blog_id.'"').
-		$this->rs->blog_id.'</th>';
-	}
-	
-	protected function getBlogName()
-	{
-		return '<td class="maximal">'.html::escapeHTML($this->rs->blog_name).'</td>';
-	}
-}
-
-?>
Index: inc/admin/lib.dc.page.php
===================================================================
--- inc/admin/lib.dc.page.php	(revision 782)
+++ inc/admin/lib.dc.page.php	(revision 888)
@@ -124,5 +124,5 @@
 		
 		'<div id="header">'.
-		'<ul id="prelude"><li><a href="#content">Aller au contenu</a></li><li><a href="#main-menu">Aller au menu</a></li></ul>'."\n".
+		'<ul id="prelude"><li><a href="#content">'.__('Go to the content').'</a></li><li><a href="#main-menu">'.__('Go to the menu').'</a></li></ul>'."\n".
 		'<div id="top"><h1><a href="index.php">'.DC_VENDOR_NAME.'</a></h1></div>'."\n";	
 		
@@ -370,4 +370,5 @@
 		self::jsLoad('js/jquery/jquery.biscuit.js').
 		self::jsLoad('js/jquery/jquery.bgFade.js').
+		self::jsLoad('js/jquery/jquery.constantfooter.js').
 		self::jsLoad('js/common.js').
 		self::jsLoad('js/prelude.js').
@@ -515,11 +516,4 @@
 	}
 	
-	public static function jqueryUI()
-	{
-		return
-		'<link rel="stylesheet" type="text/css" href="style/jquery-ui-1.8.16.custom.css" />'."\n".
-		self::jsLoad('js/jquery/jquery-ui-1.8.16.custom.min.js');
-	}
-
 	public static function jsColorPicker()
 	{
Index: inc/admin/lib.pager.php
===================================================================
--- inc/admin/lib.pager.php	(revision 782)
+++ inc/admin/lib.pager.php	(revision 860)
@@ -12,12 +12,4 @@
 if (!defined('DC_RC_PATH')) { return; }
 
-/**
-@ingroup DC_CORE
-@nosubgrouping
-@brief Dotclear generic list class.
-@deprecated Please use class adminItemslist to create admin lists
-
-Dotclear generic list handles every admin list.
-*/
 class adminGenericList
 {
@@ -26,17 +18,6 @@
 	protected $rs_count;
 	
-	/**
-	@deprecated Please use class adminItemslist to create admin lists
-	
-	@param	core		<b>dcCore</b>		dcCore object
-	@param	rs		<b>recordSet</b>	Items recordSet to display
-	@param	rs_count	<b>int</b>		Total items number
-	*/
 	public function __construct($core,$rs,$rs_count)
 	{
-		// For backward compatibility only: the developer tried to create
-		// a list with the old constructor.
-		ob_start($this->raiseDeprecated(get_class($this)));
-		
 		$this->core =& $core;
 		$this->rs =& $rs;
@@ -45,23 +26,395 @@
 		$this->html_next = __('next&#187;');
 	}
-	
-	/**
-	Raises a E_USER_NOTICE error for deprecated classes. 
-	This allows the developer to know he's been using deprecated classes.
-	
-	@param	name	<b>string</b>	Name of the deprecated classes that was called.
-	*/
-	private function raiseDeprecated($name)
-	{
-		if (DC_DEBUG) {
-			$trace = debug_backtrace();
-			array_shift($trace);
-			$grand = array_shift($trace);
-			$msg = 'Deprecated class called. (';
-			$msg .= $name.' was called from '.$grand['file'].' ['.$grand['line'].'])';
-			trigger_error($msg, E_USER_NOTICE);
-		}
-	}
 }
 
+class adminPostList extends adminGenericList
+{
+	public function display($page,$nb_per_page,$enclose_block='')
+	{
+		if ($this->rs->isEmpty())
+		{
+			echo '<p><strong>'.__('No entry').'</strong></p>';
+		}
+		else
+		{
+			$pager = new pager($page,$this->rs_count,$nb_per_page,10);
+			$pager->html_prev = $this->html_prev;
+			$pager->html_next = $this->html_next;
+			$pager->var_page = 'page';
+			
+			$html_block =
+			'<table class="clear"><tr>'.
+			'<th colspan="2">'.__('Title').'</th>'.
+			'<th>'.__('Date').'</th>'.
+			'<th>'.__('Category').'</th>'.
+			'<th>'.__('Author').'</th>'.
+			'<th>'.__('Comments').'</th>'.
+			'<th>'.__('Trackbacks').'</th>'.
+			'<th>'.__('Status').'</th>'.
+			'</tr>%s</table>';
+			
+			if ($enclose_block) {
+				$html_block = sprintf($enclose_block,$html_block);
+			}
+			
+			echo '<p>'.__('Page(s)').' : '.$pager->getLinks().'</p>';
+			
+			$blocks = explode('%s',$html_block);
+			
+			echo $blocks[0];
+			
+			while ($this->rs->fetch())
+			{
+				echo $this->postLine();
+			}
+			
+			echo $blocks[1];
+			
+			echo '<p>'.__('Page(s)').' : '.$pager->getLinks().'</p>';
+		}
+	}
+	
+	private function postLine()
+	{
+		if ($this->core->auth->check('categories',$this->core->blog->id)) {
+			$cat_link = '<a href="category.php?id=%s">%s</a>';
+		} else {
+			$cat_link = '%2$s';
+		}
+		
+		if ($this->rs->cat_title) {
+			$cat_title = sprintf($cat_link,$this->rs->cat_id,
+			html::escapeHTML($this->rs->cat_title));
+		} else {
+			$cat_title = __('None');
+		}
+		
+		$img = '<img alt="%1$s" title="%1$s" src="images/%2$s" />';
+		switch ($this->rs->post_status) {
+			case 1:
+				$img_status = sprintf($img,__('published'),'check-on.png');
+				break;
+			case 0:
+				$img_status = sprintf($img,__('unpublished'),'check-off.png');
+				break;
+			case -1:
+				$img_status = sprintf($img,__('scheduled'),'scheduled.png');
+				break;
+			case -2:
+				$img_status = sprintf($img,__('pending'),'check-wrn.png');
+				break;
+		}
+		
+		$protected = '';
+		if ($this->rs->post_password) {
+			$protected = sprintf($img,__('protected'),'locker.png');
+		}
+		
+		$selected = '';
+		if ($this->rs->post_selected) {
+			$selected = sprintf($img,__('selected'),'selected.png');
+		}
+		
+		$attach = '';
+		$nb_media = $this->rs->countMedia();
+		if ($nb_media > 0) {
+			$attach_str = $nb_media == 1 ? __('%d attachment') : __('%d attachments');
+			$attach = sprintf($img,sprintf($attach_str,$nb_media),'attach.png');
+		}
+		
+		$res = '<tr class="line'.($this->rs->post_status != 1 ? ' offline' : '').'"'.
+		' id="p'.$this->rs->post_id.'">';
+		
+		$res .=
+		'<td class="nowrap">'.
+		form::checkbox(array('entries[]'),$this->rs->post_id,'','','',!$this->rs->isEditable()).'</td>'.
+		'<td class="maximal"><a href="'.$this->core->getPostAdminURL($this->rs->post_type,$this->rs->post_id).'">'.
+		html::escapeHTML($this->rs->post_title).'</a></td>'.
+		'<td class="nowrap">'.dt::dt2str(__('%Y-%m-%d %H:%M'),$this->rs->post_dt).'</td>'.
+		'<td class="nowrap">'.$cat_title.'</td>'.
+		'<td class="nowrap">'.$this->rs->user_id.'</td>'.
+		'<td class="nowrap">'.$this->rs->nb_comment.'</td>'.
+		'<td class="nowrap">'.$this->rs->nb_trackback.'</td>'.
+		'<td class="nowrap status">'.$img_status.' '.$selected.' '.$protected.' '.$attach.'</td>'.
+		'</tr>';
+		
+		return $res;
+	}
+}
+
+class adminPostMiniList extends adminGenericList
+{
+	public function display($page,$nb_per_page,$enclose_block='')
+	{
+		if ($this->rs->isEmpty())
+		{
+			echo '<p><strong>'.__('No entry').'</strong></p>';
+		}
+		else
+		{
+			$pager = new pager($page,$this->rs_count,$nb_per_page,10);
+			$pager->html_prev = $this->html_prev;
+			$pager->html_next = $this->html_next;
+			$pager->var_page = 'page';
+			
+			$html_block =
+			'<table class="clear"><tr>'.
+			'<th>'.__('Title').'</th>'.
+			'<th>'.__('Date').'</th>'.
+			'<th>'.__('Author').'</th>'.
+			'<th>'.__('Status').'</th>'.
+			'</tr>%s</table>';
+			
+			if ($enclose_block) {
+				$html_block = sprintf($enclose_block,$html_block);
+			}
+			
+			echo '<p>'.__('Page(s)').' : '.$pager->getLinks().'</p>';
+			
+			$blocks = explode('%s',$html_block);
+			
+			echo $blocks[0];
+			
+			while ($this->rs->fetch())
+			{
+				echo $this->postLine();
+			}
+			
+			echo $blocks[1];
+			
+			echo '<p>'.__('Page(s)').' : '.$pager->getLinks().'</p>';
+		}
+	}
+	
+	private function postLine()
+	{
+		$img = '<img alt="%1$s" title="%1$s" src="images/%2$s" />';
+		switch ($this->rs->post_status) {
+			case 1:
+				$img_status = sprintf($img,__('published'),'check-on.png');
+				break;
+			case 0:
+				$img_status = sprintf($img,__('unpublished'),'check-off.png');
+				break;
+			case -1:
+				$img_status = sprintf($img,__('scheduled'),'scheduled.png');
+				break;
+			case -2:
+				$img_status = sprintf($img,__('pending'),'check-wrn.png');
+				break;
+		}
+		
+		$protected = '';
+		if ($this->rs->post_password) {
+			$protected = sprintf($img,__('protected'),'locker.png');
+		}
+		
+		$selected = '';
+		if ($this->rs->post_selected) {
+			$selected = sprintf($img,__('selected'),'selected.png');
+		}
+		
+		$attach = '';
+		$nb_media = $this->rs->countMedia();
+		if ($nb_media > 0) {
+			$attach_str = $nb_media == 1 ? __('%d attachment') : __('%d attachments');
+			$attach = sprintf($img,sprintf($attach_str,$nb_media),'attach.png');
+		}
+		
+		$res = '<tr class="line'.($this->rs->post_status != 1 ? ' offline' : '').'"'.
+		' id="p'.$this->rs->post_id.'">';
+		
+		$res .=
+		'<td class="maximal"><a href="'.$this->core->getPostAdminURL($this->rs->post_type,$this->rs->post_id).'" '.
+		'title="'.html::escapeHTML($this->rs->getURL()).'">'.
+		html::escapeHTML($this->rs->post_title).'</a></td>'.
+		'<td class="nowrap">'.dt::dt2str(__('%Y-%m-%d %H:%M'),$this->rs->post_dt).'</td>'.
+		'<td class="nowrap">'.$this->rs->user_id.'</td>'.
+		'<td class="nowrap status">'.$img_status.' '.$selected.' '.$protected.' '.$attach.'</td>'.
+		'</tr>';
+		
+		return $res;
+	}
+}
+
+class adminCommentList extends adminGenericList
+{
+	public function display($page,$nb_per_page,$enclose_block='')
+	{
+		if ($this->rs->isEmpty())
+		{
+			echo '<p><strong>'.__('No comment').'</strong></p>';
+		}
+		else
+		{
+			$pager = new pager($page,$this->rs_count,$nb_per_page,10);
+			$pager->html_prev = $this->html_prev;
+			$pager->html_next = $this->html_next;
+			$pager->var_page = 'page';
+			
+			$html_block =
+			'<table><tr>'.
+			'<th colspan="2">'.__('Title').'</th>'.
+			'<th>'.__('Date').'</th>'.
+			'<th>'.__('Author').'</th>'.
+			'<th>'.__('Type').'</th>'.
+			'<th>'.__('Status').'</th>'.
+			'<th>&nbsp;</th>'.
+			'</tr>%s</table>';
+			
+			if ($enclose_block) {
+				$html_block = sprintf($enclose_block,$html_block);
+			}
+			
+			echo '<p>'.__('Page(s)').' : '.$pager->getLinks().'</p>';
+			
+			$blocks = explode('%s',$html_block);
+			
+			echo $blocks[0];
+			
+			while ($this->rs->fetch())
+			{
+				echo $this->commentLine();
+			}
+			
+			echo $blocks[1];
+			
+			echo '<p>'.__('Page(s)').' : '.$pager->getLinks().'</p>';
+		}
+	}
+	
+	private function commentLine()
+	{
+		global $author, $status, $sortby, $order, $nb_per_page;
+		
+		$author_url =
+		'comments.php?n='.$nb_per_page.
+		'&amp;status='.$status.
+		'&amp;sortby='.$sortby.
+		'&amp;order='.$order.
+		'&amp;author='.rawurlencode($this->rs->comment_author);
+		
+		$post_url = $this->core->getPostAdminURL($this->rs->post_type,$this->rs->post_id);
+		
+		$comment_url = 'comment.php?id='.$this->rs->comment_id;
+		
+		$comment_dt =
+		dt::dt2str($this->core->blog->settings->system->date_format.' - '.
+		$this->core->blog->settings->system->time_format,$this->rs->comment_dt);
+		
+		$img = '<img alt="%1$s" title="%1$s" src="images/%2$s" />';
+		switch ($this->rs->comment_status) {
+			case 1:
+				$img_status = sprintf($img,__('published'),'check-on.png');
+				break;
+			case 0:
+				$img_status = sprintf($img,__('unpublished'),'check-off.png');
+				break;
+			case -1:
+				$img_status = sprintf($img,__('pending'),'check-wrn.png');
+				break;
+			case -2:
+				$img_status = sprintf($img,__('junk'),'junk.png');
+				break;
+		}
+		
+		$comment_author = html::escapeHTML($this->rs->comment_author);
+		if (mb_strlen($comment_author) > 20) {
+			$comment_author = mb_strcut($comment_author,0,17).'...';
+		}
+		
+		$res = '<tr class="line'.($this->rs->comment_status != 1 ? ' offline' : '').'"'.
+		' id="c'.$this->rs->comment_id.'">';
+		
+		$res .=
+		'<td class="nowrap">'.
+		form::checkbox(array('comments[]'),$this->rs->comment_id,'','','',0).'</td>'.
+		'<td class="maximal"><a href="'.$post_url.'">'.
+		html::escapeHTML($this->rs->post_title).'</a>'.
+		($this->rs->post_type != 'post' ? ' ('.html::escapeHTML($this->rs->post_type).')' : '').'</td>'.
+		'<td class="nowrap">'.dt::dt2str(__('%Y-%m-%d %H:%M'),$this->rs->comment_dt).'</td>'.
+		'<td class="nowrap"><a href="'.$author_url.'">'.$comment_author.'</a></td>'.
+		'<td class="nowrap">'.($this->rs->comment_trackback ? __('trackback') : __('comment')).'</td>'.
+		'<td class="nowrap status">'.$img_status.'</td>'.
+		'<td class="nowrap status"><a href="'.$comment_url.'">'.
+		'<img src="images/edit-mini.png" alt="" title="'.__('Edit this comment').'" /></a></td>';
+		
+		$res .= '</tr>';
+		
+		return $res;
+	}
+}
+
+class adminUserList extends adminGenericList
+{
+	public function display($page,$nb_per_page,$enclose_block='')
+	{
+		if ($this->rs->isEmpty())
+		{
+			echo '<p><strong>'.__('No user').'</strong></p>';
+		}
+		else
+		{
+			$pager = new pager($page,$this->rs_count,$nb_per_page,10);
+			$pager->html_prev = $this->html_prev;
+			$pager->html_next = $this->html_next;
+			$pager->var_page = 'page';
+			
+			$html_block =
+			'<table class="clear"><tr>'.
+			'<th colspan="2">'.__('Username').'</th>'.
+			'<th>'.__('First Name').'</th>'.
+			'<th>'.__('Last Name').'</th>'.
+			'<th>'.__('Display name').'</th>'.
+			'<th class="nowrap">'.__('Entries').'</th>'.
+			'</tr>%s</table>';
+			
+			if ($enclose_block) {
+				$html_block = sprintf($enclose_block,$html_block);
+			}
+			
+			echo '<p>'.__('Page(s)').' : '.$pager->getLinks().'</p>';
+			
+			$blocks = explode('%s',$html_block);
+			
+			echo $blocks[0];
+			
+			while ($this->rs->fetch())
+			{
+				echo $this->userLine();
+			}
+			
+			echo $blocks[1];
+			
+			echo '<p>'.__('Page(s)').' : '.$pager->getLinks().'</p>';
+		}
+	}
+	
+	private function userLine()
+	{
+		$img = '<img alt="%1$s" title="%1$s" src="images/%2$s" />';
+		$img_status = '';
+		
+		$p = $this->core->getUserPermissions($this->rs->user_id);
+		
+		if (isset($p[$this->core->blog->id]['p']['admin'])) {
+			$img_status = sprintf($img,__('admin'),'admin.png');
+		}
+		if ($this->rs->user_super) {
+			$img_status = sprintf($img,__('superadmin'),'superadmin.png');
+		}
+		return
+		'<tr class="line">'.
+		'<td class="nowrap">'.form::hidden(array('nb_post[]'),(integer) $this->rs->nb_post).
+		form::checkbox(array('users[]'),$this->rs->user_id).'</td>'.
+		'<td class="maximal"><a href="user.php?id='.$this->rs->user_id.'">'.
+		$this->rs->user_id.'</a>&nbsp;'.$img_status.'</td>'.
+		'<td class="nowrap">'.$this->rs->user_firstname.'</td>'.
+		'<td class="nowrap">'.$this->rs->user_name.'</td>'.
+		'<td class="nowrap">'.$this->rs->user_displayname.'</td>'.
+		'<td class="nowrap"><a href="posts.php?user_id='.$this->rs->user_id.'">'.
+		$this->rs->nb_post.'</a></td>'.
+		'</tr>';
+	}
+}
 ?>
Index: inc/admin/prepend.php
===================================================================
--- inc/admin/prepend.php	(revision 692)
+++ inc/admin/prepend.php	(revision 859)
@@ -37,5 +37,5 @@
 function dc_prepare_url($url) {
 
-	$u = str_replace(array('?','&amp;'),array('\?','&'),$url);
+	$u = str_replace(array('?','&amp;','/'),array('\?','&','\\/'),$url);
 	return (!strpos($u,'\?') ? 
 		'/'.$u.'$/' :
@@ -215,4 +215,7 @@
 	}
 	unset($hfiles,$locales_root);
+
+	$core->auth->user_prefs->addWorkspace('interface');
+	$user_ui_nofavmenu = $core->auth->user_prefs->interface->nofavmenu;
 
 	# Standard favorites
@@ -266,5 +269,6 @@
 	# Menus creation
 	$_menu['Dashboard'] = new dcMenu('dashboard-menu',null);
-	$_menu['Favorites'] = new dcMenu('favorites-menu','My favorites');
+	if (!$user_ui_nofavmenu)
+		$_menu['Favorites'] = new dcMenu('favorites-menu','My favorites');
 	$_menu['Blog'] = new dcMenu('blog-menu','Blog');
 	$_menu['System'] = new dcMenu('system-menu','System');
@@ -282,5 +286,6 @@
 	$_menu['Blog']->title = __('Blog');
 	$_menu['Plugins']->title = __('Plugins');
-	$_menu['Favorites']->title = __('My favorites');
+	if (!$user_ui_nofavmenu)
+		$_menu['Favorites']->title = __('My favorites');
 
 /*	
@@ -333,36 +338,38 @@
 		$core->auth->check('usage,contentadmin',$core->blog->id) && $core->auth->blog_count > 1);
 
-	// Set favorites menu
-	$ws = $core->auth->user_prefs->addWorkspace('favorites');
-	$count = 0;
-	foreach ($ws->dumpPrefs() as $k => $v) {
-		// User favorites only
-		if (!$v['global']) {
-			$fav = unserialize($v['value']);
-			if (dc_valid_fav($fav['url'])) {
-				$count++;
-				$_menu['Favorites']->addItem(__($fav['title']),$fav['url'],$fav['small-icon'],
-					preg_match(dc_prepare_url($fav['url']),$_SERVER['REQUEST_URI']),
-					(($fav['permissions'] == '*') || $core->auth->check($fav['permissions'],$core->blog->id)),$fav['id'],$fav['class']);
-			}
-		}
-	}	
-	if (!$count) {
-		// Global favorites if any
+	if (!$user_ui_nofavmenu) {
+		// Set favorites menu
+		$ws = $core->auth->user_prefs->addWorkspace('favorites');
+		$count = 0;
 		foreach ($ws->dumpPrefs() as $k => $v) {
-			$fav = unserialize($v['value']);
-			if (dc_valid_fav($fav['url'])) {
-				$count++;
-				$_menu['Favorites']->addItem(__($fav['title']),$fav['url'],$fav['small-icon'],
-					preg_match(dc_prepare_url($fav['url']),$_SERVER['REQUEST_URI']),
-					(($fav['permissions'] == '*') || $core->auth->check($fav['permissions'],$core->blog->id)),$fav['id'],$fav['class']);
-			}
-		}
-	}
-	if (!$count) {
-		// No user or global favorites, add "new entry" fav
-		$_menu['Favorites']->addItem(__('New entry'),'post.php','images/menu/edit.png',
-			preg_match('/post.php$/',$_SERVER['REQUEST_URI']),
-			$core->auth->check('usage,contentadmin',$core->blog->id),'menu-new-post',null);
+			// User favorites only
+			if (!$v['global']) {
+				$fav = unserialize($v['value']);
+				if (dc_valid_fav($fav['url'])) {
+					$count++;
+					$_menu['Favorites']->addItem(__($fav['title']),$fav['url'],$fav['small-icon'],
+						preg_match(dc_prepare_url($fav['url']),$_SERVER['REQUEST_URI']),
+						(($fav['permissions'] == '*') || $core->auth->check($fav['permissions'],$core->blog->id)),$fav['id'],$fav['class']);
+				}
+			}
+		}	
+		if (!$count) {
+			// Global favorites if any
+			foreach ($ws->dumpPrefs() as $k => $v) {
+				$fav = unserialize($v['value']);
+				if (dc_valid_fav($fav['url'])) {
+					$count++;
+					$_menu['Favorites']->addItem(__($fav['title']),$fav['url'],$fav['small-icon'],
+						preg_match(dc_prepare_url($fav['url']),$_SERVER['REQUEST_URI']),
+						(($fav['permissions'] == '*') || $core->auth->check($fav['permissions'],$core->blog->id)),$fav['id'],$fav['class']);
+				}
+			}
+		}
+		if (!$count) {
+			// No user or global favorites, add "new entry" fav
+			$_menu['Favorites']->addItem(__('New entry'),'post.php','images/menu/edit.png',
+				preg_match('/post.php$/',$_SERVER['REQUEST_URI']),
+				$core->auth->check('usage,contentadmin',$core->blog->id),'menu-new-post',null);
+		}
 	}
 }
