Index: inc/admin/class.dc.filter.php
===================================================================
--- inc/admin/class.dc.filter.php	(revision 526)
+++ inc/admin/class.dc.filter.php	(revision 526)
@@ -0,0 +1,556 @@
+<?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 Dotclear FilterSet class.
+
+Dotclear FilterSet handles filters and columns when displaying items lists.
+*/
+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;
+		$this->form_prefix=$form_prefix;
+		$this->filters = array();
+		$this->action = $action;
+	}
+
+	/**
+	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);
+		$this->filters[$filter->id] = $filter;
+		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) {
+		$this->hideform = true;
+		if (isset($form_data['clear_filters'])) {
+			$this->saveFilters();
+			return;
+		}
+		if (!isset($form_data['apply'])) {
+			$this->loadFilters();
+		}
+		foreach ($this->filters as $filter) {
+			$filter->setFormValues ($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
+	*/
+	public function setColumnsForm($html)
+	{
+		$this->columns_form = $html;
+	}
+	
+	/**
+	Returns form fields as hidden fields
+	
+	@return	<b>string</b>	the corresponding html code
+	*/
+	public function getFormFieldsAsHidden() {
+		$ret='';
+		foreach ($this->filters as $filter) {
+			$ret.= $filter->getFormFieldAsHidden();
+		}
+		return $ret;
+	}
+
+	/**
+	Retrieves filterset generated form
+	
+	@param	method	<b>string</b>		form method to use (default: "get")
+	*/
+	public function getForm($method="get") {
+		$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>';
+		$ret .=
+			'<div class="two-cols">'.
+			'<form id="filters" action="'.$this->action.'" method="'.$method.'"'.$formclass.'>'.
+			'<div class="col70">'.
+			'<h3>'.__('Entries filters').'</h3>';
+			
+		$count=0;
+		$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++;
+			}
+			$ret .= '</ul>';
+		}
+		$ret .= 
+			'<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" />'.
+			'</p>'.
+			'</div>'.
+			'<div class="col30">'.
+			$this->columns_form.
+			'</div>'.
+			'<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")
+	
+	*/
+	public function applyFilters($params) {
+		$filtered = false;
+		foreach ($this->filters as $filter) {
+			if ($filter->isEnabled()) {
+				$filter->applyFilter($params);
+				$filtered = true;
+			}
+		}
+		return $filtered;
+	}
+	
+}
+
+
+/**
+@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) {
+		$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;
+	}
+	
+	/**
+	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;
+	}
+	
+	/**
+	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) {
+		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) {
+		$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]);
+			}
+			$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='';
+		for ($cur=0; $cur < count($this->values); $cur++) {
+			$ret .= form::hidden($this->getFieldId($cur), $this->values[$cur]);
+		}
+	}
+	/**
+	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.'">'.
+				$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>';
+		}
+		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;
+	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->desc = $desc;
+		$this->verb = "is";
+		$this->values=array();
+	}
+	
+	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 setFormValues($form_data) {
+		parent::setFormValues($form_data);
+		if (isset($form_data[$this->field_id."_v"])) {
+			$this->verb = ($form_data[$this->field_id."_v"] == 'is') ? 'is' : 'isnot';
+		}
+	}
+
+	public function getFormFieldAsHidden () {
+		return parent::getFormFieldAsHidden().form::hidden($this->field_id."_v",$this->verb);
+	}
+
+	public function getFormFields($pos=0) {
+		
+		if ($pos == 0) {
+			$desc = $this->desc.' : ';
+			$labelclass="filter-title";
+		} else {
+			$desc = __('or');
+			$labelclass = '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').'"');
+	}
+	
+	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;
+	}
+}
+
+/**
+@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,$extra=array()) {
+		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->options = $options;
+		$this->values=array();
+		$this->size = $size;
+		$this->max = $max;
+	}
+	
+	
+	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];
+	}
+	
+}
+?>
Index: inc/admin/class.dc.list.php
===================================================================
--- inc/admin/class.dc.list.php	(revision 521)
+++ inc/admin/class.dc.list.php	(revision 521)
@@ -0,0 +1,1080 @@
+<?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.menu.php
===================================================================
--- inc/admin/class.dc.menu.php	(revision 270)
+++ inc/admin/class.dc.menu.php	(revision 3)
@@ -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
Index: inc/admin/lib.dc.page.php
===================================================================
--- inc/admin/lib.dc.page.php	(revision 489)
+++ inc/admin/lib.dc.page.php	(revision 224)
@@ -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
@@ -16,6 +16,4 @@
 class dcPage
 {
-	private static $loaded_js=array();
-
 	# Auth check
 	public static function check($permissions)
@@ -75,5 +73,5 @@
 			__('Blogs:').' '.
 			$core->formNonce().
-			form::combo('switchblog',$blogs,$core->blog->id).
+			form::combo('switchblog',$blogs,$core->blog->id,	'',1).
 			'</label>'.
 			'<noscript><div><input type="submit" value="'.__('ok').'" /></div></noscript>';
@@ -98,14 +96,9 @@
 		
 		self::jsLoadIE7().
-		'  	<link rel="stylesheet" href="style/default.css" type="text/css" media="screen" />'."\n"; 
+		'  <style type="text/css">'."\n". 
+		'  @import "style/default.css";'."\n".
+		"  </style>\n";
 		if (l10n::getTextDirection($GLOBALS['_lang']) == 'rtl') {
-			echo
-		'  	<link rel="stylesheet" href="style/default-rtl.css" type="text/css" media="screen" />'."\n"; 
-		}
-
-		$core->auth->user_prefs->addWorkspace('interface');
-		$user_ui_hide_std_favicon = $core->auth->user_prefs->interface->hide_std_favicon;
-		if (!$user_ui_hide_std_favicon) {
-			echo '<link rel="icon" type="image/png" href="images/favicon.png" />';
+			echo '  <style type="text/css">'."\n".'  @import "style/default-rtl.css";'."\n"."  </style>\n";
 		}
 		
@@ -123,21 +116,17 @@
 		'">'."\n".
 		
-		'<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".
-		'<div id="top"><h1><a href="index.php">'.DC_VENDOR_NAME.'</a></h1></div>'."\n";	
-		
-		echo
-		'<div id="info-boxes">'.
-		'<div id="info-box1">'.
-		'<form action="index.php" method="post">'.
+		'<div id="top"><h1><a href="index.php">'.DC_VENDOR_NAME.'</a></h1></div>'."\n";
+		
+		
+		echo
+		'<div id="info-box">'.
+		'<form action="index.php" method="post"><div>'.
 		$blog_box.
 		'<a href="'.$core->blog->url.'" onclick="window.open(this.href);return false;" title="'.__('Go to site').' ('.__('new window').')'.'">'.__('Go to site').' <img src="images/outgoing.png" alt="" /></a>'.
-		'</form>'.
+		'</div></form>'.
 		'</div>'.
-		'<div id="info-box2">'.
-		'<a'.(preg_match('/index.php$/',$_SERVER['REQUEST_URI']) ? ' class="active"' : '').' href="index.php">'.__('My dashboard').'</a>'.
-		'<span> | </span><a'.(preg_match('/preferences.php(\?.*)?$/',$_SERVER['REQUEST_URI']) ? ' class="active"' : '').' href="preferences.php">'.__('My preferences').'</a>'.
-		'<span> | </span><a href="index.php?logout=1" class="logout">'.sprintf(__('Logout %s'),$core->auth->userID()).' <img src="images/logout.png" alt="" /></a>'.
-		'</div>'.
+		'<div id="info-box2"><div>'.
+		' '.__('User:').' <strong>'.$core->auth->userID().'</strong>'.
+		' - <a href="index.php?logout=1" class="logout">'.__('Logout').' <img src="images/logout.png" alt="" /></a>'.
 		'</div>'.
 		'</div>';
@@ -215,8 +204,9 @@
 		
 		self::jsLoadIE7().
-		'  	<link rel="stylesheet" href="style/default.css" type="text/css" media="screen" />'."\n"; 
+		'  <style type="text/css">'."\n". 
+		'  @import "style/default.css";'."\n".
+		"  </style>\n";
 		if (l10n::getTextDirection($GLOBALS['_lang']) == 'rtl') {
-			echo
-			'  	<link rel="stylesheet" href="style/default-rtl.css" type="text/css" media="screen" />'."\n"; 
+			echo '  <style type="text/css">'."\n".'  @import "style/default-rtl.css";'."\n"."  </style>\n";
 		}
 		
@@ -274,7 +264,7 @@
 			} else {
 				$prof_url = http::getSelfURI();
-				$prof_url .= (strpos($prof_url,'?') === false) ? '?' : '&';
+				$prof_url .= (strpos($prof_url,'?') === false) ? '?' : '&amp;';
 				$prof_url .= 'XDEBUG_PROFILE';
-				$res .= '<p><a href="'.html::escapeURL($prof_url).'">Trigger profiler</a></p>';
+				$res .= '<p><a href="'.$prof_url.'">Trigger profiler</a></p>';
 			}
 			
@@ -353,9 +343,5 @@
 	public static function jsLoad($src)
 	{
-		$escaped_src = html::escapeHTML($src);
-		if (!isset(self::$loaded_js[$escaped_src])) {
-			self::$loaded_js[$escaped_src]=true;
-			return '<script type="text/javascript" src="'.$escaped_src.'"></script>'."\n";
-		}
+		return '<script type="text/javascript" src="'.html::escapeHTML($src).'"></script>'."\n";
 	}
 	
@@ -372,5 +358,4 @@
 		self::jsLoad('js/jquery/jquery.bgFade.js').
 		self::jsLoad('js/common.js').
-		self::jsLoad('js/prelude.js').
 		
 		'<script type="text/javascript">'."\n".
Index: inc/admin/lib.pager.php
===================================================================
--- inc/admin/lib.pager.php	(revision 270)
+++ inc/admin/lib.pager.php	(revision 531)
@@ -12,4 +12,12 @@
 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
 {
@@ -18,6 +26,17 @@
 	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;
@@ -26,395 +45,23 @@
 		$this->html_next = __('next&#187;');
 	}
-}
-
-class adminPostList extends adminGenericList
-{
-	public function display($page,$nb_per_page,$enclose_block='')
+	
+	/**
+	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 ($this->rs->isEmpty())
-		{
-			echo '<p><strong>'.__('No entry').'</strong></p>';
+		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);
 		}
-		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('user_id[]'),$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 489)
+++ inc/admin/prepend.php	(revision 185)
@@ -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
@@ -21,17 +21,4 @@
 
 define('DC_CONTEXT_ADMIN',true);
-
-function dc_valid_fav($url) {
-	global $core;
-
-	if (preg_match('#plugin\.php\?p=([^&]+)#',$url,$matches)) {
-		if (isset($matches[1])) {
-			if (!$core->plugins->moduleExists($matches[1])) {
-				return false;
-			}
-		}
-	}
-	return true;
-}
 
 function dc_prepare_url($url) {
@@ -198,43 +185,43 @@
 	# NB : '*' in permissions means any, null means super admin only
 	
-	$_fav['prefs'] = new ArrayObject(array('prefs','My preferences','preferences.php',
+	$_fav['prefs'] = new ArrayObject(array('prefs',__('My preferences'),'preferences.php',
 		'images/menu/user-pref.png','images/menu/user-pref-b.png',
 		'*',null,null));
 
-	$_fav['new_post'] = new ArrayObject(array('new_post','New entry','post.php',
+	$_fav['new_post'] = new ArrayObject(array('new_post',__('New entry'),'post.php',
 		'images/menu/edit.png','images/menu/edit-b.png',
 		'usage,contentadmin',null,'menu-new-post'));
-	$_fav['posts'] = new ArrayObject(array('posts','Entries','posts.php',
+	$_fav['posts'] = new ArrayObject(array('posts',__('Entries'),'posts.php',
 		'images/menu/entries.png','images/menu/entries-b.png',
 		'usage,contentadmin',null,null));
-	$_fav['comments'] = new ArrayObject(array('comments','Comments','comments.php',
+	$_fav['comments'] = new ArrayObject(array('comments',__('Comments'),'comments.php',
 		'images/menu/comments.png','images/menu/comments-b.png',
 		'usage,contentadmin',null,null));
-	$_fav['search'] = new ArrayObject(array('search','Search','search.php',
+	$_fav['search'] = new ArrayObject(array('search',__('Search'),'search.php',
 		'images/menu/search.png','images/menu/search-b.png',
 		'usage,contentadmin',null,null));
-	$_fav['categories'] = new ArrayObject(array('categories','Categories','categories.php',
+	$_fav['categories'] = new ArrayObject(array('categories',__('Categories'),'categories.php',
 		'images/menu/categories.png','images/menu/categories-b.png',
 		'categories',null,null));
-	$_fav['media'] = new ArrayObject(array('media','Media manager','media.php',
+	$_fav['media'] = new ArrayObject(array('media',__('Media manager'),'media.php',
 		'images/menu/media.png','images/menu/media-b.png',
 		'media,media_admin',null,null));
-	$_fav['blog_pref'] = new ArrayObject(array('blog_pref','Blog settings','blog_pref.php',
+	$_fav['blog_pref'] = new ArrayObject(array('blog_pref',__('Blog settings'),'blog_pref.php',
 		'images/menu/blog-pref.png','images/menu/blog-pref-b.png',
 		'admin',null,null));
-	$_fav['blog_theme'] = new ArrayObject(array('blog_theme','Blog appearance','blog_theme.php',
+	$_fav['blog_theme'] = new ArrayObject(array('blog_theme',__('Blog appearance'),'blog_theme.php',
 		'images/menu/themes.png','images/menu/blog-theme-b.png',
 		'admin',null,null));
 
-	$_fav['blogs'] = new ArrayObject(array('blogs','Blogs','blogs.php',
+	$_fav['blogs'] = new ArrayObject(array('blogs',__('Blogs'),'blogs.php',
 		'images/menu/blogs.png','images/menu/blogs-b.png',
 		'usage,contentadmin',null,null));
-	$_fav['users'] = new ArrayObject(array('users','Users','users.php',
+	$_fav['users'] = new ArrayObject(array('users',__('Users'),'users.php',
 		'images/menu/users.png','images/menu/users-b.png',
 		null,null,null));
-	$_fav['plugins'] = new ArrayObject(array('plugins','Plugins','plugins.php',
+	$_fav['plugins'] = new ArrayObject(array('plugins',__('Plugins'),'plugins.php',
 		'images/menu/plugins.png','images/menu/plugins-b.png',
 		null,null,null));
-	$_fav['langs'] = new ArrayObject(array('langs','Languages','langs.php',
+	$_fav['langs'] = new ArrayObject(array('langs',__('Languages'),'langs.php',
 		'images/menu/langs.png','images/menu/langs-b.png',
 		null,null,null));
@@ -267,4 +254,11 @@
 	}
 */
+	$_menu['Dashboard']->prependItem(__('My preferences'),'preferences.php','images/menu/user-pref.png',
+		preg_match('/preferences.php(\?.*)?$/',$_SERVER['REQUEST_URI']),
+		true);
+	$_menu['Dashboard']->prependItem(__('Dashboard'),'index.php','images/menu/dashboard.png',
+		preg_match('/index.php$/',$_SERVER['REQUEST_URI']),
+		$core->auth->check('usage,contentadmin',$core->blog->id));
+	
 	$_menu['Blog']->prependItem(__('Blog appearance'),'blog_theme.php','images/menu/themes.png',
 		preg_match('/blog_theme.php(\?.*)?$/',$_SERVER['REQUEST_URI']),
@@ -315,11 +309,9 @@
 		// User favorites only
 		if (!$v['global']) {
+			$count++;
 			$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']);
-			}
+			$_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']);
 		}
 	}	
@@ -327,11 +319,9 @@
 		// Global favorites if any
 		foreach ($ws->dumpPrefs() as $k => $v) {
+			$count++;
 			$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']);
-			}
+			$_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']);
 		}
 	}
Index: inc/config.php.in
===================================================================
--- inc/config.php.in	(revision 362)
+++ inc/config.php.in	(revision 0)
@@ -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
@@ -40,7 +40,4 @@
 define('DC_ADMIN_URL','');
 
-// Admin mail from address. For password recovery and such.
-define('DC_ADMIN_MAILFROM','');
-
 // Cookie's name
 define('DC_SESSION_NAME','dcxd');
Index: inc/core/class.dc.auth.php
===================================================================
--- inc/core/class.dc.auth.php	(revision 527)
+++ inc/core/class.dc.auth.php	(revision 3)
@@ -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
@@ -13,52 +13,37 @@
 
 /**
-* @ingroup DC_CORE
-* @nosubgrouping
-* @brief Authentication and user credentials management
-* 
-* dcAuth is a class used to handle everything related to user authentication
-* and credentials. Object is provided by dcCore $auth property.
+@ingroup DC_CORE
+@nosubgrouping
+@brief Authentication and user credentials management
+
+dcAuth is a class used to handle everything related to user authentication
+and credentials. Object is provided by dcCore $auth property.
 */
 class dcAuth
 {
-	/** @var dcCore dcCore instance */
-	protected $core;
-	/** @var connection Database connection object */
-	protected $con;
-	
-	/** @var string User table name */
-	protected $user_table;
-	/** @var string Perm table name */
-	protected $perm_table;
-	
-	/** @var string Current user ID */
-	protected $user_id;
-	/** @var array Array with user information */
-	protected $user_info = array();
-	/** @var array Array with user options */
-	protected $user_options = array();
-	/** @var boolean User must change his password after login */
-	protected $user_change_pwd;
-	/** @var boolean User is super admin */
-	protected $user_admin;
-	/** @var array Permissions for each blog */
-	protected $permissions = array();
-	/** @var boolean User can change its password */
-	protected $allow_pass_change = true;
-	/** @var array List of blogs on which the user has permissions */
-	protected $blogs = array();
-	/** @var integer Count of user blogs */
-	public $blog_count = null;
-	
-	/** @var array Permission types */
-	protected $perm_types;
-	
-	/** @var dcPrefs dcPrefs object */
-	public $user_prefs;
-	
-	/**
-	* Class constructor. Takes dcCore object as single argument.
-	* 
-	* @param dcCore	$core		dcCore object
+	protected $core;		///< <b>dcCore</b> dcCore instance
+	protected $con;		///< <b>connection</b> Database connection object
+	
+	protected $user_table;	///< <b>string</b>	User table name
+	protected $perm_table;	///< <b>string</b>	Perm table name
+	
+	protected $user_id;					///< <b>string</b>		Current user ID
+	protected $user_info = array();		///< <b>array</b>		Array with user information
+	protected $user_options = array();		///< <b>array</b>		Array with user options
+	protected $user_change_pwd;			///< <b>boolean</b>		User must change his password after login
+	protected $user_admin;				///< <b>boolean</b>		User is super admin
+	protected $permissions = array();		///< <b>array</b>		Permissions for each blog
+	protected $allow_pass_change = true;	///< <b>boolean</b>		User can change its password
+	protected $blogs = array();			///< <b>array</b>		List of blogs on which the user has permissions
+	public $blog_count = null;				///< <b>integer</b>		Count of user blogs
+	
+	protected $perm_types;	///< <b>array</b> Permission types
+	
+	public $user_prefs;	///< <b>dcPrefs</b>	dcPrefs object
+	
+	/**
+	Class constructor. Takes dcCore object as single argument.
+	
+	@param	core		<b>dcCore</b>		dcCore object
 	*/
 	public function __construct($core)
@@ -85,15 +70,14 @@
 	//@{
 	/**
-	* Checks if user exists and can log in. <var>$pwd</var> argument is optionnal
-	* while you may need to check user without password. This method will create
-	* credentials and populate all needed object properties.
-	* 
-	* @param string	$user_id		User ID
-	* @param string	$pwd			User password
-	* @param string	$user_key		User key check
-	* @param boolean	$check_blog	checks if user is associated to a blog or not.
-	* @return boolean
-	*/
-	public function checkUser($user_id, $pwd=null, $user_key=null, $check_blog=true)
+	Checks if user exists and can log in. <var>$pwd</var> argument is optionnal
+	while you may need to check user without password. This method will create
+	credentials and populate all needed object properties.
+	
+	@param	user_id	<b>string</b>		User ID
+	@param	pwd		<b>string</b>		User password
+	@param	user_key	<b>string</b>		User key check
+	@return	<b>boolean</b>
+	*/
+	public function checkUser($user_id, $pwd=null, $user_key=null)
 	{
 		# Check user and password
@@ -157,5 +141,5 @@
 		
 		# Get permissions on blogs
-		if ($check_blog && ($this->findUserBlog() === false)) {
+		if ($this->findUserBlog() === false) {
 			return false;
 		}
@@ -164,8 +148,8 @@
 	
 	/**
-	* This method only check current user password.
-	* 
-	* @param string	$pwd			User password
-	* @return boolean
+	This method only check current user password.
+	
+	@param	pwd		<b>string</b>		User password
+	@return	<b>boolean</b>
 	*/
 	public function checkPassword($pwd)
@@ -179,7 +163,7 @@
 	
 	/**
-	* This method checks if user session cookie exists
-	* 
-	* @return boolean
+	This method checks if user session cookie exists
+	
+	@return	<b>boolean</b>
 	*/
 	public function sessionExists()
@@ -189,7 +173,7 @@
 	
 	/**
-	* This method checks user session validity.
-	* 
-	* @return boolean
+	This method checks user session validity.
+	
+	@return	<b>boolean</b>
 	*/
 	public function checkSession($uid=null)
@@ -228,7 +212,7 @@
 	
 	/**
-	* Checks if user is super admin
-	* 
-	* @return boolean
+	Checks if user is super admin
+	
+	@return	<b>boolean</b>
 	*/
 	public function isSuperAdmin()
@@ -238,11 +222,11 @@
 	
 	/**
-	* Checks if user has permissions given in <var>$permissions</var> for blog
-	* <var>$blog_id</var>. <var>$permissions</var> is a coma separated list of
-	* permissions.
-	* 
-	* @param string	$permissions	Permissions list
-	* @param string	$blog_id		Blog ID
-	* @return boolean
+	Checks if user has permissions given in <var>$permissions</var> for blog
+	<var>$blog_id</var>. <var>$permissions</var> is a coma separated list of
+	permissions.
+	
+	@param	permissions	<b>string</b>		Permissions list
+	@param	blog_id		<b>string</b>		Blog ID
+	@return	<b>boolean</b>
 	*/
 	public function check($permissions,$blog_id)
@@ -273,7 +257,7 @@
 	
 	/**
-	* Returns true if user is allowed to change its password.
-	* 
-	* @return	boolean
+	Returns true if user is allowed to change its password.
+	
+	@return	<b>boolean</b>
 	*/
 	public function allowPassChange()
@@ -328,9 +312,8 @@
 	//@{
 	/**
-	* Calls $f function with super admin rights.
-	* Returns the function result.
-	* 
-	* @param callback	$f			Callback function
-	* @return mixed
+	Calls <var>$f</var> function with super admin rights.
+	
+	@param	f		<b>callback</b>	Callback function
+	@return	<b>mixed</b> Function result
 	*/
 	public function sudo($f)
@@ -363,12 +346,11 @@
 	//@{
 	/**
-	* Returns user permissions for a blog as an array which looks like:
-	* 
-	*  - [blog_id]
-	*    - [permission] => true
-	*    - ...
-	* 
-	* @param string	$blog_id		Blog ID
-	* @return array
+	Returns user permissions for a blog as an array which looks like:
+	
+	 - [blog_id]
+	   - [permission] => true
+	   - ...
+	
+	@return	<b>array</b>
 	*/
 	public function getPermissions($blog_id)
@@ -440,7 +422,7 @@
 	
 	/**
-	* Returns current user ID
-	* 
-	* @return string
+	Returns current user ID
+	
+	@return	<b>string</b>
 	*/
 	public function userID()
@@ -450,8 +432,8 @@
 	
 	/**
-	* Returns information about a user .
-	* 
-	* @param string	$n			Information name
-	* @return string
+	Returns information about a user .
+	
+	@param	n		<b>string</b>		Information name
+	@return	<b>string</b> Information value
 	*/
 	public function getInfo($n)
@@ -465,8 +447,8 @@
 	
 	/**
-	* Returns a specific user option
-	* 
-	* @param string	$n			Option name
-	* @return string
+	Returns a specific user option
+	
+	@param	n		<b>string</b>		Option name
+	@return	<b>string</b> Option value
 	*/
 	public function getOption($n)
@@ -479,7 +461,7 @@
 	
 	/**
-	* Returns all user options in an associative array.
-	* 
-	* @return array
+	Returns all user options in an associative array.
+	
+	@return	<b>array</b>
 	*/
 	public function getOptions()
@@ -492,8 +474,8 @@
 	//@{
 	/**
-	* Returns an array with permissions parsed from the string <var>$level</var>
-	* 
-	* @param string	$level		Permissions string
-	* @return array
+	Returns an array with permissions parsed from the string <var>$level</var>
+	
+	@param	level	<b>string</b>		Permissions string
+	@return	<b>array</b>
 	*/
 	public function parsePermissions($level)
@@ -510,7 +492,7 @@
 	
 	/**
-	* Returns <var>perm_types</var> property content.
-	* 
-	* @return array
+	Returns <var>perm_types</var> property content.
+	
+	@return	<b>array</b>
 	*/
 	public function getPermissionsTypes()
@@ -520,8 +502,8 @@
 	
 	/**
-	* Adds a new permission type.
-	* 
-	* @param string	$name		Permission name
-	* @param string	$title		Permission title
+	Adds a new permission type.
+	
+	@param	name		<b>string</b>		Permission name
+	@param	title	<b>string</b>		Permission title
 	*/
 	public function setPermissionType($name,$title)
@@ -534,10 +516,10 @@
 	//@{
 	/**
-	* Add a recover key to a specific user identified by its email and
-	* password.
-	* 
-	* @param string	$user_id		User ID
-	* @param string	$user_email	User Email
-	* @return string
+	Add a recover key to a specific user identified by its email and
+	password.
+	
+	@param	user_id		<b>string</b>		User ID
+	@param	user_email	<b>string</b>		User Email
+	@return	<b>string</b> Recover key
 	*/
 	public function setRecoverKey($user_id,$user_email)
@@ -565,12 +547,12 @@
 	
 	/**
-	* Creates a new user password using recovery key. Returns an array:
-	* 
-	* - user_email
-	* - user_id
-	* - new_pass
-	* 
-	* @param string	$recover_key	Recovery key
-	* @return array
+	Creates a new user password using recovery key. Returns an array:
+	
+	- user_email
+	- user_id
+	- new_pass
+	
+	@param	recover_key	<b>string</b>		Recovery key
+	@return	<b>array</b>
 	*/
 	public function recoverUserPassword($recover_key)
@@ -608,22 +590,22 @@
 	
 	/**
-	* Called after core->addUser
-	* @see dcCore::addUser
-	* @param cursor	$cur			User cursor
+	Called after core->addUser
+	@see		dcCore::addUser
+	@param	cur		<b>cursor</b>		User cursor
 	*/
 	public function afterAddUser($cur) {}
 	
 	/**
-	* Called after core->updUser
-	* @see dcCore::updUser
-	* @param string	$id			User ID
-	* @param cursor	$cur			User cursor
+	Called after core->updUser
+	@see		dcCore::updUser
+	@param	id		<b>string</b>		User ID
+	@param	cur		<b>cursor</b>		User cursor
 	*/
 	public function afterUpdUser($id,$cur) {}
 	
 	/**
-	* Called after core->delUser
-	* @see dcCore::delUser
-	* @param string	$id			User ID
+	Called after core->delUser
+	@see		dcCore::delUser
+	@param	id		<b>string</b>		User ID
 	*/
 	public function afterDelUser($id) {}
Index: inc/core/class.dc.blog.php
===================================================================
--- inc/core/class.dc.blog.php	(revision 407)
+++ inc/core/class.dc.blog.php	(revision 506)
@@ -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
@@ -21,36 +21,21 @@
 class dcBlog
 {
-	/** @var dcCore dcCore instance */
-	protected $core;
-	/** @var connection Database connection object */
-	public $con;
-	/** @var string Database table prefix */
-	public $prefix;
-	
-	/** @var string Blog ID */
-	public $id;
-	/** @var string Blog unique ID */
-	public $uid;
-	/** @var string Blog name */
-	public $name;
-	/** @var string Blog description */
-	public $desc;
-	/** @var string Blog URL */
-	public $url;
-	/** @var string Blog host */
-	public $host;
-	/** @var string Blog creation date */
-	public $creadt;
-	/** @var string Blog last update date */
-	public $upddt;
-	/** @var string Blog status */
-	public $status;
-	
-	/** @var dcSettings dcSettings object */
-	public $settings;
-	/** @var string Blog theme path */
-	public $themes_path;
-	/** @var string Blog public path */
-	public $public_path;
+	protected $core;	///< <b>dcCore</b> dcCore instance
+	public $con;		///< <b>connection</b>	Database connection object
+	public $prefix;	///< <b>string</b>		Database table prefix
+	
+	public $id;		///< <b>string</b>		Blog ID
+	public $uid;		///< <b>string</b>		Blog unique ID
+	public $name;		///< <b>string</b>		Blog name
+	public $desc;		///< <b>string</b>		Blog description
+	public $url;		///< <b>string</b>		Blog URL
+	public $host;		///< <b>string</b>		Blog host
+	public $creadt;	///< <b>string</b>		Blog creation date
+	public $upddt;		///< <b>string</b>		Blog last update date
+	public $status;	///< <b>string</b>		Blog status
+	
+	public $settings;		///< <b>dcSettings</b>	dcSettings object
+	public $themes_path;	///< <b>string</b>		Blog theme path
+	public $public_path;	///< <b>string</b>		Blog public path
 	
 	private $post_status = array();
@@ -59,6 +44,5 @@
 	private $categories;
 	
-	/** @var boolean Disallow entries password protection */
-	public $without_password = true;
+	public $without_password = true;	///< <b>boolean</b> Disallow entries password protection
 	
 	/**
@@ -104,4 +88,22 @@
 			$this->core->callBehavior('coreBlogConstruct',$this);
 		}
+	}
+	
+	/**
+	Returns sql statement for a given parameter. 
+	Computes in() or not_in lists in parameter
+
+	@param	params	<b>array</b> query parameters
+	@param	param_name	<b>array</b> parameter to check
+	@param	sql column	<b>array</b> sql column matching parameter name
+	
+	@return	<b>string</b> the generated SQL statement
+	*/
+	protected function getInParamStr($params, $param_name, $sql_column) {
+		$not='';
+		if (isset($params[$param_name.'_not'])) {
+			$not = "NOT ";
+		}
+		return $sql_column.' '.$not.$this->con->in($params[$param_name]).' ';
 	}
 	
@@ -407,5 +409,5 @@
 		
 		if (!empty($params['post_type'])) {
-			$strReq .= 'AND P.post_type '.$this->con->in($params['post_type']);
+			$strReq .= "AND post_type = '".$this->con->escape($params['post_type'])."' ";
 		}
 		
@@ -727,6 +729,8 @@
 		if (isset($params['post_type']))
 		{
-			if (is_array($params['post_type']) || $params['post_type'] != '') {
+			if (is_array($params['post_type']) && !empty($params['post_type'])) {
 				$strReq .= 'AND post_type '.$this->con->in($params['post_type']);
+			} elseif ($params['post_type'] != '') {
+				$strReq .= "AND post_type = '".$this->con->escape($params['post_type'])."' ";
 			}
 		}
@@ -750,5 +754,5 @@
 		
 		if (!empty($params['user_id'])) {
-			$strReq .= "AND U.user_id = '".$this->con->escape($params['user_id'])."' ";
+			$strReq .= 'AND '.$this->getInParamStr($params,'user_id','U.user_id');
 		}
 		
@@ -776,5 +780,5 @@
 		/* Other filters */
 		if (isset($params['post_status'])) {
-			$strReq .= 'AND post_status = '.(integer) $params['post_status'].' ';
+			$strReq .= 'AND '.$this->getInParamStr($params,'post_status','post_status');
 		}
 		
@@ -799,5 +803,5 @@
 		
 		if (!empty($params['post_lang'])) {
-			$strReq .= "AND P.post_lang = '".$this->con->escape($params['post_lang'])."' ";
+			$strReq .= 'AND '.$this->getInParamStr($params,'post_lang','P.post_lang');
 		}
 		
@@ -872,11 +876,11 @@
 		
 		if($dir > 0) {
-			$sign = '>';
-			$order = 'ASC';
-		}
-		else {
-			$sign = '<';
-			$order = 'DESC';
-		}
+               $sign = '>';
+               $order = 'ASC';
+          }
+          else {
+               $sign = '<';
+               $order = 'DESC';
+          }
 		
 		$params['post_type'] = $post->post_type;
@@ -1039,5 +1043,5 @@
 			$strReq .= "AND post_type = 'post' ";
 		}
-		
+				
 		if (!empty($params['year'])) {
 			$strReq .= 'AND '.$this->con->dateFormat('post_dt','%Y')." = '".sprintf('%04d',$params['year'])."' ";
@@ -1179,5 +1183,5 @@
 		$cur->post_upddt = date('Y-m-d H:i:s');
 		
-		#If user is only "usage", we need to check the post's owner
+		#�If user is only "usage", we need to check the post's owner
 		if (!$this->core->auth->check('contentadmin',$this->id))
 		{
@@ -1442,5 +1446,4 @@
 	{
 		$field = $field == 'cat_id' ? 'cat_id' : 'cat_url';
-		
 		$sub = array();
 		$not = array();
@@ -1456,7 +1459,11 @@
 			if (isset($args['not'])) { $not[$id] = 1; }
 			if (isset($args['sub'])) { $sub[$id] = 1; }
+			$nullExcluded = false;
 			if ($field == 'cat_id') {
 				if (preg_match('/^null$/i',$id)) {
 					$queries[$id] = 'P.cat_id IS NULL';
+					if ($not[$id]) {
+						$nullExcluded = true;
+					}
 				}
 				else {
@@ -1500,9 +1507,12 @@
 		
 		if ($sql[1]) {
-			$sql[1] = '(P.cat_id IS NULL OR NOT('.$sql[1].'))';
+			if ($nullExcluded) {
+				$sql[1] = 'NOT('.$sql[1].')';
+			} else {
+				$sql[1] = '(P.cat_id IS NULL OR NOT('.$sql[1].'))';
+			}
 		} else {
 			unset($sql[1]);
 		}
-		
 		return implode(' AND ',$sql);
 	}
@@ -1782,5 +1792,9 @@
 		if (!empty($params['post_type']))
 		{
-			$strReq .= 'AND post_type '.$this->con->in($params['post_type']);
+			if (is_array($params['post_type']) && !empty($params['post_type'])) {
+				$strReq .= 'AND post_type '.$this->con->in($params['post_type']);
+			} else {
+				$strReq .= "AND post_type = '".$this->con->escape($params['post_type'])."' ";
+			}
 		}
 		
@@ -1798,8 +1812,7 @@
 		
 		if (isset($params['comment_status'])) {
-			$strReq .= 'AND comment_status = '.(integer) $params['comment_status'].' ';
-		}
-		
-		if (!empty($params['comment_status_not']))
+			$strReq .= 'AND '.$this->getInParamStr($params,'comment_status','comment_status');
+		} 
+		elseif (!empty($params['comment_status_not'])) // older method, deprecated
 		{
 			$strReq .= 'AND comment_status <> '.(integer) $params['comment_status_not'].' ';
Index: inc/core/class.dc.categories.php
===================================================================
--- inc/core/class.dc.categories.php	(revision 270)
+++ inc/core/class.dc.categories.php	(revision 0)
@@ -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
Index: inc/core/class.dc.core.php
===================================================================
--- inc/core/class.dc.core.php	(revision 407)
+++ inc/core/class.dc.core.php	(revision 136)
@@ -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
@@ -27,5 +27,5 @@
 {
 	public $con;		///< <b>connection</b>		Database connection object
-	public $prefix;		///< <b>string</b>			Database tables prefix
+	public $prefix;	///< <b>string</b>			Database tables prefix
 	public $blog;		///< <b>dcBlog</b>			dcBlog object
 	public $error;		///< <b>dcError</b>			dcError object
@@ -36,6 +36,5 @@
 	public $plugins;	///< <b>dcModules</b>		dcModules object
 	public $media;		///< <b>dcMedia</b>			dcMedia object
-	public $postmedia;	///< <b>dcPostMedia</b>		dcPostMedia object
-	public $rest;		///< <b>dcRestServer</b>	dcRestServer object
+	public $rest;		///< <b>dcRestServer</b>		dcRestServer object
 	public $log;		///< <b>dcLog</b>			dcLog object
 	
@@ -64,16 +63,4 @@
 		if ($this->con instanceof mysqlConnection) {
 			mysqlConnection::$weak_locks = true;
-		}
-		
-		# define searchpath for postgresql
-		if ($this->con instanceof pgsqlConnection)
-		{
-			$searchpath = explode ('.',$prefix,2);
-			if (count($searchpath) > 1)
-			{
-				$prefix = $searchpath[1];
-				$sql = 'SET search_path TO '.$searchpath[0].',public;';
-				$this->con->execute($sql);
-			}
 		}
 		
Index: inc/core/class.dc.error.php
===================================================================
--- inc/core/class.dc.error.php	(revision 334)
+++ inc/core/class.dc.error.php	(revision 0)
@@ -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
@@ -13,24 +13,20 @@
 
 /**
-* @ingroup DC_CORE
-* @brief Error class
-* 
-* dcError is a very simple error class, with a stack. Call dcError::add to
-* add an error in stack. In administration area, errors are automatically
-* displayed.
+@ingroup DC_CORE
+@brief Error class
+
+dcError is a very simple error class, with a stack. Call dcError::add to
+add an error in stack. In administration area, errors are automatically
+displayed.
 */
 class dcError
 {
-	/** @var array Errors stack */
-	protected $errors = array();
-	/** @var boolean True if stack is not empty */
-	protected $flag = false;
-	/** @var string HTML errors list pattern */
-	protected $html_list = "<ul>\n%s</ul>\n";
-	/** @var string HTML error item pattern */
-	protected $html_item = "<li>%s</li>\n";
+	protected $errors = array();				///< <b>array</b>	Errors stack
+	protected $flag = false;					///< <b>boolean</b>	True if stack is not empty
+	protected $html_list = "<ul>\n%s</ul>\n";	///< <b>string</b>	HTML errors list pattern
+	protected $html_item = "<li>%s</li>\n";	///< <b>string</b>	HTML error item pattern
 	
 	/**
-	* Object constructor.
+	Object constructor.
 	*/
 	public function __construct()
@@ -41,7 +37,7 @@
 	
 	/**
-	* Object string representation. Returns errors stack.
-	* 
-	* @return string
+	Object string representation. Returns errors stack.
+	
+	@return	<b>string</b>
 	*/
 	public function __toString()
@@ -58,7 +54,7 @@
 	
 	/**
-	* Adds an error to stack.
-	* 
-	* @param string	$msg			Error message
+	Adds an error to stack.
+	
+	@param	msg		<b>string</b>		Error message
 	*/
 	public function add($msg)
@@ -69,7 +65,7 @@
 	
 	/**
-	* Returns the value of <var>flag</var> property. True if errors stack is not empty
-	* 
-	* @return boolean
+	Returns the value of <var>flag</var> property.
+	
+	@return	<b>boolean</b> True if errors stack is not empty
 	*/
 	public function flag()
@@ -79,5 +75,5 @@
 	
 	/**
-	* Resets errors stack.
+	Resets errors stack.
 	*/
 	public function reset()
@@ -88,7 +84,7 @@
 	
 	/**
-	* Returns <var>errors</var> property.
-	* 
-	* @return array
+	Returns <var>errors</var> property.
+	
+	@return	<b>array</b>
 	*/
 	public function getErrors()
@@ -98,8 +94,8 @@
 	
 	/**
-	* Sets <var>list</var> and <var>item</var> properties.
-	* 
-	* @param string	$list		HTML errors list pattern
-	* @param string	$item		HTML error item pattern
+	Sets <var>list</var> and <var>item</var> properties.
+	
+	@param	list		<b>string</b>		HTML errors list pattern
+	@param	item		<b>string</b>		HTML error item pattern
 	*/
 	public function setHTMLFormat($list,$item)
@@ -110,7 +106,7 @@
 	
 	/**
-	* Returns errors stack as HTML.
-	* 
-	* @return string
+	Returns errors stack as HTML.
+	
+	@return	<b>string</b>
 	*/
 	public function toHTML()
Index: inc/core/class.dc.log.php
===================================================================
--- inc/core/class.dc.log.php	(revision 270)
+++ inc/core/class.dc.log.php	(revision 126)
@@ -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
Index: inc/core/class.dc.media.php
===================================================================
--- inc/core/class.dc.media.php	(revision 505)
+++ inc/core/class.dc.media.php	(revision 40)
@@ -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
@@ -23,6 +23,6 @@
 	protected $con;		///< <b>connection</b> Database connection
 	protected $table;		///< <b>string</b> Media table name
+	protected $table_ref;	///< <b>string</b> Post-media relation table name
 	protected $type;		///< <b>string</b> Media type filter
-	protected $postmedia;
 	protected $file_sort = 'name-asc';
 	
@@ -57,5 +57,4 @@
 		$this->core =& $core;
 		$this->con =& $core->con;
-		$this->postmedia = new dcPostMedia($core);
 		
 		if ($this->core->blog == null) {
@@ -64,4 +63,5 @@
 		
 		$this->table = $this->core->prefix.'media';
+		$this->table_ref = $this->core->prefix.'post_media';
 		$root = $this->core->blog->public_path;
 		
@@ -175,21 +175,14 @@
 	
 	@param	href		<b>string</b>		URL pattern
-	@param	last		<b>string</b>		Last item pattern
 	@return	<b>string</b> HTML code
 	*/
-	public function breadCrumb($href,$last='')
+	public function breadCrumb($href)
 	{
 		$res = '';
 		if ($this->relpwd && $this->relpwd != '.') {
 			$pwd = '';
-			$arr = explode('/',$this->relpwd);
-			$count = count($arr);
-			foreach ($arr as $v) {
-				if (($last != '') && (0 === --$count)) {
-					$res .= sprintf($last,$v);
-				} else {
-					$pwd .= rawurlencode($v).'/';
-					$res .= '<a href="'.sprintf($href,$pwd).'">'.$v.'</a> / ';
-				}
+			foreach (explode('/',$this->relpwd) as $v) {
+				$pwd .= rawurlencode($v).'/';
+				$res .= '<a href="'.sprintf($href,$pwd).'">'.$v.'</a> / ';
 			}
 		}
@@ -501,12 +494,19 @@
 	public function getPostMedia($post_id,$media_id=null)
 	{
-		$params = array(
-			'post_id' => $post_id,
-			'media_path' => $this->path
-		);
+		$post_id = (integer) $post_id;
+		
+		$strReq =
+		'SELECT media_file, M.media_id, media_path, media_title, media_meta, media_dt, '.
+		'media_creadt, media_upddt, media_private, user_id '.
+		'FROM '.$this->table.' M '.
+		'INNER JOIN '.$this->table_ref.' PM ON (M.media_id = PM.media_id) '.
+		"WHERE media_path = '".$this->path."' ".
+		'AND post_id = '.$post_id.' ';
+		
 		if ($media_id) {
-			$params['media_id'] = (integer) $media_id;
-		}
-		$rs = $this->postmedia->getPostMedia($params);
+			$strReq .= 'AND M.media_id = '.(integer) $media_id.' ';
+		}
+		
+		$rs = $this->con->select($strReq);
 		
 		$res = array();
@@ -523,19 +523,45 @@
 	
 	/**
-	@deprecated since version 2.4
-	@see dcPostMedia::addPostMedia
+	Attaches a media to a post.
+	
+	@param	post_id	<b>integer</b>		Post ID
+	@param	media_id	<b>integer</b>		Optionnal media ID
 	*/
 	public function addPostMedia($post_id,$media_id)
 	{
-		$this->postmedia->addPostMedia($post_id,$media_id);
-	}
-	
-	/**
-	@deprecated since version 2.4
-	@see dcPostMedia::removePostMedia
+		$post_id = (integer) $post_id;
+		$media_id = (integer) $media_id;
+		
+		$f = $this->getPostMedia($post_id,$media_id);
+		
+		if (!empty($f)) {
+			return;
+		}
+		
+		$cur = $this->con->openCursor($this->table_ref);
+		$cur->post_id = $post_id;
+		$cur->media_id = $media_id;
+		
+		$cur->insert();
+		$this->core->blog->triggerBlog();
+	}
+	
+	/**
+	Detaches a media from a post.
+	
+	@param	post_id	<b>integer</b>		Post ID
+	@param	media_id	<b>integer</b>		Optionnal media ID
 	*/
 	public function removePostMedia($post_id,$media_id)
 	{
-		$this->postmedia->removePostMedia($post_id,$media_id,"attachment");
+		$post_id = (integer) $post_id;
+		$media_id = (integer) $media_id;
+		
+		$strReq = 'DELETE FROM '.$this->table_ref.' '.
+				'WHERE post_id = '.$post_id.' '.
+				'AND media_id = '.$media_id.' ';
+		
+		$this->con->execute($strReq);
+		$this->core->blog->triggerBlog();
 	}
 	
Index: inc/core/class.dc.meta.php
===================================================================
--- inc/core/class.dc.meta.php	(revision 270)
+++ inc/core/class.dc.meta.php	(revision 0)
@@ -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
Index: inc/core/class.dc.modules.php
===================================================================
--- inc/core/class.dc.modules.php	(revision 464)
+++ inc/core/class.dc.modules.php	(revision 43)
@@ -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
@@ -159,26 +159,9 @@
 	@param	author		<b>string</b>		Module author name
 	@param	version		<b>string</b>		Module version
-	@param	properties	<b>array</b>		extra properties (currently available keys : permissions, priority)
-	*/
-	public function registerModule($name,$desc,$author,$version, $properties = array())
-	{
-		if (!is_array($properties)) {
-			//Fallback to legacy registerModule parameters
-			$args = func_get_args();
-			$properties = array();
-			if (isset($args[4])) {
-				$properties['permissions']=$args[4];
-			}
-			if (isset($args[5])) {
-				$properties['priority']= (integer)$args[5];
-			}
-		}
-		$properties = array_merge(
-			array(
-				'permissions' => null,
-				'priority' => 1000
-			), $properties
-		);
-		$permissions = $properties['permissions'];
+	@param	permissions	<b>string</b>		Module permissions
+	@param	priority		<b>integer</b>		Module priority
+	*/
+	public function registerModule($name,$desc,$author,$version,$permissions=null,$priority=1000)
+	{
 		if ($this->ns == 'admin') {
 			if ($permissions == '' && !$this->core->auth->isSuperAdmin()) {
@@ -194,14 +177,13 @@
 			if (!$module_exists || ($module_exists && $module_overwrite)) {
 				$this->modules_names[$name] = $version;
-				$this->modules[$this->id] = array_merge(
-					$properties,
-					array(
-						'root' => $this->mroot,
-						'name' => $name,
-						'desc' => $desc,
-						'author' => $author,
-						'version' => $version,
-						'root_writable' => is_writable($this->mroot)
-					)
+				$this->modules[$this->id] = array(
+					'root' => $this->mroot,
+					'name' => $name,
+					'desc' => $desc,
+					'author' => $author,
+					'version' => $version,
+					'permissions' => $permissions,
+					'priority' => $priority === null ? 1000 : (integer) $priority,
+					'root_writable' => is_writable($this->mroot)
 				);
 			}
Index: inc/core/class.dc.namespace.php
===================================================================
--- inc/core/class.dc.namespace.php	(revision 270)
+++ inc/core/class.dc.namespace.php	(revision 0)
@@ -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
Index: inc/core/class.dc.postmedia.php
===================================================================
--- inc/core/class.dc.postmedia.php	(revision 407)
+++ 	(revision )
@@ -1,172 +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; }
-
-class dcPostMedia
-{
-	protected $core;		///< <b>dcCore</b> dcCore instance
-	protected $con;		///< <b>connection</b> Database connection
-	protected $table;		///< <b>string</b> Post-Media table name
-	
-	/**
-	Object constructor.
-	
-	@param	core		<b>dcCore</b>		dcCore instance
-	@param	type		<b>string</b>		Media type filter
-	*/
-	public function __construct($core,$type='')
-	{
-		$this->core =& $core;
-		$this->con =& $core->con;
-		$this->table = $this->core->prefix.'post_media';
-	}
-	
-	/**
-	Returns media items attached to a blog post. Result is an array containing
-	fileItems objects.
-	
-	@param	post_id	<b>integer</b>		Post ID
-	@param	media_id	<b>integer</b>		Optionnal media ID
-	@return	<b>array</b> Array of fileItems
-	*/
-	public function getPostMedia($params=array())
-	{
-		$strReq =
-		'SELECT M.media_file, M.media_id, M.media_path, M.media_title, M.media_meta, M.media_dt, '.
-		'M.media_creadt, M.media_upddt, M.media_private, M.user_id, PM.post_id ';
-		
-		if (!empty($params['columns']) && is_array($params['columns'])) {
-			$strReq .= implode(', ',$params['columns']).', ';
-		}
-		
-		$strReq .=
-		'FROM '.$this->core->prefix.'media M '.
-		'INNER JOIN '.$this->table.' PM ON (M.media_id = PM.media_id) ';
-		
-		if (!empty($params['from'])) {
-			$strReq .= $params['from'].' ';
-		}
-		
-		$where='';
-		if (isset($params['post_id'])) {
-			$where[]="PM.post_id ".$this->con->in($params['post_id']);
-		}
-		if (isset($params['media_id'])) {
-			$where[]="M.media_id ".$this->con->in($params['media_id']);
-		}
-		if (isset($params['media_path'])) {
-			$where[]="M.media_path ".$this->con->in($params['media_path']);
-		}
-		if (isset($params['link_type'])) {
-			$where[]="PM.link_type ".$this->con->in($params['link_type']);
-		} else {
-			$where[]="PM.link_type='attachment'";
-		}
-
-		$strReq .= 'WHERE '.join('AND ',$where).' ';
-
-		if (isset($params['sql'])) {
-			$strReq .= $params['sql'];
-		}
-	//echo $strReq; exit;
-		$rs = $this->con->select($strReq);
-		
-		return $rs;
-	}
-
-	/**
-	Attaches a media to a post.
-	
-	@param	post_id	<b>integer</b>		Post ID
-	@param	media_id	<b>integer</b>		Optionnal media ID
-	*/
-	public function addPostMedia($post_id,$media_id,$link_type='attachment')
-	{
-		$post_id = (integer) $post_id;
-		$media_id = (integer) $media_id;
-		
-		$f = $this->getPostMedia(array('post_id'=>$post_id,'media_id'=>$media_id,'link_type'=>$link_type));
-		
-		if (!$f->isEmpty()) {
-			return;
-		}
-		
-		$cur = $this->con->openCursor($this->table);
-		$cur->post_id = $post_id;
-		$cur->media_id = $media_id;
-		$cur->link_type = $link_type;
-		
-		$cur->insert();
-		$this->core->blog->triggerBlog();
-	}
-	
-	/**
-	Detaches a media from a post.
-	
-	@param	post_id	<b>integer</b>		Post ID
-	@param	media_id	<b>integer</b>		Optionnal media ID
-	*/
-	public function removePostMedia($post_id,$media_id,$link_type=null)
-	{
-		$post_id = (integer) $post_id;
-		$media_id = (integer) $media_id;
-		
-		$strReq = 'DELETE FROM '.$this->table.' '.
-				'WHERE post_id = '.$post_id.' '.
-				'AND media_id = '.$media_id.' ';
-		if ($link_type != null) {
-			$strReq .= "AND link_type = '".$this->con->escape($link_type)."'";
-		}
-		$this->con->execute($strReq);
-		$this->core->blog->triggerBlog();
-	}
-	
-	/**
-	Returns media items attached to a blog post. Result is an array containing
-	fileItems objects.
-	
-	@param	post_id	<b>integer</b>		Post ID
-	@param	media_id	<b>integer</b>		Optionnal media ID
-	@return	<b>array</b> Array of fileItems
-	*/
-	public function getLegacyPostMedia($post_id,$media_id=null)
-	{
-		$post_id = (integer) $post_id;
-		
-		$strReq =
-		'SELECT media_file, M.media_id, media_path, media_title, media_meta, media_dt, '.
-		'media_creadt, media_upddt, media_private, user_id '.
-		'FROM '.$this->table.' M '.
-		'INNER JOIN '.$this->table_ref.' PM ON (M.media_id = PM.media_id) '.
-		"WHERE media_path = '".$this->path."' ".
-		'AND post_id = '.$post_id.' ';
-		
-		if ($media_id) {
-			$strReq .= 'AND M.media_id = '.(integer) $media_id.' ';
-		}
-		
-		$rs = $this->con->select($strReq);
-		
-		$res = array();
-		
-		while ($rs->fetch()) {
-			$f = $this->fileRecord($rs);
-			if ($f !== null) {
-				$res[] = $f;
-			}
-		}
-		
-		return $res;
-	}
-	
-}
-?>
Index: inc/core/class.dc.rest.php
===================================================================
--- inc/core/class.dc.rest.php	(revision 270)
+++ inc/core/class.dc.rest.php	(revision 0)
@@ -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
Index: inc/core/class.dc.rs.extensions.php
===================================================================
--- inc/core/class.dc.rs.extensions.php	(revision 270)
+++ inc/core/class.dc.rs.extensions.php	(revision 0)
@@ -3,5 +3,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
Index: inc/core/class.dc.settings.php
===================================================================
--- inc/core/class.dc.settings.php	(revision 301)
+++ inc/core/class.dc.settings.php	(revision 0)
@@ -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
@@ -308,77 +308,4 @@
 	}
 
-	/**
-	Returns a list of settings matching given criteria, for any blog.
-	<b>$params</b> is an array taking the following
-	optionnal parameters:
-	
-	- ns : retrieve setting from given namespace
-	- id : retrieve only settings corresponding to the given id
-	
-	@param	params		<b>array</b>		Parameters
-	@return	<b>record</b>	A record 
-	*/
-	public function getGlobalSettings($params=array())
-	{
-		$strReq = "SELECT * from ".$this->table." ";
-		$where = array();
-		if (!empty($params['ns'])) {
-			$where[] = "setting_ns = '".$this->con->escape($params['ns'])."'";
-		}
-		if (!empty($params['id'])) {
-			$where[] = "setting_id = '".$this->con->escape($params['id'])."'";
-		}
-		if (isset($params['blog_id'])) {
-			if (!empty($params['blog_id'])) {
-				$where[] = "blog_id = '".$this->con->escape($params['blog_id'])."'";
-			} else {
-				$where[] = "blog_id IS NULL";
-			}
-		}
-		if (count($where) != 0) {
-			$strReq .= " WHERE ".join(" AND ", $where);
-		}
-		$strReq .= " ORDER by blog_id";
-		return $this->con->select($strReq);
-	}
-
-	/**
-	Updates a setting from a given record
-	
-	@param	rs		<b>record</b>		the setting to update
-	*/
-	public function updateSetting($rs) 
-	{
-		$cur = $this->con->openCursor($this->table);
-		$cur->setting_id = $rs->setting_id;
-		$cur->setting_value = $rs->setting_value;
-		$cur->setting_type = $rs->setting_type;
-		$cur->setting_label = $rs->setting_label;
-		$cur->blog_id = $rs->blog_id;
-		$cur->setting_ns = $rs->setting_ns;
-		if ($cur->blog_id == null) {
-				$where = 'WHERE blog_id IS NULL ';
-		} else {
-			$where = "WHERE blog_id = '".$this->con->escape($cur->blog_id)."' ";
-		}
-		$cur->update($where."AND setting_id = '".$this->con->escape($cur->setting_id)."' AND setting_ns = '".$this->con->escape($cur->setting_ns)."' ");
-	}
-	
-	/**
-	Drops a setting from a given record
-	
-	@param	rs		<b>record</b>		the setting to drop
-	@return	int		number of deleted records (0 if setting does not exist)
-	*/
-	public function dropSetting($rs) {
-		$strReq = "DELETE FROM ".$this->table.' ';
-		if ($rs->blog_id == null) {
-			$strReq .= 'WHERE blog_id IS NULL ';
-		} else {
-			$strReq .= "WHERE blog_id = '".$this->con->escape($rs->blog_id)."' ";
-		}
-		$strReq .= "AND setting_id = '".$this->con->escape($rs->setting_id)."' AND setting_ns = '".$this->con->escape($rs->setting_ns)."' ";
-		return $this->con->execute($strReq);
-	}
 }
 ?>
Index: inc/core/class.dc.themes.php
===================================================================
--- inc/core/class.dc.themes.php	(revision 465)
+++ inc/core/class.dc.themes.php	(revision 0)
@@ -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
@@ -39,37 +39,19 @@
 	@param	author		<b>string</b>		Module author name
 	@param	version		<b>string</b>		Module version
-	@param	properties	<b>array</b>		extra properties (currently available keys : parent, priority, standalone_config)
+	@param	parent		<b>string</b>		Module parent
+	@param	priority		<b>integer</b>		Module priority
 	*/
-	public function registerModule($name,$desc,$author,$version,$properties = array())
+	public function registerModule($name,$desc,$author,$version,$parent = null,$priority = 1000)
 	{
-		if (!is_array($properties)) {
-			//Fallback to legacy registerModule parameters
-			$args = func_get_args();
-			$properties = array();
-			if (isset($args[4])) {
-				$properties['parent']=$args[4];
-			}
-			if (isset($args[5])) {
-				$properties['priority']= (integer)$args[5];
-			}
-		}
-		$properties = array_merge(
-			array(
-				'parent' => null,
-				'priority' => 1000,
-				'standalone_config' => false
-			), $properties
-		);
 		if ($this->id) {
-			$this->modules[$this->id] = array_merge(
-				$properties,
-				array(
-					'root' => $this->mroot,
-					'name' => $name,
-					'desc' => $desc,
-					'author' => $author,
-					'version' => $version,
-					'root_writable' => is_writable($this->mroot)
-				)
+			$this->modules[$this->id] = array(
+			'root' => $this->mroot,
+			'name' => $name,
+			'desc' => $desc,
+			'author' => $author,
+			'version' => $version,
+			'parent' => $parent,
+			'priority' => 1000,
+			'root_writable' => is_writable($this->mroot)
 			);
 		}
Index: inc/core/class.dc.trackback.php
===================================================================
--- inc/core/class.dc.trackback.php	(revision 270)
+++ inc/core/class.dc.trackback.php	(revision 0)
@@ -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
Index: inc/core/class.dc.update.php
===================================================================
--- inc/core/class.dc.update.php	(revision 270)
+++ inc/core/class.dc.update.php	(revision 0)
@@ -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
Index: inc/core/class.dc.utils.php
===================================================================
--- inc/core/class.dc.utils.php	(revision 270)
+++ inc/core/class.dc.utils.php	(revision 0)
@@ -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
Index: inc/core/class.dc.xmlrpc.php
===================================================================
--- inc/core/class.dc.xmlrpc.php	(revision 270)
+++ inc/core/class.dc.xmlrpc.php	(revision 0)
@@ -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
Index: inc/core_error.php
===================================================================
--- inc/core_error.php	(revision 270)
+++ inc/core_error.php	(revision 199)
@@ -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
Index: inc/dbschema/db-schema.php
===================================================================
--- inc/dbschema/db-schema.php	(revision 407)
+++ inc/dbschema/db-schema.php	(revision 3)
@@ -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
@@ -152,7 +152,6 @@
 	->media_id	('bigint',	0,	false)
 	->post_id		('bigint',	0,	false)
-	->link_type		('varchar',	32,	false,	"'attachment'")
-	
-	->primary('pk_post_media','media_id','post_id','link_type')
+	
+	->primary('pk_post_media','media_id','post_id')
 	;
 
@@ -236,5 +235,4 @@
 $_s->media->index		('idx_media_user_id',			'btree',	'user_id');
 $_s->post_media->index	('idx_post_media_post_id',		'btree',	'post_id');
-$_s->post_media->index	('idx_post_media_media_id',		'btree',	'media_id');
 $_s->log->index		('idx_log_user_id',				'btree',	'user_id');
 $_s->comment->index		('idx_comment_post_id',			'btree',	'post_id');
Index: inc/dbschema/upgrade-cli.php
===================================================================
--- inc/dbschema/upgrade-cli.php	(revision 270)
+++ inc/dbschema/upgrade-cli.php	(revision 0)
@@ -5,5 +5,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
Index: inc/dbschema/upgrade.php
===================================================================
--- inc/dbschema/upgrade.php	(revision 462)
+++ inc/dbschema/upgrade.php	(revision 107)
@@ -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
@@ -20,5 +20,5 @@
 	}
 	
-	if (version_compare($version,DC_VERSION,'<') == 1 || strpos(DC_VERSION,'dev'))
+	if (version_compare($version,DC_VERSION,'<') == 1)
 	{
 		try
@@ -149,27 +149,27 @@
 				$init_fav = array();
 
-				$init_fav['new_post'] = array('new_post','New entry','post.php',
+				$init_fav['new_post'] = array('new_post',__('New entry'),'post.php',
 					'images/menu/edit.png','images/menu/edit-b.png',
 					'usage,contentadmin',null,'menu-new-post');
-				$init_fav['posts'] = array('posts','Entries','posts.php',
+				$init_fav['posts'] = array('posts',__('Entries'),'posts.php',
 					'images/menu/entries.png','images/menu/entries-b.png',
 					'usage,contentadmin',null,null);
-				$init_fav['comments'] = array('comments','Comments','comments.php',
+				$init_fav['comments'] = array('comments',__('Comments'),'comments.php',
 					'images/menu/comments.png','images/menu/comments-b.png',
 					'usage,contentadmin',null,null);
-				$init_fav['prefs'] = array('prefs','My preferences','preferences.php',
+				$init_fav['prefs'] = array('prefs',__('My preferences'),'preferences.php',
 					'images/menu/user-pref.png','images/menu/user-pref-b.png',
 					'*',null,null);
-				$init_fav['blog_pref'] = array('blog_pref','Blog settings','blog_pref.php',
+				$init_fav['blog_pref'] = array('blog_pref',__('Blog settings'),'blog_pref.php',
 					'images/menu/blog-pref.png','images/menu/blog-pref-b.png',
 					'admin',null,null);
-				$init_fav['blog_theme'] = array('blog_theme','Blog appearance','blog_theme.php',
+				$init_fav['blog_theme'] = array('blog_theme',__('Blog appearance'),'blog_theme.php',
 					'images/menu/themes.png','images/menu/blog-theme-b.png',
 					'admin',null,null);
 
-				$init_fav['pages'] = array('pages','Pages','plugin.php?p=pages',
+				$init_fav['pages'] = array('pages',__('Pages'),'plugin.php?p=pages',
 					'index.php?pf=pages/icon.png','index.php?pf=pages/icon-big.png',
 					'contentadmin,pages',null,null);
-				$init_fav['blogroll'] = array('blogroll','Blogroll','plugin.php?p=blogroll',
+				$init_fav['blogroll'] = array('blogroll',__('Blogroll'),'plugin.php?p=blogroll',
 					'index.php?pf=blogroll/icon-small.png','index.php?pf=blogroll/icon.png',
 					'usage,contentadmin',null,null);
@@ -290,11 +290,4 @@
 			}
 			
-			if (version_compare($version,'2.3.1','<'))
-			{
-				# Remove unecessary file
-				@unlink(DC_ROOT.'/'.'inc/libs/clearbricks/.hgignore');
-			}
-			
-			
 			$core->setVersion('core',DC_VERSION);
 			$core->blogDefaults();
Index: inc/load_plugin_file.php
===================================================================
--- inc/load_plugin_file.php	(revision 270)
+++ inc/load_plugin_file.php	(revision 0)
@@ -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
Index: inc/prepend.php
===================================================================
--- inc/prepend.php	(revision 407)
+++ inc/prepend.php	(revision 522)
@@ -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
@@ -41,7 +41,8 @@
 $__autoload['dcCategories']			= dirname(__FILE__).'/core/class.dc.categories.php';
 $__autoload['dcError']				= dirname(__FILE__).'/core/class.dc.error.php';
+$__autoload['dcGenericMeta']		= dirname(__FILE__).'/core/class.dc.genmeta.php';
 $__autoload['dcMeta']				= dirname(__FILE__).'/core/class.dc.meta.php';
+$__autoload['dcUserMeta']				= dirname(__FILE__).'/core/class.dc.usermeta.php';
 $__autoload['dcMedia']				= dirname(__FILE__).'/core/class.dc.media.php';
-$__autoload['dcPostMedia']				= dirname(__FILE__).'/core/class.dc.postmedia.php';
 $__autoload['dcModules']				= dirname(__FILE__).'/core/class.dc.modules.php';
 $__autoload['dcThemes']				= dirname(__FILE__).'/core/class.dc.themes.php';
@@ -65,8 +66,13 @@
 $__autoload['dcPage']				= dirname(__FILE__).'/admin/lib.dc.page.php';
 $__autoload['adminGenericList']		= dirname(__FILE__).'/admin/lib.pager.php';
-$__autoload['adminPostList']			= dirname(__FILE__).'/admin/lib.pager.php';
-$__autoload['adminPostMiniList']		= dirname(__FILE__).'/admin/lib.pager.php';
-$__autoload['adminCommentList']		= dirname(__FILE__).'/admin/lib.pager.php';
-$__autoload['adminUserList']			= dirname(__FILE__).'/admin/lib.pager.php';
+$__autoload['adminItemsList']			= dirname(__FILE__).'/admin/class.dc.list.php';
+$__autoload['adminPostList']			= dirname(__FILE__).'/admin/class.dc.list.php';
+$__autoload['adminPostMiniList']		= dirname(__FILE__).'/admin/class.dc.list.php';
+$__autoload['adminCommentList']		= dirname(__FILE__).'/admin/class.dc.list.php';
+$__autoload['adminUserList']			= dirname(__FILE__).'/admin/class.dc.list.php';
+$__autoload['adminBlogList']			= dirname(__FILE__).'/admin/class.dc.list.php';
+$__autoload['adminBlogPermissionsList']	= dirname(__FILE__).'/admin/class.dc.list.php';
+$__autoload['dcFilterSet']			= dirname(__FILE__).'/admin/class.dc.filter.php';
+$__autoload['dcFilter']				= dirname(__FILE__).'/admin/class.dc.filter.php';
 
 $__autoload['dcTemplate']			= dirname(__FILE__).'/public/class.dc.template.php';
@@ -119,5 +125,5 @@
 # Constants
 define('DC_ROOT',path::real(dirname(__FILE__).'/..'));
-define('DC_VERSION','2.4.0-dev');
+define('DC_VERSION','2.3.1');
 define('DC_DIGESTS',dirname(__FILE__).'/digests');
 define('DC_L10N_ROOT',dirname(__FILE__).'/../locales');
Index: inc/public/class.dc.template.php
===================================================================
--- inc/public/class.dc.template.php	(revision 415)
+++ inc/public/class.dc.template.php	(revision 0)
@@ -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
@@ -54,4 +54,18 @@
 		$this->addValue('ArchiveEntriesCount',array($this,'ArchiveEntriesCount'));
 		$this->addValue('ArchiveURL',array($this,'ArchiveURL'));
+		
+		# Attachments
+		$this->addBlock('Attachments',array($this,'Attachments'));
+		$this->addBlock('AttachmentsHeader',array($this,'AttachmentsHeader'));
+		$this->addBlock('AttachmentsFooter',array($this,'AttachmentsFooter'));
+		$this->addValue('AttachmentMimeType',array($this,'AttachmentMimeType'));
+		$this->addValue('AttachmentType',array($this,'AttachmentType'));
+		$this->addValue('AttachmentFileName',array($this,'AttachmentFileName'));
+		$this->addValue('AttachmentSize',array($this,'AttachmentSize'));
+		$this->addValue('AttachmentTitle',array($this,'AttachmentTitle'));
+		$this->addValue('AttachmentThumbnailURL',array($this,'AttachmentThumbnailURL'));
+		$this->addValue('AttachmentURL',array($this,'AttachmentURL'));
+		$this->addBlock('AttachmentIf',array($this,'AttachmentIf'));
+		$this->addValue('MediaURL',array($this,'MediaURL'));
 		
 		# Blog
@@ -127,4 +141,5 @@
 		$this->addBlock('EntriesHeader',array($this,'EntriesHeader'));
 		$this->addValue('EntryExcerpt',array($this,'EntryExcerpt'));
+		$this->addValue('EntryAttachmentCount',array($this,'EntryAttachmentCount'));
 		$this->addValue('EntryAuthorCommonName',array($this,'EntryAuthorCommonName'));
 		$this->addValue('EntryAuthorDisplayName',array($this,'EntryAuthorDisplayName'));
@@ -308,10 +323,4 @@
 		}
 		
-		if (($node instanceof tplNodeBlock) && !$node->isClosed()) {
-			$errors[] = sprintf(
-				__('Did not find closing tag for block <tpl:%s>. Content has been ignored.'),
-				html::escapeHTML($node->getTag()));
-		}
-		
 		$err = "";
 		if (count($errors) > 0) {
@@ -332,7 +341,4 @@
 		# --BEHAVIOR-- templateBeforeBlock
 		$res = $this->core->callBehavior('templateBeforeBlock',$this->core,$this->current_tag,$attr);
-		
-		# --BEHAVIOR-- templateInsideBlock
-		$this->core->callBehavior('templateInsideBlock',$this->core,$this->current_tag,$attr,array(&$content));
 		
 		if (isset($this->blocks[$this->current_tag])) {
@@ -557,5 +563,5 @@
 		}
 		
-		if (empty($attr['no_context']) && !isset($attr['category']))
+		if (empty($attr['no_context']))
 		{
 			$p .=
@@ -734,4 +740,173 @@
 	}
 	
+	/* Attachments ---------------------------------------- */
+	/*dtd
+	<!ELEMENT tpl:Attachments - - -- Post Attachments loop -->
+	*/
+	public function Attachments($attr,$content)
+	{
+		$res =
+		"<?php\n".
+		'if ($_ctx->posts !== null && $core->media) {'."\n".
+			'$_ctx->attachments = new ArrayObject($core->media->getPostMedia($_ctx->posts->post_id));'."\n".
+		"?>\n".
+		
+		'<?php foreach ($_ctx->attachments as $attach_i => $attach_f) : '.
+		'$GLOBALS[\'attach_i\'] = $attach_i; $GLOBALS[\'attach_f\'] = $attach_f;'.
+		'$_ctx->file_url = $attach_f->file_url; ?>'.
+		$content.
+		'<?php endforeach; $_ctx->attachments = null; unset($attach_i,$attach_f,$_ctx->file_url); ?>'.
+		
+		"<?php } ?>\n";
+		
+		return $res;
+	}
+	
+	/*dtd
+	<!ELEMENT tpl:AttachmentsHeader - - -- First attachments result container -->
+	*/
+	public function AttachmentsHeader($attr,$content)
+	{
+		return
+		"<?php if (\$attach_i == 0) : ?>".
+		$content.
+		"<?php endif; ?>";
+	}
+	
+	/*dtd
+	<!ELEMENT tpl:AttachmentsFooter - - -- Last attachments result container -->
+	*/
+	public function AttachmentsFooter($attr,$content)
+	{
+		return
+		"<?php if (\$attach_i+1 == count(\$_ctx->attachments)) : ?>".
+		$content.
+		"<?php endif; ?>";
+	}
+	
+	/*dtd
+	<!ELEMENT tpl:AttachmentsIf - - -- Test on attachment fields -->
+	<!ATTLIST tpl:AttachmentIf
+	is_image	(0|1)	#IMPLIED	-- test if attachment is an image (value : 1) or not (value : 0)
+	has_thumb	(0|1)	#IMPLIED	-- test if attachment has a square thumnail (value : 1) or not (value : 0)
+	is_mp3	(0|1)	#IMPLIED	-- test if attachment is a mp3 file (value : 1) or not (value : 0)
+	is_flv	(0|1)	#IMPLIED	-- test if attachment is a flv file (value : 1) or not (value : 0)
+	>
+	*/
+	public function AttachmentIf($attr,$content)
+	{
+		$if = array();
+		
+		$operator = isset($attr['operator']) ? $this->getOperator($attr['operator']) : '&&';
+		
+		if (isset($attr['is_image'])) {
+			$sign = (boolean) $attr['is_image'] ? '' : '!';
+			$if[] = $sign.'$attach_f->media_image';
+		}
+		
+		if (isset($attr['has_thumb'])) {
+			$sign = (boolean) $attr['has_thumb'] ? '' : '!';
+			$if[] = $sign.'isset($attach_f->media_thumb[\'sq\'])';
+		}
+		
+		if (isset($attr['is_mp3'])) {
+			$sign = (boolean) $attr['is_mp3'] ? '==' : '!=';
+			$if[] = '$attach_f->type '.$sign.' "audio/mpeg3"';
+		}
+		
+		if (isset($attr['is_flv'])) {
+			$sign = (boolean) $attr['is_flv'] ? '' : '!';
+			$if[] = $sign.
+				'($attach_f->type == "video/x-flv" || '.
+				'$attach_f->type == "video/mp4" || '.
+				'$attach_f->type == "video/x-m4v")';
+		}
+		
+		if (!empty($if)) {
+			return '<?php if('.implode(' '.$operator.' ',$if).') : ?>'.$content.'<?php endif; ?>';
+		} else {
+			return $content;
+		}
+	}
+	
+	/*dtd
+	<!ELEMENT tpl:AttachmentMimeType - O -- Attachment MIME Type -->
+	*/
+	public function AttachmentMimeType($attr)
+	{
+		$f = $this->getFilters($attr);
+		return '<?php echo '.sprintf($f,'$attach_f->type').'; ?>';
+	}
+	
+	/*dtd
+	<!ELEMENT tpl:AttachmentType - O -- Attachment type -->
+	*/
+	public function AttachmentType($attr)
+	{
+		$f = $this->getFilters($attr);
+		return '<?php echo '.sprintf($f,'$attach_f->media_type').'; ?>';
+	}
+	
+	/*dtd
+	<!ELEMENT tpl:AttachmentFileName - O -- Attachment file name -->
+	*/
+	public function AttachmentFileName($attr)
+	{
+		$f = $this->getFilters($attr);
+		return '<?php echo '.sprintf($f,'$attach_f->basename').'; ?>';
+	}
+	
+	/*dtd
+	<!ELEMENT tpl:AttachmentSize - O -- Attachment size -->
+	<!ATTLIST tpl:AttachmentSize
+	full	CDATA	#IMPLIED	-- if set, size is rounded to a human-readable value (in KB, MB, GB, TB)
+	>
+	*/
+	public function AttachmentSize($attr)
+	{
+		$f = $this->getFilters($attr);
+		if (!empty($attr['full'])) {
+			return '<?php echo '.sprintf($f,'$attach_f->size').'; ?>';
+		}
+		return '<?php echo '.sprintf($f,'files::size($attach_f->size)').'; ?>';
+	}
+	
+	/*dtd
+	<!ELEMENT tpl:AttachmentTitle - O -- Attachment title -->
+	*/
+	public function AttachmentTitle($attr)
+	{
+		$f = $this->getFilters($attr);
+		return '<?php echo '.sprintf($f,'$attach_f->media_title').'; ?>';
+	}
+	
+	/*dtd
+	<!ELEMENT tpl:AttachmentThumbnailURL - O -- Attachment square thumbnail URL -->
+	*/
+	public function AttachmentThumbnailURL($attr)
+	{
+		$f = $this->getFilters($attr);
+		return
+		'<?php '.
+		'if (isset($attach_f->media_thumb[\'sq\'])) {'.
+			'echo '.sprintf($f,'$attach_f->media_thumb[\'sq\']').';'.
+		'}'.
+		'?>';
+	}
+	
+	/*dtd
+	<!ELEMENT tpl:AttachmentURL - O -- Attachment URL -->
+	*/
+	public function AttachmentURL($attr)
+	{
+		$f = $this->getFilters($attr);
+		return '<?php echo '.sprintf($f,'$attach_f->file_url').'; ?>';
+	}
+	
+	public function MediaURL($attr)
+	{
+		$f = $this->getFilters($attr);
+		return '<?php echo '.sprintf($f,'$_ctx->file_url').'; ?>';
+	}
 	
 	/* Blog ----------------------------------------------- */
@@ -976,5 +1151,5 @@
 	public function CategoryIf($attr,$content)
 	{
-		$if = new ArrayObject();
+		$if = array();
 		$operator = isset($attr['operator']) ? $this->getOperator($attr['operator']) : '&&';
 		
@@ -999,8 +1174,6 @@
 		} 
 		
-		$this->core->callBehavior('tplIfConditions','CategoryIf',$attr,$content,$if);
-		
-		if (count($if) != 0) {
-			return '<?php if('.implode(' '.$operator.' ', (array) $if).') : ?>'.$content.'<?php endif; ?>';
+		if (!empty($if)) {
+			return '<?php if('.implode(' '.$operator.' ',$if).') : ?>'.$content.'<?php endif; ?>';
 		} else {
 			return $content;
@@ -1151,19 +1324,13 @@
 		if (empty($attr['no_context']))
 		{
-			if (!isset($attr['author']))
-			{
-				$p .=
-				'if ($_ctx->exists("users")) { '.
-					"\$params['user_id'] = \$_ctx->users->user_id; ".
-				"}\n";
-			}
+			$p .=
+			'if ($_ctx->exists("users")) { '.
+				"\$params['user_id'] = \$_ctx->users->user_id; ".
+			"}\n";
 			
-			if (!isset($attr['category']) && (!isset($attr['no_category']) || !$attr['no_category']))
-			{
-				$p .=
-				'if ($_ctx->exists("categories")) { '.
-					"\$params['cat_id'] = \$_ctx->categories->cat_id; ".
-				"}\n";
-			}
+			$p .=
+			'if ($_ctx->exists("categories")) { '.
+				"\$params['cat_id'] = \$_ctx->categories->cat_id; ".
+			"}\n";
 			
 			$p .=
@@ -1249,5 +1416,5 @@
 	selected	(0|1)	#IMPLIED	-- post is selected (value : 1) or not (value : 0)
 	has_category	(0|1)	#IMPLIED	-- post has a category (value : 1) or not (value : 0)
-	has_attachment	(0|1)	#IMPLIED	-- post has attachments (value : 1) or not (value : 0) (see Attachment plugin for code)
+	has_attachment	(0|1)	#IMPLIED	-- post has attachments (value : 1) or not (value : 0)
 	comments_active	(0|1)	#IMPLIED	-- comments are active for this post (value : 1) or not (value : 0)
 	pings_active	(0|1)	#IMPLIED	-- trackbacks are active for this post (value : 1) or not (value : 0)
@@ -1260,5 +1427,5 @@
 	public function EntryIf($attr,$content)
 	{
-		$if = new ArrayObject();
+		$if = array();
 		$extended = null;
 		$hascategory = null;
@@ -1317,4 +1484,9 @@
 		}
 		
+		if (isset($attr['has_attachment'])) {
+			$sign = (boolean) $attr['has_attachment'] ? '' : '!';
+			$if[] = $sign.'$_ctx->posts->countMedia()';
+		}
+		
 		if (isset($attr['comments_active'])) {
 			$sign = (boolean) $attr['comments_active'] ? '' : '!';
@@ -1353,8 +1525,6 @@
 		}
 		
-		$this->core->callBehavior('tplIfConditions','EntryIf',$attr,$content,$if);
-		
-		if (count($if) != 0) {
-			return '<?php if('.implode(' '.$operator.' ', (array) $if).') : ?>'.$content.'<?php endif; ?>';
+		if (!empty($if)) {
+			return '<?php if('.implode(' '.$operator.' ',$if).') : ?>'.$content.'<?php endif; ?>';
 		} else {
 			return $content;
@@ -1451,4 +1621,37 @@
 	}
 	
+	/*dtd
+	<!ELEMENT tpl:EntryAttachmentCount - O -- Number of attachments for entry -->
+	<!ATTLIST tpl:EntryAttachmentCount
+	none	CDATA	#IMPLIED	-- text to display for "no attachment" (default: no attachment)
+	one	CDATA	#IMPLIED	-- text to display for "one attachment" (default: one attachment)
+	more	CDATA	#IMPLIED	-- text to display for "more attachment" (default: %s attachment, %s is replaced by the number of attachments)
+	>
+	*/
+	public function EntryAttachmentCount($attr)
+	{
+		$none = 'no attachment';
+		$one = 'one attachment';
+		$more = '%d attachments';
+		
+		if (isset($attr['none'])) {
+			$none = addslashes($attr['none']);
+		}
+		if (isset($attr['one'])) {
+			$one = addslashes($attr['one']);
+		}
+		if (isset($attr['more'])) {
+			$more = addslashes($attr['more']);
+		}
+		
+		return
+		"<?php if (\$_ctx->posts->countMedia() == 0) {\n".
+		"  printf(__('".$none."'),(integer) \$_ctx->posts->countMedia());\n".
+		"} elseif (\$_ctx->posts->countMedia() == 1) {\n".
+		"  printf(__('".$one."'),(integer) \$_ctx->posts->countMedia());\n".
+		"} else {\n".
+		"  printf(__('".$more."'),(integer) \$_ctx->posts->countMedia());\n".
+		"} ?>";
+	}
 	
 	/*dtd
@@ -2006,8 +2209,6 @@
 		}
 		
-		$this->core->callBehavior('tplIfConditions','PaginationIf',$attr,$content,$if);
-		
-		if (count($if) != 0) {
-			return '<?php if('.implode(' && ', (array) $if).') : ?>'.$content.'<?php endif; ?>';
+		if (!empty($if)) {
+			return '<?php if('.implode(' && ',$if).') : ?>'.$content.'<?php endif; ?>';
 		} else {
 			return $content;
@@ -2281,8 +2482,6 @@
 		}
 		
-		$this->core->callBehavior('tplIfConditions','CommentIf',$attr,$content,$if);
-		
-		if (count($if) != 0) {
-			return '<?php if('.implode(' && ', (array) $if).') : ?>'.$content.'<?php endif; ?>';
+		if (!empty($if)) {
+			return '<?php if('.implode(' && ',$if).') : ?>'.$content.'<?php endif; ?>';
 		} else {
 			return $content;
@@ -2774,5 +2973,5 @@
 	current_mode		CDATA	#IMPLIED	-- tests if current URL mode is the one given in parameter
 	has_tpl			CDATA     #IMPLIED  -- tests if a named template exists
-	has_tag			CDATA     #IMPLIED  -- tests if a named template tag exists (see Tag plugin for code)
+	has_tag			CDATA     #IMPLIED  -- tests if a named template tag exists
 	blog_id			CDATA     #IMPLIED  -- tests if current blog ID is the one given in parameter
 	comments_active	(0|1)	#IMPLIED	-- test if comments are enabled blog-wide 
@@ -2784,5 +2983,5 @@
 	public function SysIf($attr,$content)
 	{
-		$if = new ArrayObject();
+		$if = array();
 		$is_ping = null;
 		
@@ -2830,4 +3029,13 @@
 		}
 		
+		if (isset($attr['has_tag'])) {
+			$sign = '';
+			if (substr($attr['has_tag'],0,1) == '!') {
+				$sign = '!';
+				$attr['has_tag'] = substr($attr['has_tag'],1);
+			}
+			$if[] =  $sign."(\$core->tpl->tagExists('".addslashes($attr['has_tag'])."') )";
+		}
+		
 		if (isset($attr['blog_id'])) {
 			$sign = '';
@@ -2859,8 +3067,6 @@
 		}
 		
-		$this->core->callBehavior('tplIfConditions','SysIf',$attr,$content,$if);
-		
-		if (count($if) != 0) {
-			return '<?php if('.implode(' '.$operator.' ', (array) $if).') : ?>'.$content.'<?php endif; ?>';
+		if (!empty($if)) {
+			return '<?php if('.implode(' '.$operator.' ',$if).') : ?>'.$content.'<?php endif; ?>';
 		} else {
 			return $content;
@@ -3024,7 +3230,4 @@
 		$this->closed = true;
 	}
-	public function isClosed() {
-		return $this->closed;
-	}
 	public function compile($tpl) {
 		if ($this->closed) {
Index: inc/public/default-templates/password-form.html
===================================================================
--- inc/public/default-templates/password-form.html	(revision 454)
+++ inc/public/default-templates/password-form.html	(revision 199)
@@ -34,5 +34,6 @@
 
 <p>{{tpl:lang You must give a password to access this area.}}</p>
-<p><label>{{tpl:lang Password:}} <input type="password" name="password" value="" /></label> <input type="submit" value="ok" /></p>
+<p><label>{{tpl:lang Password:}} <input type="password" name="password" value=""
+tabindex="1" /></label> <input type="submit" value="ok" /></p>
 
 </form>
Index: inc/public/default-templates/tag.html
===================================================================
--- inc/public/default-templates/tag.html	(revision 199)
+++ inc/public/default-templates/tag.html	(revision 199)
@@ -0,0 +1,153 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
+"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="{{tpl:BlogLanguage}}" lang="{{tpl:BlogLanguage}}">
+<head>
+  <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
+  <meta name="ROBOTS" content="{{tpl:BlogMetaRobots}}" />
+  
+  <title>{{tpl:lang Tag}} - {{tpl:TagID}} - {{tpl:BlogName encode_html="1"}}<tpl:PaginationIf start="0"> - {{tpl:lang page}} {{tpl:PaginationCurrent}}</tpl:PaginationIf></title>
+  <meta name="copyright" content="{{tpl:BlogCopyrightNotice encode_html="1"}}" />
+  <meta name="author" content="{{tpl:BlogEditor encode_html="1"}}" />
+  <meta name="date" scheme="W3CDTF" content="{{tpl:BlogUpdateDate iso8601="1"}}" />
+  
+  <link rel="schema.dc" href="http://purl.org/dc/elements/1.1/" />
+  <meta name="dc.title" lang="{{tpl:BlogLanguage}}" content="{{tpl:lang Tag}} - {{tpl:TagID}} - {{tpl:BlogName encode_html="1"}}<tpl:PaginationIf start="0"> - {{tpl:lang page}} {{tpl:PaginationCurrent}}</tpl:PaginationIf>" />
+  <meta name="dc.language" content="{{tpl:BlogLanguage}}" />
+  <meta name="dc.publisher" content="{{tpl:BlogEditor encode_html="1"}}" />
+  <meta name="dc.rights" content="{{tpl:BlogCopyrightNotice encode_html="1"}}" />
+  <meta name="dc.date" scheme="W3CDTF" content="{{tpl:BlogUpdateDate iso8601="1"}}" />
+  <meta name="dc.type" content="text" />
+  <meta name="dc.format" content="text/html" />
+  
+  <link rel="top" href="{{tpl:BlogURL}}" title="{{tpl:lang Home}}" />
+  <link rel="contents" href="{{tpl:BlogArchiveURL}}" title="{{tpl:lang Archives}}" />
+  
+  <tpl:Entries no_content="1">
+    <tpl:EntriesHeader>
+      <tpl:Pagination>
+        <tpl:PaginationIf end="0">
+        <link rel="prev" title="{{tpl:lang previous entries}}" href="{{tpl:PaginationURL offset="1"}}" />
+        </tpl:PaginationIf>
+        
+        <tpl:PaginationIf start="0">
+        <link rel="next" title="{{tpl:lang next entries}}" href="{{tpl:PaginationURL offset="-1"}}" />
+        </tpl:PaginationIf>
+      </tpl:Pagination>
+    </tpl:EntriesHeader>
+    
+    <link rel="chapter" href="{{tpl:EntryURL}}" title="{{tpl:EntryTitle encode_html="1"}}" />
+  </tpl:Entries>
+  
+  <link rel="alternate" type="application/atom+xml" title="Atom 1.0" href="{{tpl:TagFeedURL type="atom"}}" />
+  <link rel="meta" type="application/xbel+xml" title="Blogroll" href="{{tpl:BlogrollXbelLink}}" />
+  
+  {{tpl:include src="_head.html"}}
+</head>
+
+<body class="dc-tag">
+<div id="page">
+{{tpl:include src="_top.html"}}
+
+<div id="wrapper">
+
+<div id="main">
+  <div id="content">
+  
+  <div id="content-info">
+    <h2>{{tpl:lang Tag}} - {{tpl:TagID}}</h2>
+    
+    <p><a type="application/atom+xml" href="{{tpl:TagFeedURL type="atom"}}"
+    title="{{tpl:lang This tag's entries Atom feed}}" class="feed">{{tpl:lang Entries feed}}</a>
+    
+    <tpl:SysIf operator="or" comments_active="1" pings_active="1">
+    - <a type="application/atom+xml" href="{{tpl:TagFeedURL type="atom"}}/comments"
+    title="{{tpl:lang This tag's comments Atom feed}}" class="feed">{{tpl:lang Comments feed}}</a>
+    </tpl:SysIf>
+    </p>
+  </div>
+  
+  <tpl:Entries>
+    <div id="p{{tpl:EntryID}}" class="post {{tpl:EntryIfOdd}} {{tpl:EntryIfFirst}}" lang="{{tpl:EntryLang}}" xml:lang="{{tpl:EntryLang}}">
+    <!-- # New day date -->
+    <tpl:DateHeader><p class="day-date">{{tpl:EntryDate}}</p></tpl:DateHeader>
+    
+    <h2 class="post-title"><a
+    href="{{tpl:EntryURL}}">{{tpl:EntryTitle encode_html="1"}}</a></h2>
+    
+    <p class="post-info">{{tpl:lang By}} {{tpl:EntryAuthorLink}}
+    {{tpl:lang on}} {{tpl:EntryDate}}, {{tpl:EntryTime}}
+    <tpl:EntryIf has_category="1">
+    - <a href="{{tpl:EntryCategoryURL}}">{{tpl:EntryCategory encode_html="1"}}</a>
+    </tpl:EntryIf>
+    </p>
+    
+    <tpl:EntryTags>
+    <tpl:TagsHeader><ul class="post-tags"></tpl:TagsHeader>
+    <li><a href="{{tpl:TagURL}}">{{tpl:TagID}}</a></li>
+    <tpl:TagsFooter></ul></tpl:TagsFooter>
+    </tpl:EntryTags>
+    
+    <!-- # --BEHAVIOR-- publicEntryBeforeContent -->
+    {{tpl:SysBehavior behavior="publicEntryBeforeContent"}}
+    
+    <!-- # Entry with an excerpt -->
+    <tpl:EntryIf extended="1">
+      <div class="post-content">{{tpl:EntryExcerpt}}</div>
+      <p class="read-it"><a href="{{tpl:EntryURL}}"
+      title="{{tpl:lang Continue reading}} {{tpl:EntryTitle encode_html="1"}}">{{tpl:lang Continue
+      reading}}</a>...</p>
+    </tpl:EntryIf>
+    
+    <!-- # Entry without excerpt -->
+    <tpl:EntryIf extended="0">
+      <div class="post-content">{{tpl:EntryContent}}</div>
+    </tpl:EntryIf>
+    
+    <!-- # --BEHAVIOR-- publicEntryAfterContent -->
+    {{tpl:SysBehavior behavior="publicEntryAfterContent"}}
+    
+    <!-- # Number of comments, trackbacks and attachments -->
+    <tpl:EntryIf operator="or" show_comments="1" show_pings="1" has_attachment="1">
+      <p class="post-info-co">
+    </tpl:EntryIf>
+    <tpl:EntryIf show_comments="1">
+      <a href="{{tpl:EntryURL}}#comments" class="comment_count">{{tpl:EntryCommentCount}}</a>
+    </tpl:EntryIf>
+    <tpl:EntryIf show_pings="1">
+      <a href="{{tpl:EntryURL}}#pings" class="ping_count">{{tpl:EntryPingCount}}</a></tpl:EntryIf>
+    <tpl:EntryIf has_attachment="1">
+      <a href="{{tpl:EntryURL}}#attachments" class="attach_count">{{tpl:EntryAttachmentCount}}</a></tpl:EntryIf>
+    <tpl:EntryIf operator="or" show_comments="1" show_pings="1" has_attachment="1">
+      </p>
+    </tpl:EntryIf>
+    </div>
+    
+    <tpl:EntriesFooter>
+      <tpl:Pagination>
+        <p class="pagination"><tpl:PaginationIf end="0"><a href="{{tpl:PaginationURL offset="+1"}}" class="prev">&#171;
+        {{tpl:lang previous entries}}</a> - </tpl:PaginationIf>
+        {{tpl:lang page}} {{tpl:PaginationCurrent}} {{tpl:lang of}} {{tpl:PaginationCounter}}
+        <tpl:PaginationIf start="0"> - <a href="{{tpl:PaginationURL offset="-1"}}" class="next">{{tpl:lang next entries}}
+        &#187;</a></tpl:PaginationIf></p>
+      </tpl:Pagination>
+    </tpl:EntriesFooter>
+  </tpl:Entries>
+  </div>
+</div> <!-- End #main -->
+
+<div id="sidebar">
+  <div id="blognav">
+    {{tpl:Widgets type="nav"}}
+  </div> <!-- End #blognav -->
+  
+  <div id="blogextra">
+    {{tpl:Widgets type="extra"}}
+  </div> <!-- End #blogextra -->
+</div>
+
+</div> <!-- End #wrapper -->
+
+{{tpl:include src="_footer.html"}}
+</div> <!-- End #page -->
+</body>
+</html>
Index: inc/public/default-templates/tags.html
===================================================================
--- inc/public/default-templates/tags.html	(revision 199)
+++ inc/public/default-templates/tags.html	(revision 199)
@@ -0,0 +1,69 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
+"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="{{tpl:BlogLanguage}}" lang="{{tpl:BlogLanguage}}">
+<head>
+  <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
+  <meta name="ROBOTS" content="{{tpl:BlogMetaRobots robots="NOINDEX"}}" />
+  
+  <title>{{tpl:lang Tags}} - {{tpl:BlogName encode_html="1"}}</title>
+  <meta name="copyright" content="{{tpl:BlogCopyrightNotice encode_html="1"}}" />
+  <meta name="author" content="{{tpl:BlogEditor encode_html="1"}}" />
+  <meta name="date" scheme="W3CDTF" content="{{tpl:BlogUpdateDate iso8601="1"}}" />
+  
+  <link rel="schema.dc" href="http://purl.org/dc/elements/1.1/" />
+  <meta name="dc.title" lang="{{tpl:BlogLanguage}}" content="{{tpl:lang Tags}} - {{tpl:BlogName encode_html="1"}}" />
+  <meta name="dc.language" content="{{tpl:BlogLanguage}}" />
+  <meta name="dc.publisher" content="{{tpl:BlogEditor encode_html="1"}}" />
+  <meta name="dc.rights" content="{{tpl:BlogCopyrightNotice encode_html="1"}}" />
+  <meta name="dc.date" scheme="W3CDTF" content="{{tpl:BlogUpdateDate iso8601="1"}}" />
+  <meta name="dc.type" content="text" />
+  <meta name="dc.format" content="text/html" />
+  
+  <link rel="top" href="{{tpl:BlogURL}}" title="{{tpl:lang Home}}" />
+  <link rel="contents" href="{{tpl:BlogArchiveURL}}" title="{{tpl:lang Archives}}" />
+  
+  <link rel="meta" type="application/xbel+xml" title="Blogroll" href="{{tpl:BlogrollXbelLink}}" />
+  
+  {{tpl:include src="_head.html"}}
+</head>
+
+<body class="dc-tags">
+<div id="page">
+{{tpl:include src="_top.html"}}
+
+<div id="wrapper">
+
+<div id="main">
+  <div id="content">
+  
+  <div id="content-info">
+    <h2>{{tpl:lang Tags}}</h2>
+  </div>
+  
+  <div class="content-inner">  
+  <ul class="tags">
+    <tpl:Tags>
+      <li><a href="{{tpl:TagURL}}" class="tag{{tpl:TagRoundPercent}}">{{tpl:TagID}}</a></li>
+    </tpl:Tags>
+    </ul>
+  </div>
+  
+  </div>
+</div> <!-- End #main -->
+
+<div id="sidebar">
+  <div id="blognav">
+    {{tpl:Widgets type="nav"}}
+  </div> <!-- End #blognav -->
+  
+  <div id="blogextra">
+    {{tpl:Widgets type="extra"}}
+  </div> <!-- End #blogextra -->
+</div>
+
+</div> <!-- End #wrapper -->
+
+{{tpl:include src="_footer.html"}}
+</div> <!-- End #page -->
+</body>
+</html>
Index: inc/public/lib.tpl.context.php
===================================================================
--- inc/public/lib.tpl.context.php	(revision 478)
+++ inc/public/lib.tpl.context.php	(revision 0)
@@ -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
@@ -353,5 +353,5 @@
 		
 		$pattern = '(?:'.preg_quote($p_site,'/').')?'.preg_quote($p_url,'/');
-		$pattern = sprintf('/<img.+?src="%s(.*?\.(?:jpg|jpeg|gif|png))"[^>]+/msu',$pattern);
+		$pattern = sprintf('/<img.+?src="%s(.*?\.(?:jpg|gif|png))"[^>]+/msu',$pattern);
 		
 		$src = '';
@@ -420,6 +420,4 @@
 			} elseif (file_exists($f.'.jpg')) {
 				$res = $base.'.jpg';
-			} elseif (file_exists($f.'.jpeg')) {
-				$res = $base.'.jpeg';
 			} elseif (file_exists($f.'.png')) {
 				$res = $base.'.png';
Index: inc/public/lib.urlhandlers.php
===================================================================
--- inc/public/lib.urlhandlers.php	(revision 306)
+++ inc/public/lib.urlhandlers.php	(revision 0)
@@ -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
@@ -204,9 +204,6 @@
 		
 		$n = self::getPageNumber($args);
-		$params = new ArrayObject(array(
-			'lang' => $args));
-		
-		$core->callBehavior('publicLangBeforeGetLangs',$params,$args);
-		
+		
+		$params['lang'] = $args;
 		$_ctx->langs = $core->blog->getLangs($params);
 		
@@ -238,9 +235,6 @@
 		else
 		{
-			$params = new ArrayObject(array(
-				'cat_url' => $args,
-				'post_type' => 'post'));
-			
-			$core->callBehavior('publicCategoryBeforeGetCategories',$params,$args);
+			$params['cat_url'] = $args;
+			$params['post_type'] = 'post';
 			
 			$_ctx->categories = $core->blog->getCategories($params);
@@ -273,11 +267,7 @@
 		elseif (preg_match('|^/([0-9]{4})/([0-9]{2})$|',$args,$m))
 		{
-			$params = new ArrayObject(array(
-				'year' => $m[1],
-				'month' => $m[2],
-				'type' => 'month'));
-			
-			$core->callBehavior('publicArchiveBeforeGetDates',$params,$args);
-			
+			$params['year'] = $m[1];
+			$params['month'] = $m[2];
+			$params['type'] = 'month';
 			$_ctx->archives = $core->blog->getDates($params);
 			
@@ -310,9 +300,7 @@
 			$core->blog->withoutPassword(false);
 			
-			$params = new ArrayObject(array(
-				'post_url' => $args));
-			
-			$core->callBehavior('publicPostBeforeGetPosts',$params,$args);
-
+			$params = new ArrayObject();
+			$params['post_url'] = $args;
+			
 			$_ctx->posts = $core->blog->getPosts($params);
 			
@@ -422,5 +410,5 @@
 						
 						$redir = $_ctx->posts->getURL();
-						$redir .= $core->blog->settings->system->url_scan == 'query_string' ? '&' : '?';
+						$redir .= strpos($redir,'?') !== false ? '&' : '?';
 						
 						try
@@ -493,4 +481,5 @@
 		$cat_url = false;
 		$post_id = null;
+		$params = array();
 		$subtitle = '';
 		
@@ -501,9 +490,6 @@
 		
 		if (preg_match('!^([a-z]{2}(-[a-z]{2})?)/(.*)$!',$args,$m)) {
-			$params = new ArrayObject(array('lang' => $m[1]));
-			
+			$params['lang'] = $m[1];
 			$args = $m[3];
-			
-			$core->callBehavior('publicFeedBeforeGetLangs',$params,$args);
 			
 			$_ctx->langs = $core->blog->getLangs($params);
@@ -549,10 +535,6 @@
 		if ($cat_url)
 		{
-			$params = new ArrayObject(array(
-				'cat_url' => $cat_url,
-				'post_type' => 'post'));
-			
-			$core->callBehavior('publicFeedBeforeGetCategories',$params,$args);
-			
+			$params['cat_url'] = $cat_url;
+			$params['post_type'] = 'post';
 			$_ctx->categories = $core->blog->getCategories($params);
 			
@@ -567,10 +549,6 @@
 		elseif ($post_id)
 		{
-			$params = new ArrayObject(array(
-				'post_id' => $post_id,
-				'post_type' => ''));
-				
-			$core->callBehavior('publicFeedBeforeGetPosts',$params,$args);
-			
+			$params['post_id'] = $post_id;
+			$params['post_type'] = '';
 			$_ctx->posts = $core->blog->getPosts($params);
 			
Index: inc/public/prepend.php
===================================================================
--- inc/public/prepend.php	(revision 270)
+++ inc/public/prepend.php	(revision 0)
@@ -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
Index: inc/public/rs.extension.php
===================================================================
--- inc/public/rs.extension.php	(revision 270)
+++ inc/public/rs.extension.php	(revision 0)
@@ -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
