Index: inc/admin/class.dc.filter.php
===================================================================
--- inc/admin/class.dc.filter.php	(revision 580)
+++ inc/admin/class.dc.filter.php	(revision 242)
@@ -1,37 +1,34 @@
 <?php
-# -- BEGIN LICENSE BLOCK ---------------------------------------
+# ***** BEGIN LICENSE BLOCK *****
+# This file is part of DotClear "MyPostTypes" plugin.
+# Copyright (c) 2010 Bruno Hondelatte, and contributors. 
+# Many, many thanks to Olivier Meunier and the Dotclear Team.
+# All rights reserved.
 #
-# This file is part of Dotclear 2.
+# MyPostTypes plugin for DC2 is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+# 
+# DotClear is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+# 
+# You should have received a copy of the GNU General Public License
+# along with DotClear; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 #
-# 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 Dotclear FilterSet class.
-
-Dotclear FilterSet handles filters and columns when displaying items lists.
-*/
+# ***** END LICENSE BLOCK *****
+
 class dcFilterSet {
 
-	protected $filters;			/// <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 $columns_form;	/// <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;
+	protected $filters;
+	protected $form_prefix;
+	protected $action;
+	protected $hideform;
+	protected $columns_form;
+	
+	public function __construct($action,$form_prefix="f_") {
 		$this->form_prefix=$form_prefix;
 		$this->filters = array();
@@ -39,9 +36,4 @@
 	}
 
-	/**
-	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);
@@ -49,66 +41,20 @@
 		return $this;
 	}
-	
-	/**
-	Saves user filters to preferences
-	*/
-	protected function saveFilters() {
-		$ser = array();
-		$ws = $GLOBALS['core']->auth->user_prefs->addWorkspace('filters');
-		foreach($this->filters as $filter) {
-			$ser[$filter->id]=$filter->serialize();
-		}
-		$ws->put($this->name,serialize($ser),'string');
-	}
-	
-	/**
-	Loads user filters from preferences
-	*/
-	protected function loadFilters() {
-		$ws = $GLOBALS['core']->auth->user_prefs->addWorkspace('filters');
-		
-		$settings = !is_null($ws->{$this->name}) ? unserialize($ws->{$this->name}) : array();
-		foreach($settings as $k => $v) {
-			$this->filters[$k]->unserialize($v);
-		}
-	}
-	
-	/**
-	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)
-	*/
-	public function setFormValues ($form_data) {
+
+	// Retrieves filter values from context
+	public function setValues ($form_data) {
 		$this->hideform = true;
-		if (isset($form_data['clear_filters'])) {
-			$this->saveFilters();
+		if (isset($form_data['clear_filters']))
 			return;
-		}
-		if (!isset($form_data['apply'])) {
-			$this->loadFilters();
-		}
-		foreach ($this->filters as $filter) {
-			$filter->setFormValues ($form_data);
-			if ($filter->isEnabled()) {
+		foreach ($this->filters as $filter) {
+			$filter->setValues ($form_data);
+			if ($filter->isEnabled())
 				$this->hideform=false;
-			}
-		}
-		if (isset($form_data['apply'])) {
-			if (trim($form_data['apply']) == '+'
-				&& isset($form_data['add_filter']) 
-				&& isset($this->filters[$form_data['add_filter']])) {
-				$this->filters[$form_data['add_filter']]->add();
-				$this->hideform=false;
-			}
-		}
-		$this->saveFilters();
-	}
-	
-	/**
-	Defines additional form in layout (right column)
-	
-	@param	html	<b>string</b>		the code to add
-	*/
+		}
+		if (isset($form_data['add_filter']) && isset($this->filters[$form_data['add_filter']])) {
+			$this->filters[$form_data['add_filter']]->add();
+		}
+	}
+	
 	public function setColumnsForm($html)
 	{
@@ -116,9 +62,4 @@
 	}
 	
-	/**
-	Returns form fields as hidden fields
-	
-	@return	<b>string</b>	the corresponding html code
-	*/
 	public function getFormFieldsAsHidden() {
 		$ret='';
@@ -129,21 +70,14 @@
 	}
 
-	/**
-	Retrieves filterset generated form
-	
-	@param	method	<b>string</b>		form method to use (default: "get")
-	*/
-	public function getForm($method="get") {
+	public function getForm($action,$extra_content,$method="get",$nb_cols=3) {
 		$ret = '';
-		
-		if ($this->hideform) {
-			$formclass = ' class="hidden"';
-		} else {
-			$formclass='';
-		}
-		$ret .= '<p><img alt="" src="images/minus.png" /> <a href="#" id="toggle-filters">'.__('Toggle filters and display options').'</a></p>';
+		/*if ($this->hideform) {
+			$ret .= '<p><a id="filter-control" class="form-control" href="#">'.
+			__('Filters').'</a></p>';
+		}*/
+		$ret .= '<p><img alt="" src="minus.png"/> <a href="#" id="toggle-filters">'.__('Toggle filters and display options').'</a></p>';
 		$ret .=
 			'<div class="two-cols">'.
-			'<form id="filters" action="'.$this->action.'" method="'.$method.'"'.$formclass.'>'.
+			'<form id="filters" action="'.$this->action.'" method="get" id="filters-form">'.
 			'<div class="col70">'.
 			'<h3>'.__('Entries filters').'</h3>';
@@ -152,21 +86,17 @@
 		$form_combo=array();
 		$form_combo['-']='';
-		if (count($this->filters)) {
-			$ret .= '<ul>';
-			foreach ($this->filters as $filter) {
-				if ($filter->isEnabled()) {
-					$ret .= $filter->getFormLine();
-				}
-				$form_combo[$filter->name]=$filter->id;
-				$count++;
+		foreach ($this->filters as $filter) {
+			if ($filter->isEnabled()) {
+				$ret .= $filter->getFormLine();
 			}
-			$ret .= '</ul>';
+			$form_combo[$filter->desc]=$filter->id;
+			$count++;
 		}
 		$ret .= 
-			'<p class="clear"><input class="delete" type="submit" value="'.__('Delete all filters').'" name="clear_filters" /></p>'.
+			'<p class="clear"><input class="delete" type="submit" value="'.__('Delete all filters').'" name="clear_filters"></p>'.
 			'<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="apply" />'.
+			'<input type="submit" value=" + " title="'.__('Add this filter').'" name="apply">'.
 			'</p>'.
 			'</div>'.
@@ -174,33 +104,17 @@
 			$this->columns_form.
 			'</div>'.
-			'<p class="clear margintop"><input type="submit" value="'.__('Apply filters and display options').'" name="apply" /></p>'.
+			'<p class="clear margintop"><input type="submit" value="'.__('Apply filters and display options').'" name="apply"></p>'.
 
 			'</form></div>';
 		return $ret;
 	}
-	
-	/**
-	Displays required fieldset http header
-	To be called in page header, of course.
-	*/
+
 	public function header() {
 		return dcPage::jsLoad('js/filters.js');
 	}
-	
-	
-	/**
-	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")
-	
-	*/
+		echo $this->getForm("#","");
+	}
+
 	public function applyFilters($params) {
 		$filtered = false;
@@ -217,30 +131,15 @@
 
 
-/**
-@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 $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) {
+	public $id;
+	public $desc;
+	protected $request_param;
+	protected $enabled;
+	protected $values;
+	public $field_id;
+	
+	
+	public function __construct ($id,$desc,$request_param) {
 		$this->id = $id;
-		$this->name=$name;
 		$this->desc = $desc;
 		$this->request_param = $request_param;
@@ -250,10 +149,4 @@
 	}
 	
-	/**
-	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) {
@@ -264,88 +157,36 @@
 	}
 	
-	/**
-	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;
 	}
 	
-	/**
-	Defines form prefix for filter
-	
-	@param	prefix		<b>string</b>	the form prefix
-	*/
 	public function setFormPrefix($prefix) {
 		$this->field_id = $prefix.$this->id;
 	}
 	
-	
-	/**
-	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
-	*/
-	public function getFormFields($pos=0) {
+	public abstract function getType();
+	
+	public function getFormFields() {
 		return '';
 	}
-	
-	/**
-	Returns filter values il a serialized way (array)
-	
-	@return		<b>array</b>	serialized data
-	*/
-	public function serialize() {
-		return array(
-			'values' => $this->values,
-			'enabled' => $this->enabled
-		);
-	}
-	
-	/**
-	Defines filter values from serialized data (array)
-	To be used in conjunction with serialize method
-	
-	@param	$data	<b>array</b>	serialized data to retrieve
-	*/
-	public function unserialize ($data) {
-		$this->values = $data['values'];
-		$this->enabled = $data['enabled'];
-	}
-	
-	/**
-	Set filter values from form_data (usually $_GET)	
-	@param	$form_data	<b>array</b>	form data
-	*/
-	public function setFormValues($form_data) {
+
+	public function setValues($form_data) {
+/*		if (isset($form_data['c_'.$this->field_id])) {
+			$this->enabled = true;
+		}*/
 		$count=0;
 		while (isset($form_data[$this->getFieldId($count)])) {
 			if (!isset($form_data['del_'.$this->getFieldId($count)])) {
-				$this->values[$count] = $form_data[$this->getFieldId($count)];
-			} elseif (isset($this->values[$count])) {
-				unset($this->values[$count]);
+				$this->values[] = $form_data[$this->getFieldId($count)];
+				$this->enabled = true;
 			}
 			$count++;
-
-		}
-		$this->values = array_values($this->values);
-		$this->enabled = (count($this->values)!=0);
-	}
-	
-		/**
-	Returns form fields as hidden fields
-	
-	@return	<b>string</b>	the corresponding html code
-	*/	
+		}
+	}
+	
 	public function getFormFieldAsHidden () {
 		$ret='';
@@ -354,47 +195,21 @@
 		}
 	}
-	/**
-	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 .= '<li id="'.$this->getFieldId($cur).'" class="line" title="'.$this->desc.'">'.
+			$ret .= '<p id="'.$this->getFieldId($cur).'" class="line" title="'.$this->desc.'">'.
 				$this->getFormFields($cur).
 				'<input id="del_'.$this->getFieldId($cur).'" class="delete" '.
-				'type="submit" title="Delete the following filter : " value=" - " name="del_'.$this->getFieldId($cur).'"/>'.
-				'</li>';
+				'type="submit" title="Delete this filter" value=" - " name="del_'.$this->getFieldId($cur).'"/>'.
+				'</p>';
 		}
 		return $ret;
 	}
 	
-	/**
-	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;
-	}
-	
 }
 
-/**
-@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;
@@ -404,8 +219,9 @@
 	protected $extra;
 	
-	public function __construct($id,$name,$desc,$request_param,$options,$extra=array()) {
-		parent::__construct($id,$name,$desc,$request_param);
+	public function __construct($id,$desc,$request_param,$options,$extra=array()) {
+		parent::__construct($id,$desc,$request_param);
 		$this->options = $options;
 		$this->extra = $extra;
+		$this->desc = $desc;
 		$this->verb = "is";
 		$this->values=array();
@@ -422,20 +238,9 @@
 		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 setFormValues($form_data) {
-		parent::setFormValues($form_data);
+	
+	public function setValues($form_data) {
+		parent::setValues($form_data);
 		if (isset($form_data[$this->field_id."_v"])) {
-			$this->verb = ($form_data[$this->field_id."_v"] == 'is') ? 'is' : 'isnot';
+			$this->verb = $form_data[$this->field_id."_v"] == 'is' ? 'is' : 'isnot';
 		}
 	}
@@ -449,106 +254,20 @@
 		if ($pos == 0) {
 			$desc = $this->desc.' : ';
-			$labelclass="filter-title";
+			$labelclass="";
 		} else {
 			$desc = __('or');
-			$labelclass = 'or';
+			$labelclass = ' class="or"';
 		};
-		return '<span class="'.$labelclass.'">'.$desc.'</span>'.
-			(($pos == 0) 
-				?form::combo($this->field_id.'_v',
-					array(__('is')=>'is',__('is not')=>'isnot'),$this->verb,'','',
-					false,'title="'.sprintf(__('%s is or is not'),$this->desc).'"') 
-				:'').
-			form::combo($this->getFieldId($pos),$this->options,$this->values[$pos],
-				'','',false,'title="'.__('Choose an option').'"');
+		return '<label for="'.$this->getFieldId($pos).'"'.$labelclass.'>'.$desc.'</label>'.
+			(($pos == 0) ?form::combo($this->field_id.'_v',array(__('is')=>'is',__('is not')=>'isnot'),$this->verb) : '').
+			form::combo($this->getFieldId($pos),$this->options,$this->values[$pos]);
 	}
 	
 	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];
+			$params[$this->request_param]=$this->values[0];
 		else
-			$params[$attr]=$this->values;
-	}
-}
-
-/**
-@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 '<span class="'.$labelclass.'">'.$this->desc.'</span>'.
-			form::combo($this->getFieldId($pos),$this->options,$this->values[$pos],
-				'','',false,'title="'.__('Choose an option').'"');
-	}
-	
-	public function applyFilter($params) {
-		$params[$this->request_param]=$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 '<span class="'.$labelclass.'">'.$this->desc.'</span>'.
-			form::field($this->getFieldId($pos),$this->size,$this->max,html::escapeHTML($this->values[0]));
-	}
-	
-	public function applyFilter($params) {
-		$params[$this->request_param]=$this->values[0];
-	}
-	
-	public function setValues($value) {
-		parent::setValues(array($value));
-	}
-
-	public function getValue() {
-		$v = parent::getValue();
-		return $v[0];
-	}
-	
+			$params[$this->request_param]=$this->values;
+	}
 }
 ?>
Index: inc/admin/class.dc.list.php
===================================================================
--- inc/admin/class.dc.list.php	(revision 521)
+++ 	(revision )
@@ -1,1080 +1,0 @@
-<?php
-# -- BEGIN LICENSE BLOCK ---------------------------------------
-#
-# This file is part of Dotclear 2.
-#
-# Copyright (c) 2003-2010 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
-{
-	protected $core;
-	protected $rs;
-	protected $rs_count;
-	protected $columns;
-	protected $sortby;
-	protected $order;
-	protected $nb_per_page;
-	protected $page;
-	
-	/*
-	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 = array();
-		$this->form_prefix = 'col_%s';
-		$this->form_trigger = 'add_filter';
-		
-		$this->html_prev = __('prev');
-		$this->html_next = __('next');
-		$this->html_start = __('start');
-		$this->html_end = __('end');
-		
-		$this->setOptions();
-		
-		$this->setColumns();
-		
-		# --BEHAVIOR-- adminItemsListConstruct
-		$core->callBehavior('adminItemsListConstruct',$this);
-		
-		$this->setColumnsVisibility();
-	}
-	
-	/**
-	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)) {
-			foreach ($this->columns as $k => $c) {
-				if (
-					$this->sortby === $c->getInfo('alias') &&
-					in_array($this->order,array('asc','desc'))
-				) {
-					$params['order'] = $this->sortby.' '.$this->order;
-					break;
-				}
-			}
-		}
-		
-		$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 getColumnsForm()
-	{
-		$block = 
-		'<h3>'.__('Displayed information').'</h3>'.
-		'<ul>%s</ul>';
-		
-		$list = array();
-		
-		foreach ($this->columns as $k => $v) {
-			$col_id = sprintf($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);
-			}
-		}
-		
-		$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>';
-	}
-	
-	/**
-	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>';
-	}
-	
-	/**
-	Sets options of defined list
-	*/
-	private function setOptions()
-	{
-		$opts = $_GET;
-		
-		$ws = $this->core->auth->user_prefs->addWorkspace('lists');
-		
-		$user_pref = !is_null($ws->{$this->context.'_opts'}) ? unserialize($ws->{$this->context.'_opts'}) : array();
-		# Sortby
-		$this->sortby = array_key_exists('sortby',$user_pref) ? $user_pref['sortby'] : null;
-		$this->sortby = array_key_exists('sortby',$opts) ? $opts['sortby'] : $this->sortby;
-		$user_pref['sortby'] = $this->sortby;
-		# Order
-		$this->order = array_key_exists('order',$user_pref) ? $user_pref['order'] : null;
-		$this->order = array_key_exists('order',$opts) ? $opts['order'] : $this->order;
-		$user_pref['order'] = $this->order;
-		# Number per page
-		$this->nb_per_page = array_key_exists('nb_per_page',$user_pref) ? $user_pref['nb_per_page'] : 10;
-		$this->nb_per_page = array_key_exists('nb_per_page',$opts) ? $opts['nb_per_page'] : $this->nb_per_page;
-		$user_pref['nb_per_page'] = $this->nb_per_page;
-		
-		if (array_key_exists('sortby',$opts) || array_key_exists('order',$opts) || array_key_exists('nb_per_page',$opts)) {
-			$this->core->auth->user_prefs->lists->put($this->context.'_opts',serialize($user_pref),'string');
-		}
-		
-		# Page
-		$this->page = array_key_exists('page',$opts) ? $opts['page'] : 1;
-	}
-	
-	/**
-	Sets columns visibility of defined list
-	*/
-	private function setColumnsVisibility()
-	{
-		$opts = $_GET;
-		
-		# Visibility
-		$ws = $this->core->auth->user_prefs->addWorkspace('lists');
-		
-		$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;
-			if (array_key_exists($this->form_trigger,$opts)) {
-				$key = sprintf($this->form_prefix,$k);
-				$visibility = !array_key_exists($key,$opts) ? false : true;
-			}
-			$v->setVisibility($visibility);
-			$user_pref[$k] = $visibility;
-		}
-		
-		if (array_key_exists($this->form_trigger,$opts)) {
-			$this->core->auth->user_prefs->lists->put($this->context.'_col',serialize($user_pref),'string');
-		}
-	}
-	
-	/**
-	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('alias')) {
-			if (!is_null($this->order) && $this->order === $order) {
-				$order = 'asc';
-			}
-		}
-		
-		$args = $_GET;
-		$args['sortby'] = $c->getInfo('alias');
-		$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'));
-	}
-	
-	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);
-	}
-	
-	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')));
-	}
-	
-	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')));
-	}
-	
-	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/class.dc.toolbar.php
===================================================================
--- inc/admin/class.dc.toolbar.php	(revision 430)
+++ inc/admin/class.dc.toolbar.php	(revision 430)
@@ -0,0 +1,128 @@
+<?php
+
+class dcToolbar
+{
+	protected $core;
+	protected $toolbars;
+	
+	public function __construct($core)
+	{
+		$this->core = $core;
+		$this->toolbars = array();
+		$this->i18n = array();
+	}
+	
+	public function addFormatter($formatter)
+	{
+		if (!is_string($formatter) || empty($formatter)) {
+			return;
+		}
+		
+		$toolbar = array(
+			'settings' => array(),
+			'plugins' => array(),
+			'buttons' => array(
+				1 => array(),
+				2 => array(),
+				3 => array(),
+				4 => array()
+			)
+		);
+		
+		$this->toolbars[$formatter] = array_key_exists($formatter,$this->toolbars) ? $this->toolbars[$formatter] : $toolbar;
+	}
+	
+	public function addSettings($formatter,$settings)
+	{
+		
+		if (!array_key_exists($formatter,$this->toolbars)) {
+			return;
+		}
+		if (!is_array($settings)) {
+			return;
+		}
+		
+		$this->toolbars[$formatter]['settings'] = array_merge($this->toolbars[$formatter]['settings'],$settings);
+	}
+	
+	public function addPlugins($formatter,$plugins)
+	{
+		if (!array_key_exists($formatter,$this->toolbars)) {
+			return;
+		}
+		if (!is_array($plugins)) {
+			return;
+		}
+		
+		$this->toolbars[$formatter]['plugins'] = array_merge($this->toolbars[$formatter]['plugins'],$plugins);
+	}
+	
+	public function addButtons($formatter,$buttons)
+	{
+		if (!array_key_exists($formatter,$this->toolbars)) {
+			return;
+		}
+		if (!is_array($buttons)) {
+			return;
+		}
+		
+		foreach($buttons as $level => $ids) {
+			if (!is_array($ids)) {
+				continue;
+			}
+			$level = !preg_match('#^[1-4]$#',$level) ? 1 : $level;
+			$this->toolbars[$formatter]['buttons'][$level] = array_merge($this->toolbars[$formatter]['buttons'][$level],$ids);
+		}
+	}
+	
+	public function addI18n($component,$i18n)
+	{
+		if (!is_array($i18n)) {
+			return;
+		}
+		if (!array_key_exists($component,$this->i18n)) {
+			$this->i18n[$component] = array();
+		}
+		
+		$this->i18n[$component] = array_merge($this->i18n[$component],$i18n);
+	}
+	
+	public function getJS()
+	{
+		$res = "dcToolBar = new dcToolBar();\n";
+		
+		$this->core->callBehavior('adminToolbar',$this);
+		
+		foreach ($this->toolbars as $formatter => $options) {
+			$s = $options['settings'];
+			$s['language'] = 'en';
+			
+			// Add plugins
+			array_walk($options['plugins'],create_function('&$v,$k','$v=!$v ? "-".$k : $k;'));
+			$s['plugins'] = implode(',',$options['plugins']);
+			
+			// Add buttons
+			foreach ($options['buttons'] as $level => $buttons) {
+				$s[sprintf('theme_advanced_buttons%d',$level)] = implode(',',$buttons);
+			}
+			
+			// Add configuration
+			array_walk($s,create_function('&$v,$k','$v=sprintf("%s: %s",$k,(!preg_match("#^(true|false|{.*})$#",$v)?"\'".$v."\'":$v));'));
+			$res .= sprintf("dcToolBar.setConfig('%s',{%s});\n",$formatter,implode(",\n",$s));
+		}
+		
+		// Add translation
+		$i18n = array();
+		
+		foreach ($this->i18n as $component => $values) {
+			array_walk($this->i18n[$component],create_function('&$v,$k','$v=sprintf("\'%s\': \'%s\'",$k,html::escapeJS($v));'));
+			array_push($i18n,sprintf('%s: {%s}',$component,implode(',',$this->i18n[$component])));
+		}
+		
+		$res .= !empty($i18n) ? sprintf("dcToolBar.setI18n({%s: {%s}});\n",'en',implode(",\n",$i18n)) : '';
+		
+		return $res;
+	}
+}
+
+?>
Index: inc/admin/lib.dc.page.php
===================================================================
--- inc/admin/lib.dc.page.php	(revision 224)
+++ inc/admin/lib.dc.page.php	(revision 593)
@@ -549,80 +549,281 @@
 	public static function jsToolBar()
 	{
+		$tb = new dcToolBar($GLOBALS['core']);
+		
+		// Add xhtml toolbar
+		$tb->addFormatter('xhtml');
+		$tb->addSettings('xhtml',array(
+			'mode' => 'textareas',
+			'relative_urls' => 'false',
+			'theme' => 'advanced',
+			'skin' => 'dotclear',
+			'theme_advanced_toolbar_location' => 'top',
+			'theme_advanced_toolbar_align' => 'left',
+			'theme_advanced_statusbar_location' => 'bottom',
+			'theme_advanced_resizing' => 'true',
+			'theme_advanced_resize_horizontal' => 'false',
+			'theme_advanced_blockformats' => 'p,pre,h3,h4,h5,h6',
+			'paste_auto_cleanup_on_paste' => 'true',
+			'forced_root_block' => '',
+			'formats' => '{underline: {inline: "ins"},strikethrough: {inline: "del"},inlinecode: {inline: "code"},quote: {inline: "q"}}',
+			'entity_encoding' => 'raw'
+		));
+		$tb->addPlugins('xhtml',array(
+			'fullscreen' => true,
+			'paste' => true,
+			'searchreplace' => true,
+			'inlinepopups' => true,
+			'dcControls' => true
+		));
+		$tb->addButtons('xhtml',array(
+			1 => array(
+				'justifyleft',
+				'justifycenter',
+				'justifyright',
+				'separator',
+				'bold',
+				'italic',
+				'underline',
+				'strikethrough',
+				'inlinecode',
+				'quote',
+				'sub',
+				'sup',
+				'separator',
+				'blockquote',
+				'bullist',
+				'numlist',
+				'outdent',
+				'indent',
+				'separator',
+				'undo',
+				'redo',
+				'separator',
+				'fullscreen',
+				'separator',
+				'code'
+			),
+			2 => array(
+				'formatselect',
+				'removeformat',
+				'cleanup',
+				'separator',
+				'pastetext',
+				'pasteword',
+				'separator',
+				'search',
+				'replace',
+				'autosave'
+			),
+			3 => array(
+				'link',
+				'unlink',
+				'anchor',
+				'separator',
+				'media',
+				'webmedia',
+				'separator',
+				'hr',
+				'charmap',
+				'visualchars'
+			)
+		));
+		
+		// Add wiki toolbar
+		$tb->addFormatter('wiki');
+		$tb->addSettings('wiki',array(
+			'mode' => 'none',
+			'relative_urls' => 'false',
+			'theme' => 'advanced',
+			'skin' => 'dotclear',
+			'theme_advanced_toolbar_location' => 'top',
+			'theme_advanced_toolbar_align' => 'left',
+			'theme_advanced_statusbar_location' => 'bottom',
+			'theme_advanced_resizing' => 'true',
+			'theme_advanced_resize_horizontal' => 'false',
+			'theme_advanced_path'  => 'false',
+			'theme_advanced_blockformats' => 'h3,h4,h5,h6',
+			'remove_linebreaks' => 'false',
+			'inline_styles' => 'false',
+			'convert_fonts_to_spans' => 'false',
+			'paste_auto_cleanup_on_paste' => 'true',
+			'force_br_newlines' => 'true',
+			'force_p_newlines' => 'false',
+			'forced_root_block' => ''
+		));
+		$tb->addPlugins('wiki',array(
+			'fullscreen' => true,
+			'paste' => true,
+			'searchreplace' => true,
+			'dcControls' => true,
+			'dcwiki' => true
+		));
+		$tb->addButtons('wiki',array(
+			1 => array(
+				'formatselect',
+				'bold',
+				'italic',
+				'underline',
+				'strikethrough',
+				'quote',
+				'inlinecode',
+				'separator',
+				'blockquote',
+				'bullist',
+				'numlist',
+				'separator',
+				'link',
+				'separator',
+				'search',
+				'replace',
+				'separator',
+				'undo',
+				'redo',
+				'separator',
+				'fullscreen'
+			)
+		));
+		
+		$tb->addI18n('common',array(
+			'edit_confirm' => __('Do you want to use the WYSIWYG mode for this textarea?'),
+			'apply' => __('Apply'),
+			'insert' => __('Insert'),
+			'update' => __('Update'),
+			'cancel' => __('Cancel'),
+			'close' => __('Close'),
+			'browse' => __('Browse'),
+			'class_name' => __('Class'),
+			'not_set' => __('-- Not set --'),
+			'clipboard_msg' => __('Copy/Cut/Paste is not available in Mozilla and Firefox. Do you want more information about this issue?'),
+			'clipboard_no_support' => __('Currently not supported by your browser, use keyboard shortcuts instead.'),
+			'popup_blocked' => __('Sorry, but we have noticed that your popup-blocker has disabled a window that provides application functionality. You will need to disable popup blocking on this site in order to fully utilize this tool.'),
+			'invalid_data' => __('{#field} is invalid'),
+			'invalid_data_number' => __('{#field} must be a number'),
+			'invalid_data_min' => __('{#field} must be a number greater than {#min}'),
+			'invalid_data_size' => __('{#field} must be a number or percentage'),
+			'more_colors' => __('More colors')
+		));
+		$tb->addI18n('advanced',array(
+			'style_select' => __('Styles'),
+			'font_size' => __('Font size'),
+			'fontdefault' => __('Font family'),
+			'block' => __('Format'),
+			'paragraph' => __('Paragraph'),
+			'div' => __('Div'),
+			'address' => __('Address'),
+			'pre' => __('Preformatted'),
+			'h1' => __('Heading blog'),
+			'h2' => __('Heading entry'),
+			'h3' => __('Heading 1'),
+			'h4' => __('Heading 2'),
+			'h5' => __('Heading 3'),
+			'h6' => __('Heading 4'),
+			'blockquote' => __('Blockquote'),
+			'code' => __('Code'),
+			'samp' => __('Code sample'),
+			'dt' => __('Definition term '),
+			'dd' => __('Definition description'),
+			'bold_desc' => __('Bold (Ctrl+B)'),
+			'italic_desc' => __('Italic (Ctrl+I)'),
+			'underline_desc' => __('Underline (Ctrl+U)'),
+			'striketrough_desc' => __('Strikethrough'),
+			'justifyleft_desc' => __('Align left'),
+			'justifycenter_desc' => __('Align center'),
+			'justifyright_desc' => __('Align right'),
+			'justifyfull_desc' => __('Align full'),
+			'bullist_desc' => __('Unordered list'),
+			'numlist_desc' => __('Ordered list'),
+			'outdent_desc' => __('Outdent'),
+			'indent_desc' => __('Indent'),
+			'undo_desc' => __('Undo (Ctrl+Z)'),
+			'redo_desc' => __('Redo (Ctrl+Y)'),
+			'link_desc' => __('Insert/edit link'),
+			'unlink_desc' => __('Unlink'),
+			'image_desc' => __('Insert/edit image'),
+			'cleanup_desc' => __('Cleanup messy code'),
+			'code_desc' => __('Edit HTML Source'),
+			'sub_desc' => __('Subscript'),
+			'sup_desc' => __('Superscript'),
+			'hr_desc' => __('Insert horizontal ruler'),
+			'removeformat_desc' => __('Remove formatting'),
+			'custom1_desc' => __('Your custom description here'),
+			'forecolor_desc' => __('Select text color'),
+			'backcolor_desc' => __('Select background color'),
+			'charmap_desc' => __('Insert custom character'),
+			'visualaid_desc' => __('Toggle guidelines/invisible elements'),
+			'anchor_desc' => __('Insert/edit anchor'),
+			'cut_desc' => __('Cut'),
+			'copy_desc' => __('Copy'),
+			'paste_desc' => __('Paste'),
+			'image_props_desc' => __('Image properties'),
+			'newdocument_desc' => __('New document'),
+			'help_desc' => __('Help'),
+			'blockquote_desc' => __('Blockquote'),
+			'clipboard_msg' => __('Copy/Cut/Paste is not available in Mozilla and Firefox.\r\nDo you want more information about this issue?'),
+			'path' => __('Path'),
+			'newdocument' => __('Are you sure you want clear all contents?'),
+			'toolbar_focus' => __('Jump to tool buttons - Alt+Q, Jump to editor - Alt-Z, Jump to element path - Alt-X'),
+			'more_colors' => __('More colors'),
+			'shortcuts_desc' => __('Accessibility Help'),
+			'help_shortcut' => __('. Press ALT F10 for toolbar. Press ALT 0 for help.'),
+			'rich_text_area' => __('Rich Text Area'),
+			'toolbar' => __('Toolbar'),
+			'anchor_delta_width' => '40',
+			'anchor_delta_height' => '50',
+			'charmap_delta_width' => '50',
+			'charmap_delta_height' => '70',
+		));
+		$tb->addI18n('advanced_dlg',array(
+			'about_title' => __('About TinyMCE'),
+			'about_general' => __('About'),
+			'about_help' => __('Help'),
+			'about_license' => __('License'),
+			'about_plugins' => __('Plugins'),
+			'about_plugin' => __('Plugin'),
+			'about_author' => __('Author'),
+			'about_version' => __('Version'),
+			'about_loaded' => __('Loaded plugins'),
+			'anchor_title' => __('Insert/edit anchor'),
+			'anchor_name' => __('Anchor name'),
+			'anchor_invalid' => __('Please specify a valid anchor name.'),
+			'code_title' => __('HTML Source Editor'),
+			'code_wordwrap' => __('Word wrap'),
+			'charmap_title' => __('Select custom character'),
+			'accessibility_help' => __('Accessibility Help'),
+			'accessibility_usage_title' => __('General Usage')
+		));
+		$tb->addI18n('paste',array(
+			'paste_text_desc' => __('Paste as Plain Text'),
+			'paste_word_desc' => __('Paste from Word'),
+			'selectall_desc' => __('Select All'),
+			'plaintext_mode_sticky' => __('Paste is now in plain text mode. Click again to toggle back to regular paste mode. After you paste something you will be returned to regular paste mode.'),
+			'plaintext_mode' => __('Paste is now in plain text mode. Click again to toggle back to regular paste mode.')
+		));
+		$tb->addI18n('paste_dlg',array(
+			'text_title' => __('Use CTRL+V on your keyboard to paste the text into the window.'),
+			'text_linebreaks' => __('Keep linebreaks'),
+			'word_title' => __('Use CTRL+V on your keyboard to paste the text into the window.')
+		));
+		$tb->addI18n('fullscreen',array('desc' => __('Toggle fullscreen mode')));
+		$tb->addI18n('aria',array('rich_text_area' => __('Rich Text Area')));
+		$tb->addI18n('dcControls',array(
+			'inlinecode_desc' => __('Code'),
+			'quote_desc' => __('Quote'),
+			'link_desc' => __('Link'),
+			'media_desc' => __('Add media from media manager'),
+			'webmedia_desc' => __('Add media from web')
+		));
+		$tb->addI18n('dcControls_dlg',array(
+			'provider_not_supported' => __('Provider not supported.'),
+			'webmedia_no_information' => __('Impossible to get media information. Please, try again later.'),
+			'no_media_loaded' => __('No media loaded. Please, load one beore inserting.')
+		));
+		
 		$res =
-		'<link rel="stylesheet" type="text/css" href="style/jsToolBar/jsToolBar.css" />'.
-		'<script type="text/javascript" src="js/jsToolBar/jsToolBar.js"></script>';
-		
-		if (isset($GLOBALS['core']->auth) && $GLOBALS['core']->auth->getOption('enable_wysiwyg')) {
-			$res .= '<script type="text/javascript" src="js/jsToolBar/jsToolBar.wysiwyg.js"></script>';
-		}
-		
-		$res .=
-		'<script type="text/javascript" src="js/jsToolBar/jsToolBar.dotclear.js"></script>'.
+		'<script type="text/javascript" src="js/tiny_mce/tiny_mce.js"></script>'.
+		'<script type="text/javascript" src="js/dcToolBar.js"></script>'.
 		'<script type="text/javascript">'."\n".
 		"//<![CDATA[\n".
-		"jsToolBar.prototype.dialog_url = 'popup.php'; ".
-		"jsToolBar.prototype.iframe_css = '".
-			'body{'.
-				'font: 12px "DejaVu Sans","Lucida Grande","Lucida Sans Unicode",Arial,sans-serif;'.
-				'color : #000;'.
-				'background: #f9f9f9;'.
-				'margin: 0;'.
-				'padding : 2px;'.
-				'border: none;'.
-				(l10n::getTextDirection($GLOBALS['_lang']) == 'rtl' ? 'direction:rtl;' : '').
-			'}'.
-			'pre, code, kbd, samp {'.
-				'font-family:"Courier New",Courier,monospace;'.
-				'font-size : 1.1em;'.
-			'}'.
-			'code {'.
-				'color : #666;'.
-				'font-weight : bold;'.
-			'}'.
-			'body > p:first-child {'.
-				'margin-top: 0;'.
-			'}'.
-		"'; ".
-		"jsToolBar.prototype.base_url = '".html::escapeJS($GLOBALS['core']->blog->host)."'; ".
-		"jsToolBar.prototype.switcher_visual_title = '".html::escapeJS(__('visual'))."'; ".
-		"jsToolBar.prototype.switcher_source_title = '".html::escapeJS(__('source'))."'; ".
-		"jsToolBar.prototype.legend_msg = '".
-		html::escapeJS(__('You can use the following shortcuts to format your text.'))."'; ".
-		"jsToolBar.prototype.elements.blocks.options.none = '".html::escapeJS(__('-- none --'))."'; ".
-		"jsToolBar.prototype.elements.blocks.options.nonebis = '".html::escapeJS(__('-- block format --'))."'; ".
-		"jsToolBar.prototype.elements.blocks.options.p = '".html::escapeJS(__('Paragraph'))."'; ".
-		"jsToolBar.prototype.elements.blocks.options.h1 = '".html::escapeJS(__('Level 1 header'))."'; ".
-		"jsToolBar.prototype.elements.blocks.options.h2 = '".html::escapeJS(__('Level 2 header'))."'; ".
-		"jsToolBar.prototype.elements.blocks.options.h3 = '".html::escapeJS(__('Level 3 header'))."'; ".
-		"jsToolBar.prototype.elements.blocks.options.h4 = '".html::escapeJS(__('Level 4 header'))."'; ".
-		"jsToolBar.prototype.elements.blocks.options.h5 = '".html::escapeJS(__('Level 5 header'))."'; ".
-		"jsToolBar.prototype.elements.blocks.options.h6 = '".html::escapeJS(__('Level 6 header'))."'; ".
-		"jsToolBar.prototype.elements.strong.title = '".html::escapeJS(__('Strong emphasis'))."'; ".
-		"jsToolBar.prototype.elements.em.title = '".html::escapeJS(__('Emphasis'))."'; ".
-		"jsToolBar.prototype.elements.ins.title = '".html::escapeJS(__('Inserted'))."'; ".
-		"jsToolBar.prototype.elements.del.title = '".html::escapeJS(__('Deleted'))."'; ".
-		"jsToolBar.prototype.elements.quote.title = '".html::escapeJS(__('Inline quote'))."'; ".
-		"jsToolBar.prototype.elements.code.title = '".html::escapeJS(__('Code'))."'; ".
-		"jsToolBar.prototype.elements.br.title = '".html::escapeJS(__('Line break'))."'; ".
-		"jsToolBar.prototype.elements.blockquote.title = '".html::escapeJS(__('Blockquote'))."'; ".
-		"jsToolBar.prototype.elements.pre.title = '".html::escapeJS(__('Preformated text'))."'; ".
-		"jsToolBar.prototype.elements.ul.title = '".html::escapeJS(__('Unordered list'))."'; ".
-		"jsToolBar.prototype.elements.ol.title = '".html::escapeJS(__('Ordered list'))."'; ".
-		
-		"jsToolBar.prototype.elements.link.title = '".html::escapeJS(__('Link'))."'; ".
-		"jsToolBar.prototype.elements.link.href_prompt = '".html::escapeJS(__('URL?'))."'; ".
-		"jsToolBar.prototype.elements.link.hreflang_prompt = '".html::escapeJS(__('Language?'))."'; ".
-		
-		"jsToolBar.prototype.elements.img.title = '".html::escapeJS(__('External image'))."'; ".
-		"jsToolBar.prototype.elements.img.src_prompt = '".html::escapeJS(__('URL?'))."'; ".
-		
-		"jsToolBar.prototype.elements.img_select.title = '".html::escapeJS(__('Media chooser'))."'; ".
-		"jsToolBar.prototype.elements.post_link.title = '".html::escapeJS(__('Link to an entry'))."'; ";
-		
-		if (!$GLOBALS['core']->auth->check('media,media_admin',$GLOBALS['core']->blog->id)) {
-			$res .= "jsToolBar.prototype.elements.img_select.disabled = true;\n";
-		}
-		
-		$res .=
+		$tb->getJS().
 		"\n//]]>\n".
 		"</script>\n";
@@ -678,17 +879,21 @@
 	{
 		return
-		'<script type="text/javascript" src="js/tool-man/core.js"></script>'.
-		'<script type="text/javascript" src="js/tool-man/events.js"></script>'.
-		'<script type="text/javascript" src="js/tool-man/css.js"></script>'.
-		'<script type="text/javascript" src="js/tool-man/coordinates.js"></script>'.
-		'<script type="text/javascript" src="js/tool-man/drag.js"></script>'.
-		'<script type="text/javascript" src="js/tool-man/dragsort.js"></script>'.
-		'<script type="text/javascript" src="js/dragsort-tablerows.js"></script>';
+		self::jsLoad('js/tool-man/core.js').
+		self::jsLoad('js/tool-man/events.js').
+		self::jsLoad('js/tool-man/css.js').
+		self::jsLoad('js/tool-man/coordinates.js').
+		self::jsLoad('js/tool-man/drag.js').
+		self::jsLoad('js/tool-man/dragsort.js').
+		self::jsLoad('js/dragsort-tablerows.js');
 	}
 	
 	public static function jsMetaEditor()
 	{
-		return
-		'<script type="text/javascript" src="js/meta-editor.js"></script>';
+		return self::jsLoad('js/meta-editor.js');
+	}
+	
+	public static function jsOEmbed()
+	{
+		return self::jsLoad('js/jquery/jquery.oembed.js');
 	}
 }
Index: inc/admin/lib.pager.php
===================================================================
--- inc/admin/lib.pager.php	(revision 531)
+++ inc/admin/lib.pager.php	(revision 326)
@@ -4,5 +4,5 @@
 # This file is part of Dotclear 2.
 #
-# Copyright (c) 2003-2011 Olivier Meunier & Association Dotclear
+# Copyright (c) 2003-2010 Olivier Meunier & Association Dotclear
 # Licensed under the GPL version 2.0 license.
 # See LICENSE file or
@@ -12,12 +12,71 @@
 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 adminGenericColumn
+{
+	protected $core;
+	protected $rs;
+	protected $id;
+	protected $title;
+	protected $callback;
+	protected $html;
+	protected $visibility;
+	
+	public function __construct($id,$title,$callback,$html = null)
+	{
+		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_string($html)) {
+			$html = '';
+		}
+		if (!empty($html)) {
+			$html = ' '.$html;
+		}
+		
+		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();
+			if (!$p || $p->name != 'adminGenericList') {
+				throw new Exception(__('Callback class should be inherited of adminGenericList class'));
+			}
+		}
+		catch (Exception $e) {
+			throw new Exception(sprintf(__('Invalid column callback: %s'),$e->getMessage()));
+		}
+		
+		$this->id = $id;
+		$this->title = $title;
+		$this->callback = $callback;
+		$this->html = $html;
+		$this->visibility = true;
+	}
+	
+	public function getInfo($k)
+	{
+		return property_exists(get_class($this),$k) ? $this->{$k} : null;
+	}
+	
+	public function setVisibility($visibility)
+	{
+		if (is_bool($visibility)) {
+			$this->visibility = $visibility;
+		}
+	}
+	
+	public function isVisible()
+	{
+		return $this->visibility;
+	}
+}
+
 class adminGenericList
 {
@@ -25,41 +84,408 @@
 	protected $rs;
 	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
-	*/
+	protected $columns;
+	
 	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;
 		$this->rs_count = $rs_count;
+		$this->context = get_class($this);
+		$this->columns = array();
+		$this->form_prefix = 'col_%s';
+		$this->form_trigger = 'add_filter';
+		
 		$this->html_prev = __('&#171;prev.');
 		$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);
-		}
+		
+		# Post columns
+		$this->addColumn('adminPostList','title',__('Title'),array('adminPostList','getTitle'));
+		$this->addColumn('adminPostList','date',__('Date'),array('adminPostList','getDate'));
+		$this->addColumn('adminPostList','category',__('Category'),array('adminPostList','getCategory'));
+		$this->addColumn('adminPostList','author',__('Author'),array('adminPostList','getAuthor'));
+		$this->addColumn('adminPostList','comment',__('Comments'),array('adminPostList','getComments'));
+		$this->addColumn('adminPostList','trackback',__('Trackbacks'),array('adminPostList','getTrackbacks'));
+		$this->addColumn('adminPostList','status',__('Status'),array('adminPostList','getStatus'));
+		
+		# Post (mini list) columns
+		$this->addColumn('adminPostMiniList','title',__('Title'),array('adminPostList','getTitle'));
+		$this->addColumn('adminPostMiniList','date',__('Date'),array('adminPostList','getDate'));
+		$this->addColumn('adminPostMiniList','author',__('Author'),array('adminPostList','getAuthor'));
+		$this->addColumn('adminPostMiniList','status',__('Status'),array('adminPostList','getStatus'));
+		
+		# Comment columns
+		$this->addColumn('adminCommentList','title',__('Title'),array('adminCommentList','getTitle'));
+		$this->addColumn('adminCommentList','date',__('Date'),array('adminCommentList','getDate'));
+		$this->addColumn('adminCommentList','author',__('Author'),array('adminCommentList','getAuthor'));
+		$this->addColumn('adminCommentList','type',__('Type'),array('adminCommentList','getType'));
+		$this->addColumn('adminCommentList','status',__('Status'),array('adminCommentList','getStatus'));
+		$this->addColumn('adminCommentList','edit','',array('adminCommentList','getEdit'));
+		
+		# User columns
+		$this->addColumn('adminUserList','username',__('Username'),array('adminUserList','getUserName'));
+		$this->addColumn('adminUserList','firstname',__('First name'),array('adminUserList','getFirstName'));
+		$this->addColumn('adminUserList','lastname',__('Last name'),array('adminUserList','getLastName'));
+		$this->addColumn('adminUserList','displayname',__('Display name'),array('adminUserList','getDisplayName'));
+		$this->addColumn('adminUserList','entries',__('Entries'),array('adminUserList','getEntries'));
+		
+		$core->callBehavior('adminGenericListConstruct',$this);
+		
+		$this->setColumnsVisibility();
+	}
+	
+	public function addColumn($context,$id,$title,$callback,$html = null)
+	{
+		try {
+			if (!array_key_exists($context,$this->columns)) {
+				$this->columns[$context] = array();
+			}
+			
+			$c = new adminGenericColumn($id,$title,$callback,$html);
+			$this->columns[$context][$c->getInfo('id')] = $c;
+		}
+		catch (Exception $e) {
+			if (DC_DEBUG) {
+				$this->core->error->add($e->getMessage());
+			}
+		}
+	}
+	
+	public function setColumnsVisibility()
+	{
+		$ws = $this->core->auth->user_prefs->addWorkspace('lists');
+		
+		$user_pref = !is_null($ws->{$this->context}) ? unserialize($ws->{$this->context}) : array();
+		
+		foreach ($this->columns[$this->context] as $k => $v) {
+			$visibility =  array_key_exists($k,$user_pref) ? $user_pref[$k] : true;
+			if (array_key_exists($this->form_trigger,$_REQUEST)) {
+				$key = sprintf($this->form_prefix,$k);
+				$visibility = !array_key_exists($key,$_REQUEST) ? false : true;
+			}
+			$v->setVisibility($visibility);
+			$user_pref[$k] = $visibility;
+		}
+		
+		if (array_key_exists($this->form_trigger,$_REQUEST)) {
+			$this->core->auth->user_prefs->lists->put($this->context,serialize($user_pref),'string');
+		}
+	}
+	
+	public function getColumnsForm()
+	{
+		$block = 
+		'<h3>'.__('Displayed information').'</h3>'.
+		'<ul>%s</ul>';
+		
+		$list = array();
+		
+		foreach ($this->columns[$this->context] as $k => $v) {
+			$col_id = sprintf($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()));
+			
+			array_push($list,$col_html);
+		}
+		
+		return !empty($list) ? sprintf($block,implode('',$list)) : '';
+	}
+	
+	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="maximal clear"><tr>';
+			
+			foreach ($this->columns[$this->context] as $k => $v) {
+				if ($v->isVisible()) {
+					$html_block .= sprintf('<th%s>%s</th>',$v->getInfo('html'),$v->getInfo('title'));
+				}
+			}
+			
+			$html_block .= '</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->displayLine();
+			}
+			
+			echo $blocks[1];
+			
+			echo '<p>'.__('Page(s)').' : '.$pager->getLinks().'</p>';
+		}
+	}
+	
+	private function displayLine()
+	{
+		$res = '';
+		
+		foreach ($this->columns[$this->context] as $k => $v) {
+			if ($v->isVisible()) {
+				$c = $v->getInfo('callback');
+				$func = $c[1];
+				$res .= $this->{$c[1]}();
+			}
+		}
+		
+		return sprintf($this->getDefaultLine(),$res);
+	}
+	
+	protected function getDefaultLine()
+	{
+		return '<tr class="line">%s</tr>';
+	}
+}
+
+class adminPostList extends adminGenericList
+{
+	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
+		'<td 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></td>';
+	}
+	
+	protected function getDate()
+	{
+		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>';
+	}
+}
+
+class adminPostMiniList extends adminPostList
+{
+	protected function getTitle()
+	{
+		return
+		'<td 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>';
+	}
+}
+
+class adminCommentList extends adminGenericList
+{
+	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
+		'<td 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).')' : '').'</td>';
+	}
+	
+	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>';
+	}
+}
+
+class adminUserList extends adminGenericList
+{
+	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
+		'<td 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.'</td>';
+	}
+	
+	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>';
 	}
 }
