Index: admin/auth.php
===================================================================
--- admin/auth.php	(revision 2566)
+++ admin/auth.php	(revision 2593)
@@ -4,5 +4,5 @@
 # This file is part of Dotclear 2.
 #
-# Copyright (c) 2003-2013 Olivier Meunier & Association Dotclear
+# Copyright (c) 2003-2011 Olivier Meunier & Association Dotclear
 # Licensed under the GPL version 2.0 license.
 # See LICENSE file or
@@ -14,6 +14,5 @@
 
 # If we have a session cookie, go to index.php
-if (isset($_SESSION['sess_user_id']))
-{
+if (isset($_SESSION['sess_user_id'])) {
 	http::redirect('index.php');
 }
@@ -23,19 +22,7 @@
 $dlang = http::getAcceptLanguage();
 $dlang = ($dlang == '' ? 'en' : $dlang);
-if ($dlang != 'en' && preg_match('/^[a-z]{2}(-[a-z]{2})?$/',$dlang))
-{
-	l10n::lang($dlang);
+if ($dlang != 'en' && preg_match('/^[a-z]{2}(-[a-z]{2})?$/',$dlang)) {
 	l10n::set(dirname(__FILE__).'/../locales/'.$dlang.'/main');
 }
-
-$page_url = http::getHost().$_SERVER['REQUEST_URI'];
-
-$change_pwd = $core->auth->allowPassChange() && isset($_POST['new_pwd']) && isset($_POST['new_pwd_c']) && isset($_POST['login_data']);
-$login_data = !empty($_POST['login_data']) ? html::escapeHTML($_POST['login_data']) : null;
-$recover = $core->auth->allowPassChange() && !empty($_REQUEST['recover']);
-$safe_mode = !empty($_REQUEST['safe_mode']);
-$akey = $core->auth->allowPassChange() && !empty($_GET['akey']) ? $_GET['akey'] : null;
-$user_id = $user_pwd = $user_key = $user_email = null;
-$err = $msg = null;
 
 # Auto upgrade
@@ -44,400 +31,309 @@
 	try {
 		if (($changes = dotclearUpgrade($core)) !== false) {
-			$msg = __('Dotclear has been upgraded.').'<!-- '.$changes.' -->';
-		}
-	} catch (Exception $e) {
-		$err = $e->getMessage();
-	}
-}
-
-# If we have POST login informations, go throug auth process
-if (!empty($_POST['user_id']) && !empty($_POST['user_pwd']))
+			$_ctx->setAlert(__('Dotclear has been upgraded.').'<!-- '.$changes.' -->');
+		}
+	}
+	catch (Exception $e) {
+		$_ctx->addError($e->getMessage());
+	}
+}
+
+/**
+Actions for authentication on admin pages
+*/
+class adminPageAuth
 {
-	$user_id = !empty($_POST['user_id']) ? $_POST['user_id'] : null;
-	$user_pwd = !empty($_POST['user_pwd']) ? $_POST['user_pwd'] : null;
-}
-# If we have COOKIE login informations, go throug auth process
-elseif (isset($_COOKIE['dc_admin']) && strlen($_COOKIE['dc_admin']) == 104)
-{
+	# Send new password from recover email
+	public static function send($akey)
+	{
+		global $core, $_ctx;
+		
+		$_ctx->akey = true;
+		
+		try	{
+			$recover_res = $core->auth->recoverUserPassword($akey);
+			
+			$subject = mb_encode_mimeheader('DotClear '.__('Your new password'),'UTF-8','B');
+			$message =
+			__('Username:').' '.$recover_res['user_id']."\n".
+			__('Password:').' '.$recover_res['new_pass']."\n\n".
+			preg_replace('/\?(.*)$/','',http::getHost().$_SERVER['REQUEST_URI']);
+			
+			$headers[] = 'From: dotclear@'.$_SERVER['HTTP_HOST'];
+			$headers[] = 'Content-Type: text/plain; charset=UTF-8;';
+			
+			mail::sendMail($recover_res['user_email'],$subject,$message,$headers);
+			$_ctx->setAlert(__('Your new password is in your mailbox.'));
+		}
+		catch (Exception $e) {
+			$_ctx->addError($e->getMessage());
+		}
+	}
+	
+	# Authentication process
+	public static function process($form,$user_id,$user_pwd,$user_key=null)
+	{
+		global $core, $_ctx;
+		
+		# We check the user
+		$check_user = $core->auth->checkUser($user_id,$user_pwd,$user_key) === true;
+		
+		$cookie_admin = http::browserUID(DC_MASTER_KEY.$user_id.
+			crypt::hmac(DC_MASTER_KEY,$user_pwd)).bin2hex(pack('a32',$user_id));
+		
+		if ($check_user && $core->auth->mustChangePassword())
+		{
+			$form->login_data = join('/',array(
+				base64_encode($user_id),
+				$cookie_admin,
+				$form->user_remember == '' ? '0' : '1'
+			));
+			
+			if (!$core->auth->allowPassChange()) {
+				$_ctx->addError(__('You have to change your password before you can login.'));
+			} else {
+				$_ctx->addError(__('In order to login, you have to change your password now.'));
+				$_ctx->change_pwd = true;
+			}
+		}
+		elseif ($check_user && $form->safe_mode != '' && !$core->auth->isSuperAdmin()) 
+		{
+			$_ctx->addError(__('Safe Mode can only be used for super administrators.'));
+		}
+		elseif ($check_user)
+		{
+			$core->session->start();
+			$_SESSION['sess_user_id'] = $user_id;
+			$_SESSION['sess_browser_uid'] = http::browserUID(DC_MASTER_KEY);
+			
+			if ($form->blog != '') {
+				$_SESSION['sess_blog_id'] = $form->blog;
+			}
+			
+			if ($form->safe_mode != '' && $core->auth->isSuperAdmin()) {
+				$_SESSION['sess_safe_mode'] = true;
+			}
+			
+			if ($form->user_remember != '') {
+				setcookie('dc_admin',$cookie_admin,strtotime('+15 days'),'','',DC_ADMIN_SSL);
+			}
+			
+			http::redirect('index.php');
+		}
+		else
+		{
+			if (isset($_COOKIE['dc_admin'])) {
+				unset($_COOKIE['dc_admin']);
+				setcookie('dc_admin',false,-600,'','',DC_ADMIN_SSL);
+			}
+			$_ctx->addError(__('Wrong username or password'));
+		}
+	}
+	
+	# Login form action
+	public static function login($form)
+	{
+		global $_ctx;
+		
+		if ($form->user_id != '' && $form->user_pwd != '') {
+			self::process($form,$form->user_id,$form->user_pwd);
+		}
+		
+		# Send post values to form
+		$form->user_id = $form->user_id;
+	}
+
+	# Recover password form action
+	public static function recover($form)
+	{
+		global $core, $_ctx;
+		
+		if ($form->user_id == '' || $form->user_email == '') {
+			return;
+		}
+		
+		$user_id = $form->user_id;
+		$user_email = $form->user_email;
+		$page_url = http::getHost().$_SERVER['REQUEST_URI'];
+		
+		try {
+			$recover_key = $core->auth->setRecoverKey($user_id,$user_email);
+			
+			$subject = mail::B64Header('DotClear '.__('Password reset'));
+			$message =
+			__('Someone has requested to reset the password for the following site and username.')."\n\n".
+			$page_url."\n".__('Username:').' '.$user_id."\n\n".
+			__('To reset your password visit the following address, otherwise just ignore this email and nothing will happen.')."\n".
+			$page_url.'?akey='.$recover_key;
+			
+			$headers[] = 'From: '.(defined('DC_ADMIN_MAILFROM') && DC_ADMIN_MAILFROM ? DC_ADMIN_MAILFROM : 'dotclear@local');
+			$headers[] = 'Content-Type: text/plain; charset=UTF-8;';
+			
+			mail::sendMail($user_email,$subject,$message,$headers);
+			$_ctx->setAlert(sprintf(__('The e-mail was sent successfully to %s.'),$user_email));
+		}
+		catch (Exception $e) {
+			$_ctx->addError($e->getMessage());
+		}
+		
+		# Send post values to form
+		$form->user_id = $form->user_id;
+		$form->user_email = $form->user_email;
+	}
+
+	# Change password form action
+	public static function change($form)
+	{
+		global $core, $_ctx;
+		
+		if ($form->login_data) {
+			return;
+		}
+		$_ctx->change_pwd = true;
+		
+		$new_pwd = (string) $form->new_pwd;
+		$new_pwd_c = (string) $form->new_pwd_c;
+		
+		try {
+			$tmp_data = explode('/',$form->login_data);
+			if (count($tmp_data) != 3) {
+				throw new Exception();
+			}
+			$data = array(
+				'user_id'=>base64_decode($tmp_data[0]),
+				'cookie_admin'=>$tmp_data[1],
+				'user_remember'=>$tmp_data[2]=='1'
+			);
+			if ($data['user_id'] === false) {
+				throw new Exception();
+			}
+			
+			# Check login informations
+			$check_user = false;
+			if (isset($data['cookie_admin']) && strlen($data['cookie_admin']) == 104)
+			{
+				$user_id = substr($data['cookie_admin'],40);
+				$user_id = @unpack('a32',@pack('H*',$user_id));
+				if (is_array($user_id))
+				{
+					$user_id = $user_id[1];
+					$user_key = substr($data['cookie_admin'],0,40);
+					$check_user = $core->auth->checkUser($user_id,null,$user_key) === true;
+				}
+			}
+			
+			if (!$core->auth->allowPassChange() || !$check_user) {
+				$_ctx->change_pwd = false;
+				throw new Exception();
+			}
+			
+			if ($new_pwd != $new_pwd_c) {
+				throw new Exception(__("Passwords don't match"));
+			}
+			
+			if ($core->auth->checkUser($user_id,$new_pwd) === true) {
+				throw new Exception(__("You didn't change your password."));
+			}
+			
+			$cur = $core->con->openCursor($core->prefix.'user');
+			$cur->user_change_pwd = 0;
+			$cur->user_pwd = $new_pwd;
+			$core->updUser($core->auth->userID(),$cur);
+			
+			$core->session->start();
+			$_SESSION['sess_user_id'] = $user_id;
+			$_SESSION['sess_browser_uid'] = http::browserUID(DC_MASTER_KEY);
+			
+			if ($data['user_remember']) {
+				setcookie('dc_admin',$data['cookie_admin'],strtotime('+15 days'),'','',DC_ADMIN_SSL);
+			}
+			
+			http::redirect('index.php');
+		}
+		catch (Exception $e) {
+			$_ctx->addError($e->getMessage());
+		}
+		
+		# Send post values to form
+		$form->login_data = $form->login_data;
+	}
+}
+
+# Form fields
+$form = new dcForm($core,'auth','auth.php');
+$form
+	->addField(
+		new dcFieldText('user_id','',array(
+			"label" => __('Username:'),
+			"maxlength" => 32)))
+	->addField(
+		new dcFieldPassword('user_pwd','',array(
+			"label" => __('Password:'))))
+	->addField(
+		new dcFieldText('user_email','',array(
+			"label" => __('Email:'))))
+	->addField(
+		new dcFieldPassword('new_pwd','',array(
+			"label" => __('New password:'))))
+	->addField(
+		new dcFieldPassword('new_pwd_c','',array(
+			"label" => __('Confirm password:'))))
+	->addField(
+		new dcFieldCheckbox ('user_remember',1,array(
+			"label" => __('Remember my ID on this computer'))))
+	->addField(
+		new dcFieldSubmit('auth_login',__('log in'),array(
+			'action' => array('adminPageAuth','login'))))
+	->addField(
+		new dcFieldSubmit('auth_recover',__('recover'),array(
+			'action' => array('adminPageAuth','recover'))))
+	->addField(
+		new dcFieldSubmit('auth_change',__('change'),array(
+			'action' => array('adminPageAuth','change'))))
+	->addField(
+		new dcFieldHidden ('safe_mode','0'))
+	->addField(
+		new dcFieldHidden ('recover','0'))
+	->addField(
+		new dcFieldHidden ('login_data',''))
+	->addField(
+		new dcFieldHidden ('blog',''));
+
+# Context variables
+$_ctx->allow_pass_change = $core->auth->allowPassChange();
+$_ctx->change_pwd = $core->auth->allowPassChange() && $form->new_pwd != '' && $form->new_pwd_c != '' && $form->login_data != '';
+$_ctx->recover = $form->recover = $core->auth->allowPassChange() && !empty($_REQUEST['recover']);
+$_ctx->setSafeMode(!empty($_REQUEST['safe_mode'])); 
+$form->safe_mode = !empty($_REQUEST['safe_mode']);
+$_ctx->akey = false;
+$_ctx->dlang = $dlang;
+
+# If we have no POST login informations and have COOKIE login informations, go throug auth process
+if ($form->user_id == '' && $form->user_pwd == '' 
+ && isset($_COOKIE['dc_admin']) && strlen($_COOKIE['dc_admin']) == 104) {
+
 	# If we have a remember cookie, go through auth process with user_key
 	$user_id = substr($_COOKIE['dc_admin'],40);
 	$user_id = @unpack('a32',@pack('H*',$user_id));
-	if (is_array($user_id))
-	{
+	
+	if (is_array($user_id)) {
 		$user_id = $user_id[1];
 		$user_key = substr($_COOKIE['dc_admin'],0,40);
-		$user_pwd = null;
-	}
-	else
-	{
-		$user_id = null;
-	}
-}
-
-# Recover password
-if ($recover && !empty($_POST['user_id']) && !empty($_POST['user_email']))
-{
-	$user_id = !empty($_POST['user_id']) ? $_POST['user_id'] : null;
-	$user_email = !empty($_POST['user_email']) ? $_POST['user_email'] : '';
-	try
-	{
-		$recover_key = $core->auth->setRecoverKey($user_id,$user_email);
-
-		$subject = mail::B64Header('DotClear '.__('Password reset'));
-		$message =
-		__('Someone has requested to reset the password for the following site and username.')."\n\n".
-		$page_url."\n".__('Username:').' '.$user_id."\n\n".
-		__('To reset your password visit the following address, otherwise just ignore this email and nothing will happen.')."\n".
-		$page_url.'?akey='.$recover_key;
-
-		$headers[] = 'From: '.(defined('DC_ADMIN_MAILFROM') && DC_ADMIN_MAILFROM ? DC_ADMIN_MAILFROM : 'dotclear@local');
-		$headers[] = 'Content-Type: text/plain; charset=UTF-8;';
-
-		mail::sendMail($user_email,$subject,$message,$headers);
-		$msg = sprintf(__('The e-mail was sent successfully to %s.'),$user_email);
-	}
-	catch (Exception $e)
-	{
-		$err = $e->getMessage();
-	}
-}
-# Send new password
-elseif ($akey)
-{
-	try
-	{
-		$recover_res = $core->auth->recoverUserPassword($akey);
-
-		$subject = mb_encode_mimeheader('DotClear '.__('Your new password'),'UTF-8','B');
-		$message =
-		__('Username:').' '.$recover_res['user_id']."\n".
-		__('Password:').' '.$recover_res['new_pass']."\n\n".
-		preg_replace('/\?(.*)$/','',$page_url);
-
-		$headers[] = 'From: dotclear@'.$_SERVER['HTTP_HOST'];
-		$headers[] = 'Content-Type: text/plain; charset=UTF-8;';
-
-		mail::sendMail($recover_res['user_email'],$subject,$message,$headers);
-		$msg = __('Your new password is in your mailbox.');
-	}
-	catch (Exception $e)
-	{
-		$err = $e->getMessage();
-	}
-}
-# Change password and retry to log
-elseif ($change_pwd)
-{
-	try
-	{
-		$tmp_data = explode('/',$_POST['login_data']);
-		if (count($tmp_data) != 3) {
-			throw new Exception();
-		}
-		$data = array(
-			'user_id'=>base64_decode($tmp_data[0]),
-			'cookie_admin'=>$tmp_data[1],
-			'user_remember'=>$tmp_data[2]=='1'
-		);
-		if ($data['user_id'] === false) {
-			throw new Exception();
-		}
-
-	# Check login informations
-	$check_user = false;
-	if (isset($data['cookie_admin']) && strlen($data['cookie_admin']) == 104)
-	{
-		$user_id = substr($data['cookie_admin'],40);
-		$user_id = @unpack('a32',@pack('H*',$user_id));
-		if (is_array($user_id))
-		{
-			$user_id = $user_id[1];
-			$user_key = substr($data['cookie_admin'],0,40);
-			$check_user = $core->auth->checkUser($user_id,null,$user_key) === true;
-		}
-	}
-
-		if (!$core->auth->allowPassChange() || !$check_user) {
-			$change_pwd = false;
-			throw new Exception();
-		}
-
-		if ($_POST['new_pwd'] != $_POST['new_pwd_c']) {
-			throw new Exception(__("Passwords don't match"));
-		}
-
-		if ($core->auth->checkUser($user_id,$_POST['new_pwd']) === true) {
-			throw new Exception(__("You didn't change your password."));
-		}
-
-		$cur = $core->con->openCursor($core->prefix.'user');
-		$cur->user_change_pwd = 0;
-		$cur->user_pwd = $_POST['new_pwd'];
-		$core->updUser($core->auth->userID(),$cur);
-
-		$core->session->start();
-		$_SESSION['sess_user_id'] = $user_id;
-		$_SESSION['sess_browser_uid'] = http::browserUID(DC_MASTER_KEY);
-
-		if ($data['user_remember'])
-		{
-			setcookie('dc_admin',$data['cookie_admin'],strtotime('+15 days'),'','',DC_ADMIN_SSL);
-		}
-
-		http::redirect('index.php');
-	}
-	catch (Exception $e)
-	{
-		$err = $e->getMessage();
-	}
-}
-# Try to log
-elseif ($user_id !== null && ($user_pwd !== null || $user_key !== null))
-{
-	# We check the user
-	$check_user = $core->auth->checkUser($user_id,$user_pwd,$user_key,false) === true;
-	if ($check_user) {
-		$check_perms = $core->auth->findUserBlog() !== false;
-	} else {
-		$check_perms = false;
-	}
-
-	$cookie_admin = http::browserUID(DC_MASTER_KEY.$user_id.
-		crypt::hmac(DC_MASTER_KEY,$user_pwd)).bin2hex(pack('a32',$user_id));
-
-	if ($check_perms && $core->auth->mustChangePassword())
-	{
-		$login_data = join('/',array(
-			base64_encode($user_id),
-			$cookie_admin,
-			empty($_POST['user_remember'])?'0':'1'
-		));
-
-		if (!$core->auth->allowPassChange()) {
-			$err = __('You have to change your password before you can login.');
-		} else {
-			$err = __('In order to login, you have to change your password now.');
-			$change_pwd = true;
-		}
-	}
-	elseif ($check_perms && !empty($_POST['safe_mode']) && !$core->auth->isSuperAdmin())
-	{
-		$err = __('Safe Mode can only be used for super administrators.');
-	}
-	elseif ($check_perms)
-	{
-		$core->session->start();
-		$_SESSION['sess_user_id'] = $user_id;
-		$_SESSION['sess_browser_uid'] = http::browserUID(DC_MASTER_KEY);
-
-		if (!empty($_POST['blog'])) {
-			$_SESSION['sess_blog_id'] = $_POST['blog'];
-		}
-
-		if (!empty($_POST['safe_mode']) && $core->auth->isSuperAdmin()) {
-			$_SESSION['sess_safe_mode'] = true;
-		}
-
-		if (!empty($_POST['user_remember'])) {
-			setcookie('dc_admin',$cookie_admin,strtotime('+15 days'),'','',DC_ADMIN_SSL);
-		}
-
-		http::redirect('index.php');
-	}
-	else
-	{
-		if (isset($_COOKIE['dc_admin'])) {
-			unset($_COOKIE['dc_admin']);
-			setcookie('dc_admin',false,-600,'','',DC_ADMIN_SSL);
-		}
-		if ($check_user) {
-			$err = __('Insufficient permissions');
-		} else {
-			$err = __('Wrong username or password');
-		}
-	}
+		$user_pwd = '';
+		
+		adminPageAuth::process($form,$user_id,$user_pwd,$user_key);
+	}
+}
+# If we have an akey, go throug send password process
+elseif ($core->auth->allowPassChange() && !empty($_GET['akey'])) {
+	adminPageAuth::send($_GET['akey']);
 }
 
 if (isset($_GET['user'])) {
-	$user_id = $_GET['user'];
-}
-
-header('Content-Type: text/html; charset=UTF-8');
+	$form->user_id = $_GET['user'];
+}
+
+$form->setup();
+
+$core->tpl->display('auth.html.twig');
 ?>
-<!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="<?php echo $dlang; ?>" lang="<?php echo $dlang; ?>">
-<head>
-  <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
-  <meta http-equiv="Content-Script-Type" content="text/javascript" />
-  <meta http-equiv="Content-Style-Type" content="text/css" />
-  <meta http-equiv="Content-Language" content="<?php echo $dlang; ?>" />
-  <meta name="ROBOTS" content="NOARCHIVE,NOINDEX,NOFOLLOW" />
-  <meta name="GOOGLEBOT" content="NOSNIPPET" />
-  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
-  <title><?php echo html::escapeHTML(DC_VENDOR_NAME); ?></title>
-  <link rel="icon" type="image/png" href="images/favicon96-logout.png" />
-  <link rel="shortcut icon" href="favicon.ico" type="image/x-icon" />
-
-
-<?php
-echo dcPage::jsLoadIE7();
-echo dcPage::jsCommon();
-?>
-
-	<link rel="stylesheet" href="style/default.css" type="text/css" media="screen" />
-
-  <?php
-  # --BEHAVIOR-- loginPageHTMLHead
-  $core->callBehavior('loginPageHTMLHead');
-  ?>
-
-  <script type="text/javascript">
-  //<![CDATA[
-  $(window).load(function() {
-    var uid = $('input[name=user_id]');
-    var upw = $('input[name=user_pwd]');
-    uid.focus();
-
-    if (upw.length == 0) { return; }
-
-    uid.keypress(processKey);
-
-    function processKey(evt) {
-      if (evt.which == 13 && upw.val() == '') {
-         upw.focus();
-	    return false;
-      }
-	 return true;
-    };
-    $.cookie('dc_admin_test_cookie',true);
-    if ($.cookie('dc_admin_test_cookie')) {
-      $('#cookie_help').hide();
-      $.cookie('dc_admin_test_cookie', '', {'expires': -1});
-    } else {
-      $('#cookie_help').show();
-    }
-    $('#issue #more').toggleWithLegend($('#issue').children().not('#more'));
-  });
-  //]]>
-  </script>
-</head>
-
-<body id="dotclear-admin" class="auth">
-
-<form action="auth.php" method="post" id="login-screen">
-<h1><?php echo html::escapeHTML(DC_VENDOR_NAME); ?></h1>
-
-<?php
-if ($err) {
-	echo '<div class="error">'.$err.'</div>';
-}
-if ($msg) {
-	echo '<p class="success">'.$msg.'</p>';
-}
-
-if ($akey)
-{
-	echo '<p><a href="auth.php">'.__('Back to login screen').'</a></p>';
-}
-elseif ($recover)
-{
-	echo
-	'<div class="fieldset"><h2>'.__('Request a new password').'</h2>'.
-	'<p><label for="user_id">'.__('Username:').'</label> '.
-	form::field(array('user_id','user_id'),20,32,html::escapeHTML($user_id)).'</p>'.
-
-	'<p><label for="user_email">'.__('Email:').'</label> '.
-	form::field(array('user_email','user_email'),20,255,html::escapeHTML($user_email)).'</p>'.
-
-	'<p><input type="submit" value="'.__('recover').'" />'.
-	form::hidden(array('recover'),1).'</p>'.
-	'</div>'.
-
-	'<div id="issue">'.
-	'<p><a href="auth.php">'.__('Back to login screen').'</a></p>'.
-	'</div>';
-}
-elseif ($change_pwd)
-{
-	echo
-	'<div class="fieldset"><h2>'.__('Change your password').'</h2>'.
-	'<p><label for="new_pwd">'.__('New password:').'</label> '.
-	form::password(array('new_pwd','new_pwd'),20,255).'</p>'.
-
-	'<p><label for="new_pwd_c">'.__('Confirm password:').'</label> '.
-	form::password(array('new_pwd_c','new_pwd_c'),20,255).'</p>'.
-	'</div>'.
-
-	'<p><input type="submit" value="'.__('change').'" />'.
-	form::hidden('login_data',$login_data).'</p>';
-}
-else
-{
-	if (is_callable(array($core->auth,'authForm')))
-	{
-		echo $core->auth->authForm($user_id);
-	}
-	else
-	{
-		if ($safe_mode) {
-			echo '<div class="fieldset">';
-			echo '<h2>'.__('Safe mode login').'</h2>';
-			echo
-				'<p class="form-note">'.
-				__('This mode allows you to login without activating any of your plugins. This may be useful to solve compatibility problems').'&nbsp;</p>'.
-				'<p class="form-note">'.__('Disable or delete any plugin suspected to cause trouble, then log out and log back in normally.').
-				'</p>';
-		}
-		else {
-			echo '<div class="fieldset">';
-		}
-
-		echo
-		'<p><label for="user_id">'.__('Username:').'</label> '.
-		form::field(array('user_id','user_id'),20,32,html::escapeHTML($user_id)).'</p>'.
-
-		'<p><label for="user_pwd">'.__('Password:').'</label> '.
-		form::password(array('user_pwd','user_pwd'),20,255).'</p>'.
-
-		'<p>'.
-		form::checkbox(array('user_remember','user_remember'),1).
-		'<label for="user_remember" class="classic">'.
-		__('Remember my ID on this computer').'</label></p>'.
-
-		'<p><input type="submit" value="'.__('log in').'" class="login" /></p>';
-
-		if (!empty($_REQUEST['blog'])) {
-			echo form::hidden('blog',html::escapeHTML($_REQUEST['blog']));
-		}
-		if($safe_mode) {
-			echo
-			form::hidden('safe_mode',1).
-			'</div>';
-		}
-		else {
-			echo '</div>';
-		}
-		echo
-		'<p id="cookie_help" class="error">'.__('You must accept cookies in order to use the private area.').'</p>';
-
-		echo '<div id="issue">';
-
-		if ($safe_mode) {
-			echo
-			'<p><a href="auth.php" id="normal_mode_link">'.__('Get back to normal authentication').'</a></p>';
-		} else {
-			echo '<p id="more"><strong>'.__('Connection issue?').'</strong></p>';
-			if ($core->auth->allowPassChange()) {
-				echo '<p><a href="auth.php?recover=1">'.__('I forgot my password').'</a></p>';
-			}
-			echo '<p><a href="auth.php?safe_mode=1" id="safe_mode_link">'.__('I want to log in in safe mode').'</a></p>';
-		}
-
-		echo '</div>';
-	}
-}
-?>
-</form>
-</body>
-</html>
Index: admin/index.php
===================================================================
--- admin/index.php	(revision 2566)
+++ admin/index.php	(revision 2593)
@@ -15,4 +15,9 @@
 	exit;
 }
+if (!empty($_GET['tf'])) {
+	define('DC_CONTEXT_ADMIN',true);
+	require dirname(__FILE__).'/../inc/load_theme_file.php';
+	exit;
+}
 
 require dirname(__FILE__).'/../inc/admin/prepend.php';
@@ -43,6 +48,35 @@
 $plugins_install = $core->plugins->installModules();
 
+# Send plugins install messages to templates
+if (!empty($plugins_install['success'])) {
+	$_ctx->addMessagesList(__('Following plugins have been installed:'),$plugins_install['success']);
+}
+if (!empty($plugins_install['failure'])) {
+	$_ctx->addMessagesList(__('Following plugins have not been installed:'),$plugins_install['failure']);
+}
+
+# Send plugins errors messages to templates
+$_ctx->modules_errors = $core->auth->isSuperAdmin() ? $core->plugins->getErrors() : array();
+
+# Send Dotclear updates notifications to tempaltes
+$_ctx->updater = array();
+if ($core->auth->isSuperAdmin() && is_readable(DC_DIGESTS)) {
+
+	$updater = new dcUpdate(DC_UPDATE_URL,'dotclear',DC_UPDATE_VERSION,DC_TPL_CACHE.'/versions');
+	$new_v = $updater->check(DC_VERSION);
+	$version_info = $new_v ? $updater->getInfoURL() : '';
+	
+	if ($updater->getNotify() && $new_v) {
+		$_ctx->updater = array(
+			'new_version'	=> $new_v,
+			'version_info'	=> $version_info
+		);
+	}
+}
+
 # Check dashboard module prefs
 $ws = $core->auth->user_prefs->addWorkspace('dashboard');
+
+# Doclinks prefs
 if (!$core->auth->user_prefs->dashboard->prefExists('doclinks')) {
 	if (!$core->auth->user_prefs->dashboard->prefExists('doclinks',true)) {
@@ -51,4 +85,12 @@
 	$core->auth->user_prefs->dashboard->put('doclinks',true,'boolean');
 }
+
+# Send doclinks to templates
+$_ctx->dashboard_doclinks = array();
+if ($core->auth->user_prefs->dashboard->doclinks && !empty($__resources['doc'])) {
+	$_ctx->dashboard_doclinks = $__resources['doc'];
+}
+
+# Dcnews prefs
 if (!$core->auth->user_prefs->dashboard->prefExists('dcnews')) {
 	if (!$core->auth->user_prefs->dashboard->prefExists('dcnews',true)) {
@@ -57,56 +99,10 @@
 	$core->auth->user_prefs->dashboard->put('dcnews',true,'boolean');
 }
-if (!$core->auth->user_prefs->dashboard->prefExists('quickentry')) {
-	if (!$core->auth->user_prefs->dashboard->prefExists('quickentry',true)) {
-		$core->auth->user_prefs->dashboard->put('quickentry',false,'boolean','',null,true);
-	}
-	$core->auth->user_prefs->dashboard->put('quickentry',false,'boolean');
-}
-
-// Handle folded/unfolded sections in admin from user preferences
-$ws = $core->auth->user_prefs->addWorkspace('toggles');
-if (!$core->auth->user_prefs->toggles->prefExists('unfolded_sections')) {
-	$core->auth->user_prefs->toggles->put('unfolded_sections','','string','Folded sections in admin',null,true);
-}
-
-
-# Dashboard icons
-$__dashboard_icons = new ArrayObject();
-
-$favs = $core->favs->getUserFavorites();
-$core->favs->appendDashboardIcons($__dashboard_icons);
-
-# Check plugins and themes update from repository
-function dc_check_store_update($mod, $url, $img, $icon)
-{
-	$repo = new dcStore($mod, $url);
-	$upd = $repo->get(true);
-	if (!empty($upd)) {
-		$icon[0] .= '<br />'.sprintf(__('An update is available', '%s updates are available.', count($upd)),count($upd));
-		$icon[1] .= '#update';
-		$icon[2] = 'images/menu/'.$img.'-b-update.png';
-	}
-}
-if (isset($__dashboard_icons['plugins'])) {
-	dc_check_store_update($core->plugins, $core->blog->settings->system->store_plugin_url, 'plugins', $__dashboard_icons['plugins']);
-}
-if (isset($__dashboard_icons['blog_theme'])) {
-	$themes = new dcThemes($core);
-	$themes->loadModules($core->blog->themes_path, null);
-	dc_check_store_update($themes, $core->blog->settings->system->store_theme_url, 'blog-theme', $__dashboard_icons['blog_theme']);
-}
-
-# Latest news for dashboard
-$__dashboard_items = new ArrayObject(array(new ArrayObject(),new ArrayObject()));
-
-$dashboardItem = 0;
-
-if ($core->auth->user_prefs->dashboard->dcnews) {
+
+# Send dcnews to templates
+$_ctx->dashboard_dcnews = array();
+if ($core->auth->user_prefs->dashboard->dcnews && !empty($__resources['rss_news'])) {
 	try
 	{
-		if (empty($__resources['rss_news'])) {
-			throw new Exception();
-		}
-
 		$feed_reader = new feedReader;
 		$feed_reader->setCacheDir(DC_TPL_CACHE);
@@ -114,29 +110,18 @@
 		$feed_reader->setUserAgent('Dotclear - http://www.dotclear.org/');
 		$feed = $feed_reader->parse($__resources['rss_news']);
-		if ($feed)
-		{
-			$latest_news = '<div class="box medium dc-box"><h3>'.__('Dotclear news').'</h3><dl id="news">';
+		if ($feed) {
+			$items = array();
 			$i = 1;
-			foreach ($feed->items as $item)
-			{
-				$dt = isset($item->link) ? '<a href="'.$item->link.'" class="outgoing" title="'.$item->title.'">'.
-					$item->title.' <img src="images/outgoing-blue.png" alt="" /></a>' : $item->title;
-
-				if ($i < 3) {
-					$latest_news .=
-					'<dt>'.$dt.'</dt>'.
-					'<dd><p><strong>'.dt::dt2str(__('%d %B %Y:'),$item->pubdate,'Europe/Paris').'</strong> '.
-					'<em>'.text::cutString(html::clean($item->content),120).'...</em></p></dd>';
-				} else {
-					$latest_news .=
-					'<dt>'.$dt.'</dt>'.
-					'<dd>'.dt::dt2str(__('%d %B %Y:'),$item->pubdate,'Europe/Paris').'</dd>';
-				}
+			foreach ($feed->items as $item) {
+				$items[] = array(
+					'title' => $item->title,
+					'link' => isset($item->link) ? $item->link : '',
+					'date' => dt::dt2str(__('%d %B %Y'),$item->pubdate,'Europe/Paris'),
+					'content' => html::clean($item->content)
+				);
 				$i++;
-				if ($i > 2) { break; }
+				if ($i > 3) { break; }
 			}
-			$latest_news .= '</dl></div>';
-			$__dashboard_items[$dashboardItem][] = $latest_news;
-			$dashboardItem++;
+			$_ctx->dashboard_dcnews = $items;
 		}
 	}
@@ -144,243 +129,180 @@
 }
 
-# Documentation links
-if ($core->auth->user_prefs->dashboard->doclinks) {
-	if (!empty($__resources['doc']))
-	{
-		$doc_links = '<div class="box small dc-box"><h3>'.__('Documentation and support').'</h3><ul>';
-
-		foreach ($__resources['doc'] as $k => $v) {
-			$doc_links .= '<li><a class="outgoing" href="'.$v.'" title="'.$k.'">'.$k.
-			' <img src="images/outgoing-blue.png" alt="" /></a></li>';
-		}
-
-		$doc_links .= '</ul></div>';
-		$__dashboard_items[$dashboardItem][] = $doc_links;
-		$dashboardItem++;
-	}
-}
-
+# Quick entry prefs
+if (!$core->auth->user_prefs->dashboard->prefExists('quickentry')) {
+	if (!$core->auth->user_prefs->dashboard->prefExists('quickentry',true)) {
+		$core->auth->user_prefs->dashboard->put('quickentry',true,'boolean','',null,true);
+	}
+	$core->auth->user_prefs->dashboard->put('quickentry',true,'boolean');
+}
+
+# Send quick entry to templates
+$_ctx->dashboard_quickentry = false;
+if ($core->auth->user_prefs->dashboard->quickentry &&$core->auth->check('usage,contentadmin',$core->blog->id))
+{
+	$categories_combo = array('&nbsp;' => '');
+	try {
+		$categories = $core->blog->getCategories(array('post_type'=>'post'));
+		while ($categories->fetch()) {
+			$categories_combo[$categories->cat_id] = 
+				str_repeat('&nbsp;&nbsp;',$categories->level-1).
+				($categories->level-1 == 0 ? '' : '&bull; ').
+				html::escapeHTML($categories->cat_title);
+		}
+	} catch (Exception $e) { }
+	
+	$form = new dcForm($core,array('quickentry','quick-entry'),'post.php');
+	$form
+		->addField(
+			new dcFieldText('post_title','', array(
+				'size'		=> 20,
+				'required'	=> true,
+				'label'		=> __('Title'))))
+		->addField(
+			new dcFieldTextArea('post_content','', array(
+				'required'	=> true,
+				'label'		=> __("Content:"))))
+		->addField(
+			new dcFieldCombo('cat_id','',$categories_combo,array(
+				"label" => __('Category:'))))
+		->addField(
+			new dcFieldSubmit('save',__('Save'),array(
+				'action' => 'savePost')))
+		->addField(
+			new dcFieldHidden ('post_status',-2))
+		->addField(
+			new dcFieldHidden ('post_format',$core->auth->getOption('post_format')))
+		->addField(
+			new dcFieldHidden ('post_excerpt',''))
+		->addField(
+			new dcFieldHidden ('post_lang',$core->auth->getInfo('user_lang')))
+		->addField(
+			new dcFieldHidden ('post_notes',''))
+	;
+	if ($core->auth->check('publish',$core->blog->id)) {
+		$form->addField(
+			new dcFieldHidden ('save-publish',__('Save and publish')));
+	}
+	
+	$_ctx->dashboard_quickentry = true;
+}
+
+# Dashboard icons
+$__dashboard_icons = new ArrayObject();
+
+# Dashboard favorites
+$post_count = $core->blog->getPosts(array(),true)->f(0);
+$str_entries = ($post_count > 1) ? __('%d entries') : __('%d entry');
+
+$comment_count = $core->blog->getComments(array(),true)->f(0);
+$str_comments = ($comment_count > 1) ? __('%d comments') : __('%d comment');
+
+$ws = $core->auth->user_prefs->addWorkspace('favorites');
+$count = 0;
+foreach ($ws->dumpPrefs() as $k => $v) {
+	// User favorites only
+	if (!$v['global']) {
+		$fav = unserialize($v['value']);
+		if (($fav['permissions'] == '*') || $core->auth->check($fav['permissions'],$core->blog->id)) {
+			if (dc_valid_fav($fav['url'])) {
+				$count++;
+				$title = ($fav['name'] == 'posts' ? sprintf($str_entries,$post_count) : 
+					($fav['name'] == 'comments' ? sprintf($str_comments,$comment_count) : $fav['title']));
+				$__dashboard_icons[$fav['name']] = new ArrayObject(array(__($title),$fav['url'],$fav['large-icon']));
+
+				# Let plugins set their own title for favorite on dashboard
+				$core->callBehavior('adminDashboardFavsIcon',$core,$fav['name'],$__dashboard_icons[$fav['name']]);
+			}
+		}
+	}
+}	
+if (!$count) {
+	// Global favorites if any
+	foreach ($ws->dumpPrefs() as $k => $v) {
+		$fav = unserialize($v['value']);
+		if (($fav['permissions'] == '*') || $core->auth->check($fav['permissions'],$core->blog->id)) {
+			if (dc_valid_fav($fav['url'])) {
+				$count++;
+				$title = ($fav['name'] == 'posts' ? sprintf($str_entries,$post_count) : 
+					($fav['name'] == 'comments' ? sprintf($str_comments,$comment_count) : $fav['title']));
+				$__dashboard_icons[$fav['name']] = new ArrayObject(array(__($title),$fav['url'],$fav['large-icon']));
+
+				# Let plugins set their own title for favorite on dashboard
+				$core->callBehavior('adminDashboardFavsIcon',$core,$fav['name'],$__dashboard_icons[$fav['name']]);
+			}
+		}
+	}
+}
+if (!$count) {
+	// No user or global favorites, add "user pref" and "new entry" fav
+	if ($core->auth->check('usage,contentadmin',$core->blog->id)) {
+		$__dashboard_icons['new_post'] = new ArrayObject(array(__('New entry'),'post.php','images/menu/edit-b.png'));
+	}
+	$__dashboard_icons['prefs'] = new ArrayObject(array(__('My preferences'),'preferences.php','images/menu/user-pref-b.png'));
+}
+
+# Send dashboard icons to templates
+$icons = array();
+foreach ($__dashboard_icons as $i) {
+	$icons[] = array(
+		'title' 	=> $i[0],
+		'url' 	=> $i[1],
+		'img' 	=> dc_admin_icon_url($i[2])
+	);
+}
+$_ctx->dashboard_icons = $icons;
+
+# Dashboard items
+$__dashboard_items = new ArrayObject(array(new ArrayObject,new ArrayObject));
 $core->callBehavior('adminDashboardItems', $core, $__dashboard_items);
 
+# Send dashboard items to templates
+$items = array();
+foreach ($__dashboard_items as $i) {	
+	if ($i->count() > 0) {
+		foreach ($i as $v) {
+			$items[] = $v;
+		}
+	}
+}
+$_ctx->dashboard_items = $items;
+
 # Dashboard content
-$dashboardContents = '';
 $__dashboard_contents = new ArrayObject(array(new ArrayObject,new ArrayObject));
 $core->callBehavior('adminDashboardContents', $core, $__dashboard_contents);
 
-/* DISPLAY
--------------------------------------------------------- */
-dcPage::open(__('Dashboard'),
-	dcPage::jsToolBar().
-	dcPage::jsLoad('js/_index.js').
-	# --BEHAVIOR-- adminDashboardHeaders
-	$core->callBehavior('adminDashboardHeaders'),
-	dcPage::breadcrumb(
-		array(
-		__('Dashboard').' : '.html::escapeHTML($core->blog->name) => ''
-		),
-		array('home_link' =>false)
-	)
-);
-
-# Dotclear updates notifications
-if ($core->auth->isSuperAdmin() && is_readable(DC_DIGESTS))
-{
-	$updater = new dcUpdate(DC_UPDATE_URL,'dotclear',DC_UPDATE_VERSION,DC_TPL_CACHE.'/versions');
-	$new_v = $updater->check(DC_VERSION);
-	$version_info = $new_v ? $updater->getInfoURL() : '';
-
-	if ($updater->getNotify() && $new_v) {
-		echo
-		'<div class="dc-update"><h3>'.sprintf(__('Dotclear %s is available!'),$new_v).'</h3> '.
-		'<p><a class="button submit" href="update.php">'.sprintf(__('Upgrade now'),$new_v).'</a> '.
-		'<a class="button" href="update.php?hide_msg=1">'.__('Remind me later').'</a>'.
-		($version_info ? ' </p>'.
-		'<p class="updt-info"><a href="'.$version_info.'">'.__('Information about this version').'</a>' : '').'</p>'.
-		'</div>';
-	}
-}
-
-if ($core->auth->getInfo('user_default_blog') != $core->blog->id && $core->auth->getBlogCount() > 1) {
-	echo
-	'<p><a href="index.php?default_blog=1" class="button">'.__('Make this blog my default blog').'</a></p>';
-}
-
+# Send dashboard contents to templates
+$contents = array();
+foreach ($__dashboard_contents as $i) {	
+	if ($i->count() > 0) {
+		foreach ($i as $v) {
+			$contents[] = $v;
+		}
+	}
+}
+$_ctx->dashboard_contents = $contents;
+
+# Blog status message
 if ($core->blog->status == 0) {
-	echo '<p class="static-msg">'.__('This blog is offline').'.</p>';
+	$_ctx->addMessageStatic(__('This blog is offline'));
 } elseif ($core->blog->status == -1) {
-	echo '<p class="static-msg">'.__('This blog is removed').'.</p>';
-}
-
+	$_ctx->addMessageStatic(__('This blog is removed'));
+}
+
+# Config errors messages
 if (!defined('DC_ADMIN_URL') || !DC_ADMIN_URL) {
-	echo
-	'<p class="static-msg">'.
-	sprintf(__('%s is not defined, you should edit your configuration file.'),'DC_ADMIN_URL').
-	' '.__('See <a href="http://dotclear.org/documentation/2.0/admin/config">documentation</a> for more information.').
-	'</p>';
-}
-
+	$_ctx->addMessageStatic(
+		sprintf(__('%s is not defined, you should edit your configuration file.'),'DC_ADMIN_URL').' '.
+		__('See <a href="http://dotclear.org/documentation/2.0/admin/config">documentation</a> for more information.')
+	);
+}
 if (!defined('DC_ADMIN_MAILFROM') || !DC_ADMIN_MAILFROM) {
-	echo
-	'<p class="static-msg">'.
-	sprintf(__('%s is not defined, you should edit your configuration file.'),'DC_ADMIN_MAILFROM').
-	' '.__('See <a href="http://dotclear.org/documentation/2.0/admin/config">documentation</a> for more information.').
-	'</p>';
-}
-
-$err = array();
-
-# Check cache directory
-if ( $core->auth->isSuperAdmin() ) {
-	if (!is_dir(DC_TPL_CACHE) || !is_writable(DC_TPL_CACHE)) {
-		$err[] = '<p>'.__("The cache directory does not exist or is not writable. You must create this directory with sufficient rights and affect this location to \"DC_TPL_CACHE\" in inc/config.php file.").'</p>';
-	}
-} else {
-	if (!is_dir(DC_TPL_CACHE) || !is_writable(DC_TPL_CACHE)) {
-		$err[] = '<p>'.__("The cache directory does not exist or is not writable. You should contact your administrator.").'</p>';
-	}
-}
-
-# Check public directory
-if ( $core->auth->isSuperAdmin() ) {
-	if (!is_dir($core->blog->public_path) || !is_writable($core->blog->public_path)) {
-		$err[] = '<p>'.__("There is no writable directory /public/ at the location set in about:config \"public_path\". You must create this directory with sufficient rights (or change this setting).").'</p>';
-	}
-} else {
-	if (!is_dir($core->blog->public_path) || !is_writable($core->blog->public_path)) {
-		$err[] = '<p>'.__("There is no writable root directory for the media manager. You should contact your administrator.").'</p>';
-	}
-}
-
-# Error list
-if (count($err) > 0) {
-	echo '<div class="error"><p><strong>'.__('Error:').'</strong></p>'.
-	'<ul><li>'.implode("</li><li>",$err).'</li></ul></div>';
-}
-
-# Plugins install messages
-if (!empty($plugins_install['success']))
-{
-	echo '<div class="success">'.__('Following plugins have been installed:').'<ul>';
-	foreach ($plugins_install['success'] as $k => $v) {
-		echo '<li>'.$k.'</li>';
-	}
-	echo '</ul></div>';
-}
-if (!empty($plugins_install['failure']))
-{
-	echo '<div class="error">'.__('Following plugins have not been installed:').'<ul>';
-	foreach ($plugins_install['failure'] as $k => $v) {
-		echo '<li>'.$k.' ('.$v.')</li>';
-	}
-	echo '</ul></div>';
-}
-# Errors modules notifications
-if ($core->auth->isSuperAdmin())
-{
-	$list = $core->plugins->getErrors();
-	if (!empty($list)) {
-		echo
-		'<div class="error" id="module-errors" class="error"><p>'.__('Errors have occured with following plugins:').'</p> '.
-		'<ul><li>'.implode("</li>\n<li>", $list).'</li></ul></div>';
-	}
-}
-
-# Dashboard columns (processed first, as we need to know the result before displaying the icons.)
-$dashboardItems = '';
-
-foreach ($__dashboard_items as $i)
-{
-	if ($i->count() > 0)
-	{
-		$dashboardItems .= '';
-		foreach ($i as $v) {
-			$dashboardItems .= $v;
-		}
-		$dashboardItems .= '';
-	}
-}
-
-# Dashboard elements
-echo '<div id="dashboard-main">';
-
-# Dashboard icons
-echo '<div id="icons">';
-foreach ($__dashboard_icons as $i)
-{
-	echo
-	'<p><a href="'.$i[1].'"><img src="'.dc_admin_icon_url($i[2]).'" alt="" />'.
-	'<br /><span>'.$i[0].'</span></a></p>';
-}
-echo '</div>';
-
-if ($core->auth->user_prefs->dashboard->quickentry) {
-	if ($core->auth->check('usage,contentadmin',$core->blog->id))
-	{
-		# Getting categories
-		$categories_combo = dcAdminCombos::getCategoriesCombo(
-			$core->blog->getCategories(array('post_type'=>'post'))
-		);
-
-		echo
-		'<div id="quick">'.
-		'<h3>'.__('Quick entry').'</h3>'.
-		'<form id="quick-entry" action="post.php" method="post" class="fieldset">'.
-		'<h4>'.__('New entry').'</h4>'.
-		'<p class="col"><label for="post_title" class="required"><abbr title="'.__('Required field').'">*</abbr> '.__('Title:').'</label>'.
-		form::field('post_title',20,255,'','maximal').
-		'</p>'.
-		'<p class="area"><label class="required" '.
-		'for="post_content"><abbr title="'.__('Required field').'">*</abbr> '.__('Content:').'</label> '.
-		form::textarea('post_content',50,10).
-		'</p>'.
-		'<p><label for="cat_id" class="classic">'.__('Category:').'</label> '.
-		form::combo('cat_id',$categories_combo).'</p>'.
-		($core->auth->check('categories', $core->blog->id)
-			? '<div>'.
-			'<p id="new_cat" class="q-cat">'.__('Add a new category').'</p>'.
-			'<p class="q-cat"><label for="new_cat_title">'.__('Title:').'</label> '.
-			form::field('new_cat_title',30,255,'','').'</p>'.
-			'<p class="q-cat"><label for="new_cat_parent">'.__('Parent:').'</label> '.
-			form::combo('new_cat_parent',$categories_combo,'','').
-			'</p>'.
-			'<p class="form-note info clear">'.__('This category will be created when you will save your post.').'</p>'.
-			'</div>'
-			: '').
-		'<p><input type="submit" value="'.__('Save').'" name="save" /> '.
-		($core->auth->check('publish',$core->blog->id)
-			? '<input type="hidden" value="'.__('Save and publish').'" name="save-publish" />'
-			: '').
-		$core->formNonce().
-		form::hidden('post_status',-2).
-		form::hidden('post_format',$core->auth->getOption('post_format')).
-		form::hidden('post_excerpt','').
-		form::hidden('post_lang',$core->auth->getInfo('user_lang')).
-		form::hidden('post_notes','').
-		'</p>'.
-		'</form>'.
-		'</div>';
-	}
-}
-
-foreach ($__dashboard_contents as $i)
-{
-	if ($i->count() > 0)
-	{
-		$dashboardContents .= '';
-		foreach ($i as $v) {
-			$dashboardContents .= $v;
-		}
-		$dashboardContents .= '';
-	}
-}
-
-if ($dashboardContents != '' || $dashboardItems != '') {
-	echo
-	'<div id="dashboard-boxes">'.
-	'<div class="db-items">'.$dashboardItems.$dashboardContents.'</div>'.
-	'</div>';
-}
-
-echo '</div>'; #end dashboard-main
-dcPage::helpBlock('core_dashboard');
-dcPage::close();
+	$_ctx->addMessageStatic(
+		sprintf(__('%s is not defined, you should edit your configuration file.'),'DC_ADMIN_MAILFROM').' '.
+		__('See <a href="http://dotclear.org/documentation/2.0/admin/config">documentation</a> for more information.')
+	);
+}
+
+$_ctx->setBreadCrumb(__('Dashboard').' : '.html::escapeHTML($core->blog->name), false);
+$core->tpl->display('index.html.twig');
+?>
Index: admin/js/filters.js
===================================================================
--- admin/js/filters.js	(revision 1152)
+++ admin/js/filters.js	(revision 1152)
@@ -0,0 +1,6 @@
+$(function() {
+	$('#toggle-filters').click(function(e) {
+		$('#filters').toggleClass('hidden');
+		$('#toggle-filters').toggleClass('opened');
+	});
+});
Index: admin/plugin.php
===================================================================
--- admin/plugin.php	(revision 2566)
+++ admin/plugin.php	(revision 2593)
@@ -4,5 +4,5 @@
 # This file is part of Dotclear 2.
 #
-# Copyright (c) 2003-2013 Olivier Meunier & Association Dotclear
+# Copyright (c) 2003-2011 Olivier Meunier & Association Dotclear
 # Licensed under the GPL version 2.0 license.
 # See LICENSE file or
@@ -15,31 +15,28 @@
 dcPage::check('usage,contentadmin');
 
+$has_content = false;
 $p_file = '';
 $p = !empty($_REQUEST['p']) ? $_REQUEST['p'] : null;
-$popup = (integer) !empty($_REQUEST['popup']);
-
-if ($popup) {
-	$open_f = array('dcPage','openPopup');
-	$close_f = array('dcPage','closePopup');
-} else {
-	$open_f = array('dcPage','open');
-	$close_f = array('dcPage','close');
-}
+$popup = $_ctx->popup = (integer) !empty($_REQUEST['popup']);
 
 if ($core->plugins->moduleExists($p)) {
 	$p_file = $core->plugins->moduleRoot($p).'/index.php';
 }
+if (file_exists($p_file)) {
 
-if (file_exists($p_file))
-{
-	# Loading plugin
+//* Keep this for old style plugins using dcPage
+	if ($popup) {
+		$open_f = array('dcPage','openPopup');
+		$close_f = array('dcPage','closePopup');
+	} else {
+		$open_f = array('dcPage','open');
+		$close_f = array('dcPage','close');
+	}
+	
 	$p_info = $core->plugins->getModules($p);
-
 	$p_url = 'plugin.php?p='.$p;
-
-	$p_title = 'no content - plugin';
-	$p_head = '';
-	$p_content = '<p>'.__('No content found on this plugin.').'</p>';
-
+	$p_title = $p_head = $p_content = '';
+//*/	
+	# Get page content
 	ob_start();
 	include $p_file;
@@ -47,48 +44,53 @@
 	ob_end_clean();
 
-	if (preg_match('|<head>(.*?)</head|ms',$res,$m)) {
-		if (preg_match('|<title>(.*?)</title>|ms',$m[1],$mt)) {
-			$p_title = $mt[1];
-		}
-
-		if (preg_match_all('|(<script.*?>.*?</script>)|ms',$m[1],$ms)) {
-			foreach ($ms[1] as $v) {
-				$p_head .= $v."\n";
+	# Check context and display
+	if ($_ctx->hasPageTitle() && !empty($res)) {
+		$has_content = true;
+		echo $res;
+	}
+//* Keep this for old style plugins using dcPage
+	elseif (!$_ctx->hasPageTitle()) {
+		
+		if (preg_match('|<head>(.*?)</head|ms',$res,$m)) {
+			if (preg_match('|<title>(.*?)</title>|ms',$m[1],$mt)) {
+				$p_title = $mt[1];
+			}
+			
+			if (preg_match_all('|(<script.*?>.*?</script>)|ms',$m[1],$ms)) {
+				foreach ($ms[1] as $v) {
+					$p_head .= $v."\n";
+				}
+			}
+			
+			if (preg_match_all('|(<style.*?>.*?</style>)|ms',$m[1],$ms)) {
+				foreach ($ms[1] as $v) {
+					$p_head .= $v."\n";
+				}
+			}
+			
+			if (preg_match_all('|(<link.*?/>)|ms',$m[1],$ms)) {
+				foreach ($ms[1] as $v) {
+					$p_head .= $v."\n";
+				}
 			}
 		}
-
-		if (preg_match_all('|(<style.*?>.*?</style>)|ms',$m[1],$ms)) {
-			foreach ($ms[1] as $v) {
-				$p_head .= $v."\n";
-			}
-		}
-
-		if (preg_match_all('|(<link.*?/>)|ms',$m[1],$ms)) {
-			foreach ($ms[1] as $v) {
-				$p_head .= $v."\n";
-			}
+		
+		if (preg_match('|<body.*?>(.+)</body>|ms',$res,$m)) {
+			$p_content = $m[1];
+			
+			call_user_func($open_f,$p_title,$p_head);
+			echo $p_content;
+			call_user_func($close_f);
+			
+			$has_content = true;
 		}
 	}
-
-	if (preg_match('|<body.*?>(.+)</body>|ms',$res,$m)) {
-		$p_content = $m[1];
-	}
-
-	call_user_func($open_f,$p_title,$p_head);
-	echo $p_content;
-	call_user_func($close_f);
+//*/
 }
-else
-{
-	call_user_func($open_f,__('Plugin not found'),'',
-		dcPage::breadcrumb(
-			array(
-				__('System') => '',
-				__('Plugin not found') => ''
-			))
-	);
-
-	echo '<p>'.__('The plugin you reached does not exist or does not have an admin page.').'</p>';
-
-	call_user_func($close_f);
+# No plugin or content found
+if (!$has_content) {
+	$_ctx->setBreadcrumb(__('Plugin not found'));
+	$_ctx->addError(__('The plugin you reached does not exist or does not have an admin page.'));
+	$core->tpl->display('plugin.html.twig');
 }
+?>
Index: admin/post.php
===================================================================
--- admin/post.php	(revision 2566)
+++ admin/post.php	(revision 2593)
@@ -15,24 +15,114 @@
 dcPage::check('usage,contentadmin');
 
-$post_id = '';
-$cat_id = '';
-$post_dt = '';
-$post_format = $core->auth->getOption('post_format');
-$post_password = '';
-$post_url = '';
-$post_lang = $core->auth->getInfo('user_lang');
-$post_title = '';
-$post_excerpt = '';
-$post_excerpt_xhtml = '';
-$post_content = '';
-$post_content_xhtml = '';
-$post_notes = '';
-$post_status = $core->auth->getInfo('user_post_status');
-$post_selected = false;
-$post_open_comment = $core->blog->settings->system->allow_comments;
-$post_open_tb = $core->blog->settings->system->allow_trackbacks;
+class PostActions 
+{
+	public static function savePost($form) {
+		global $_ctx, $core;
+		if (!$form->can_edit_post) {
+			return;
+		}
+		try {
+			$form->check($_ctx);
+			$form->cat_id = (integer) $form->cat_id;
+	
+			if (!empty($form->post_dt)) {
+				try
+				{
+					$post_dt = strtotime($form->post_dt);
+					if ($post_dt == false || $post_dt == -1) {
+						$bad_dt = true;
+						throw new Exception(__('Invalid publication date'));
+					}
+					$form->post_dt = date('Y-m-d H:i',$post_dt);
+				}
+				catch (Exception $e)
+				{
+					$core->error->add($e->getMessage());
+				}
+			}
+			$post_excerpt = $form->post_excerpt;
+			$post_content = $form->post_content;
+			$post_excerpt_xhtml = '';
+			$post_content_xhtml = '';
+			$core->blog->setPostContent(
+				$form->id,$form->post_format,$form->post_lang,
+				$post_excerpt,$post_excerpt_xhtml,$post_content,$post_content_xhtml
+			);
+			$form->post_excerpt = $post_excerpt;
+			$form->post_content = $post_content;
+			$form->post_excerpt_xhtml = $post_excerpt_xhtml;
+			$form->post_content_xhtml = $post_content_xhtml;
+			
+			$cur = $core->con->openCursor($core->prefix.'post');
+	
+			$cur->post_title = $form->post_title;
+			$cur->cat_id = $form->cat_id ? $form->cat_id : null;
+			$cur->post_dt = $form->post_dt ? date('Y-m-d H:i:00',strtotime($form->post_dt)) : '';
+			$cur->post_format = $form->post_format;
+			$cur->post_password = $form->post_password;
+			$cur->post_lang = $form->post_lang;
+			$cur->post_title = $form->post_title;
+			$cur->post_excerpt = $form->post_excerpt;
+			$cur->post_excerpt_xhtml = $form->post_excerpt_xhtml;
+			$cur->post_content = $form->post_content;
+			$cur->post_content_xhtml = $form->post_content_xhtml;
+			$cur->post_notes = $form->post_notes;
+			$cur->post_status = $form->post_status;
+			$cur->post_selected = (integer) $form->post_selected;
+			$cur->post_open_comment = (integer) $form->post_open_comment;
+			$cur->post_open_tb = (integer) $form->post_open_tb;
+	
+			if (!empty($form->post_url)) {
+				$cur->post_url = $form->post_url;
+			}
+	
+			# Update post
+			if ($form->id)
+			{
+				# --BEHAVIOR-- adminBeforePostUpdate
+				$core->callBehavior('adminBeforePostUpdate',$cur,$form->id);
+				
+				$core->blog->updPost($form->id,$cur);
+				
+				# --BEHAVIOR-- adminAfterPostUpdate
+				$core->callBehavior('adminAfterPostUpdate',$cur,$form->id);
+				http::redirect('post.php?id='.$form->id.'&upd=1');
+			}
+			else
+			{
+				$cur->user_id = $core->auth->userID();
+								# --BEHAVIOR-- adminBeforePostCreate
+				$core->callBehavior('adminBeforePostCreate',$cur);
+				
+				$return_id = $core->blog->addPost($cur);
+				
+				# --BEHAVIOR-- adminAfterPostCreate
+				$core->callBehavior('adminAfterPostCreate',$cur,$return_id);
+				
+				http::redirect('post.php?id='.$return_id.'&crea=1');
+			}
+
+		} catch (Exception $e) {
+			$_ctx->addError($e->getMessage());
+		}
+	}
+	public static function deletePost($form) {
+		global $core,$_ctx;
+		if ($form->can_delete) {
+			try {
+				$post_id = $form->id;
+				$core->callBehavior('adminBeforePostDelete',$post_id);
+				$core->blog->delPost($post_id);
+				http::redirect('posts.php');
+				exit;
+			} catch (Exception $e) {
+				$_ctx->addError($e->getMessage());
+			}
+		}
+	}
+}
 
 $page_title = __('New entry');
-
+$post_id='';
 $can_view_page = true;
 $can_edit_post = $core->auth->check('usage,contentadmin',$core->blog->id);
@@ -47,39 +137,121 @@
 # If user can't publish
 if (!$can_publish) {
-	$post_status = -2;
+	$form->post_status = -2;
 }
 
 # Getting categories
-$categories_combo = dcAdminCombos::getCategoriesCombo(
-	$core->blog->getCategories(array('post_type'=>'post'))
-);
-
-$status_combo = dcAdminCombos::getPostStatusesCombo();
-
-$img_status_pattern = '<img class="img_select_option" alt="%1$s" title="%1$s" src="images/%2$s" />';
+$categories_combo = array('&nbsp;' => '');
+try {
+	$categories = $core->blog->getCategories(array('post_type'=>'post'));
+	while ($categories->fetch()) {
+		$categories_combo[$categories->cat_id] = 
+			str_repeat('&nbsp;&nbsp;',$categories->level-1).
+			($categories->level-1 == 0 ? '' : '&bull; ').
+			html::escapeHTML($categories->cat_title);
+	}
+} catch (Exception $e) { }
+
+# Status combo
+foreach ($core->blog->getAllPostStatus() as $k => $v) {
+	$status_combo[$k] = $v;
+}
 
 # Formaters combo
-$formaters_combo = dcAdminCombos::getFormatersCombo();
+foreach ($core->getFormaters() as $v) {
+	$formaters_combo[$v] = $v;
+}
 
 # Languages combo
 $rs = $core->blog->getLangs(array('order'=>'asc'));
-$lang_combo = dcAdminCombos::getLangsCombo($rs,true);
-
-# Validation flag
-$bad_dt = false;
-
-# Trackbacks
-$TB = new dcTrackback($core);
-$tb_urls = $tb_excerpt = '';
-
+$all_langs = l10n::getISOcodes(0,1);
+$lang_combo = array('' => '', __('Most used') => array(), __('Available') => l10n::getISOcodes(0,1));
+while ($rs->fetch()) {
+	if (isset($all_langs[$rs->post_lang])) {
+		$lang_combo[__('Most used')][$rs->post_lang] = $all_langs[$rs->post_lang];
+		unset($lang_combo[__('Available')][$rs->post_lang]);
+	} else {
+		$lang_combo[__('Most used')][$rs->post_lang] = $all_langs[$rs->post_lang];
+	}
+}
+unset($all_langs);
+unset($rs);
+
+$form = new dcForm($core,'post','post.php');
+$form
+	->addField(
+		new dcFieldText('post_title','', array(
+			'maxlength'		=> 255,
+			'required'	=> true,
+			'label'		=> __('Title:'))))
+	->addField(
+		new dcFieldTextArea('post_excerpt','', array(
+			'cols'		=> 50,
+			'rows'		=> 5,
+			'label'		=> __("Excerpt:").'<span class="form-note">'.
+			__('Add an introduction to the post.').'</span>')))
+	->addField(
+		new dcFieldTextArea('post_content','', array(
+			'required'	=> true,
+			'label'		=> __("Content:"))))
+	->addField(
+		new dcFieldTextArea('post_notes','', array(
+			'label'		=> __("Notes"))))
+	->addField(
+		new dcFieldSubmit('save',__('Save'),array(
+			'action' => array('PostActions','savePost'))))
+	->addField(
+		new dcFieldSubmit('delete',__('Delete'),array(
+			'action' => array('PostActions','deletePost'))))
+	->addField(
+		new dcFieldCombo('post_status',$core->auth->getInfo('user_post_status'),$status_combo,array(
+			'disabled' => !$can_publish,
+			'label' => __('Entry status'))))
+	->addField(
+		new dcFieldCombo('cat_id','',$categories_combo,array(
+			"label" => __('Category'))))
+	->addField(
+		new dcFieldCombo('new_cat_parent','',$categories_combo,array(
+			"label" => __('Parent:'))))
+	->addField(
+		new dcFieldText('new_cat_title','', array(
+			'maxlength'		=> 255,
+			'label'		=> __('Title'))))
+		
+	->addField(
+		new dcFieldText('post_dt','',array(
+			"label" => __('Publication date and hour'))))
+	->addField(
+		new dcFieldCombo('post_format',$core->auth->getOption('post_format'),$formaters_combo,array(
+			"label" => __('Text formating'))))
+	->addField(
+		new dcFieldCheckbox ('post_open_comment',$core->blog->settings->system->allow_comments,array(
+			"label" => __('Accept comments'))))
+	->addField(
+		new dcFieldCheckbox ('post_open_tb',$core->blog->settings->system->allow_trackbacks,array(
+			"label" => __('Accept trackbacks'))))
+	->addField(
+		new dcFieldCheckbox ('post_selected',array(1=>false),array(
+			"label" => __('Selected entry'))))
+	->addField(
+		new dcFieldCombo ('post_lang',$core->auth->getInfo('user_lang'),$lang_combo, array(
+			"label" => __('Entry lang:'))))
+	->addField(
+		new dcFieldText('post_password','',array(
+			"maxlength" => 32,
+			"label" => __('Entry password:'))))
+	->addField(
+		new dcFieldText('post_url','',array(
+			"maxlength" => 255,
+			"label" => __('Basename:'))))
+	->addField(
+		new dcFieldHidden ('id',''))
+;
 # Get entry informations
 if (!empty($_REQUEST['id']))
 {
-	$page_title = __('Edit entry');
-
 	$params['post_id'] = $_REQUEST['id'];
-
+	
 	$post = $core->blog->getPosts($params);
-
+	
 	if ($post->isEmpty())
 	{
@@ -89,251 +261,97 @@
 	else
 	{
-		$post_id = $post->post_id;
-		$cat_id = $post->cat_id;
-		$post_dt = date('Y-m-d H:i',strtotime($post->post_dt));
-		$post_format = $post->post_format;
-		$post_password = $post->post_password;
-		$post_url = $post->post_url;
-		$post_lang = $post->post_lang;
-		$post_title = $post->post_title;
-		$post_excerpt = $post->post_excerpt;
-		$post_excerpt_xhtml = $post->post_excerpt_xhtml;
-		$post_content = $post->post_content;
-		$post_content_xhtml = $post->post_content_xhtml;
-		$post_notes = $post->post_notes;
-		$post_status = $post->post_status;
-		$post_selected = (boolean) $post->post_selected;
-		$post_open_comment = (boolean) $post->post_open_comment;
-		$post_open_tb = (boolean) $post->post_open_tb;
-
-		$can_edit_post = $post->isEditable();
-		$can_delete= $post->isDeletable();
-
+		$form->id = $post_id = $post->post_id;
+		$form->cat_id = $post->cat_id;
+		$form->post_dt = date('Y-m-d H:i',strtotime($post->post_dt));
+		$form->post_format = $post->post_format;
+		$form->post_password = $post->post_password;
+		$form->post_url = $post->post_url;
+		$form->post_lang = $post->post_lang;
+		$form->post_title = $post->post_title;
+		$form->post_excerpt = $post->post_excerpt;
+		$form->post_excerpt_xhtml = $post->post_excerpt_xhtml;
+		$form->post_content = $post->post_content;
+		$form->post_content_xhtml = $post->post_content_xhtml;
+		$form->post_notes = $post->post_notes;
+		$form->post_status = $post->post_status;
+		$form->post_selected = (boolean) $post->post_selected;
+		$form->post_open_comment = (boolean) $post->post_open_comment;
+		$form->post_open_tb = (boolean) $post->post_open_tb;
+		$form->can_edit_post = $post->isEditable();
+		$form->can_delete= $post->isDeletable();
 		$next_rs = $core->blog->getNextPost($post,1);
 		$prev_rs = $core->blog->getNextPost($post,-1);
-
+		
 		if ($next_rs !== null) {
-			$next_link = sprintf($post_link,$next_rs->post_id,
-				html::escapeHTML($next_rs->post_title),__('Next entry').'&nbsp;&#187;');
-			$next_headlink = sprintf($post_headlink,'next',
-				html::escapeHTML($next_rs->post_title),$next_rs->post_id);
-		}
-
+			$_ctx->next_post = array('id' => $next_rs->post_id,'title' => $next_rs->post_title);
+		}
 		if ($prev_rs !== null) {
-			$prev_link = sprintf($post_link,$prev_rs->post_id,
-				html::escapeHTML($prev_rs->post_title),'&#171;&nbsp;'.__('Previous entry'));
-			$prev_headlink = sprintf($post_headlink,'previous',
-				html::escapeHTML($prev_rs->post_title),$prev_rs->post_id);
-		}
-
-		try {
-			$core->media = new dcMedia($core);
-		} catch (Exception $e) {
-			$core->error->add($e->getMessage());
-		}
-
-		# Sanitize trackbacks excerpt
-		$tb_excerpt = empty($_POST['tb_excerpt']) ?
-			$post_excerpt_xhtml.' '.$post_content_xhtml :
-			$_POST['tb_excerpt'];
-		$tb_excerpt = html::decodeEntities(html::clean($tb_excerpt));
-		$tb_excerpt = text::cutString(html::escapeHTML($tb_excerpt), 255);
-		$tb_excerpt = preg_replace('/\s+/ms', ' ', $tb_excerpt);
-	}
-}
-if (isset($_REQUEST['section']) && $_REQUEST['section']=='trackbacks') {
-	$anchor = 'trackbacks';
-} else {
-	$anchor = 'comments';
-}
-
-$comments_actions_page = new dcCommentsActionsPage($core,'post.php',array('id' => $post_id, '_ANCHOR'=>$anchor,'section' => $anchor));
-
-if ($comments_actions_page->process()) {
-	return;
-}
-
-# Ping blogs
-if (!empty($_POST['ping']))
-{
-	if (!empty($_POST['tb_urls']) && $post_id && $post_status == 1 && $can_edit_post)
-	{
-		$tb_urls = $_POST['tb_urls'];
-		$tb_urls = str_replace("\r", '', $tb_urls);
-		$tb_post_title = html::escapeHTML(trim(html::clean($post_title)));
-		$tb_post_url = $post->getURL();
-
-		foreach (explode("\n", $tb_urls) as $tb_url)
-		{
-			try {
-				$TB->ping($tb_url, $post_id, $tb_post_title, $tb_excerpt, $tb_post_url);
-			} catch (Exception $e) {
-				$core->error->add($e->getMessage());
-			}
-		}
-
-		if (!$core->error->flag()) {
-			dcPage::addSuccessNotice(__('All pings sent.'));
-			http::redirect('post.php?id='.$post_id.'&tb=1');
-		}
-	}
-}
-
-# Format excerpt and content
-elseif (!empty($_POST) && $can_edit_post)
-{
-	$post_format = $_POST['post_format'];
-	$post_excerpt = $_POST['post_excerpt'];
-	$post_content = $_POST['post_content'];
-
-	$post_title = $_POST['post_title'];
-
-	$cat_id = (integer) $_POST['cat_id'];
-
-	if (isset($_POST['post_status'])) {
-		$post_status = (integer) $_POST['post_status'];
-	}
-
-	if (empty($_POST['post_dt'])) {
-		$post_dt = '';
-	} else {
-		try
-		{
-			$post_dt = strtotime($_POST['post_dt']);
-			if ($post_dt == false || $post_dt == -1) {
-				$bad_dt = true;
-				throw new Exception(__('Invalid publication date'));
-			}
-			$post_dt = date('Y-m-d H:i',$post_dt);
-		}
-		catch (Exception $e)
-		{
-			$core->error->add($e->getMessage());
-		}
-	}
-
-	$post_open_comment = !empty($_POST['post_open_comment']);
-	$post_open_tb = !empty($_POST['post_open_tb']);
-	$post_selected = !empty($_POST['post_selected']);
-	$post_lang = $_POST['post_lang'];
-	$post_password = !empty($_POST['post_password']) ? $_POST['post_password'] : null;
-
-	$post_notes = $_POST['post_notes'];
-
-	if (isset($_POST['post_url'])) {
-		$post_url = $_POST['post_url'];
-	}
-
-	$core->blog->setPostContent(
-		$post_id,$post_format,$post_lang,
-		$post_excerpt,$post_excerpt_xhtml,$post_content,$post_content_xhtml
-	);
-}
-
-# Delete post
-if (!empty($_POST['delete']) && $can_delete)
-{
-	try {
-		# --BEHAVIOR-- adminBeforePostDelete
-		$core->callBehavior('adminBeforePostDelete',$post_id);
-		$core->blog->delPost($post_id);
-		http::redirect('posts.php');
-	} catch (Exception $e) {
-		$core->error->add($e->getMessage());
-	}
-}
-
-# Create or update post
-if (!empty($_POST) && !empty($_POST['save']) && $can_edit_post && !$bad_dt)
-{
-	# Create category
-	if (!empty($_POST['new_cat_title']) && $core->auth->check('categories', $core->blog->id)) {
-
-		$cur_cat = $core->con->openCursor($core->prefix.'category');
-		$cur_cat->cat_title = $_POST['new_cat_title'];
-		$cur_cat->cat_url = '';
-
-		$parent_cat = !empty($_POST['new_cat_parent']) ? $_POST['new_cat_parent'] : '';
-
-		# --BEHAVIOR-- adminBeforeCategoryCreate
-		$core->callBehavior('adminBeforeCategoryCreate', $cur_cat);
-
-		$cat_id = $core->blog->addCategory($cur_cat, (integer) $parent_cat);
-
-		# --BEHAVIOR-- adminAfterCategoryCreate
-		$core->callBehavior('adminAfterCategoryCreate', $cur_cat, $cat_id);
-	}
-
-	$cur = $core->con->openCursor($core->prefix.'post');
-
-	$cur->post_title = $post_title;
-	$cur->cat_id = ($cat_id ? $cat_id : null);
-	$cur->post_dt = $post_dt ? date('Y-m-d H:i:00',strtotime($post_dt)) : '';
-	$cur->post_format = $post_format;
-	$cur->post_password = $post_password;
-	$cur->post_lang = $post_lang;
-	$cur->post_title = $post_title;
-	$cur->post_excerpt = $post_excerpt;
-	$cur->post_excerpt_xhtml = $post_excerpt_xhtml;
-	$cur->post_content = $post_content;
-	$cur->post_content_xhtml = $post_content_xhtml;
-	$cur->post_notes = $post_notes;
-	$cur->post_status = $post_status;
-	$cur->post_selected = (integer) $post_selected;
-	$cur->post_open_comment = (integer) $post_open_comment;
-	$cur->post_open_tb = (integer) $post_open_tb;
-
-	if (isset($_POST['post_url'])) {
-		$cur->post_url = $post_url;
-	}
-
-	# Update post
-	if ($post_id)
-	{
-		try
-		{
-			# --BEHAVIOR-- adminBeforePostUpdate
-			$core->callBehavior('adminBeforePostUpdate',$cur,$post_id);
-
-			$core->blog->updPost($post_id,$cur);
-
-			# --BEHAVIOR-- adminAfterPostUpdate
-			$core->callBehavior('adminAfterPostUpdate',$cur,$post_id);
-			dcPage::addSuccessNotice (sprintf(__('The post "%s" has been successfully updated'),html::escapeHTML($cur->post_title)));
-			http::redirect('post.php?id='.$post_id);
-		}
-		catch (Exception $e)
-		{
-			$core->error->add($e->getMessage());
-		}
-	}
-	else
-	{
-		$cur->user_id = $core->auth->userID();
-
-		try
-		{
-			# --BEHAVIOR-- adminBeforePostCreate
-			$core->callBehavior('adminBeforePostCreate',$cur);
-
-			$return_id = $core->blog->addPost($cur);
-
-			# --BEHAVIOR-- adminAfterPostCreate
-			$core->callBehavior('adminAfterPostCreate',$cur,$return_id);
-
-			dcPage::addSuccessNotice(__('Entry has been successfully created.'));
-			http::redirect('post.php?id='.$return_id);
-		}
-		catch (Exception $e)
-		{
-			$core->error->add($e->getMessage());
-		}
-	}
-}
-
-# Getting categories
-$categories_combo = dcAdminCombos::getCategoriesCombo(
-	$core->blog->getCategories(array('post_type'=>'post'))
-);
+			$_ctx->prev_post = array('id' => $prev_rs->post_id,'title' => $prev_rs->post_title);
+		}
+		$page_title = __('Edit entry');
+
+	}
+}
+if ($post_id) {
+	$_ctx->post_id = $post->post_id;
+
+	$_ctx->preview_url =
+		$core->blog->url.$core->url->getURLFor('preview',$core->auth->userID().'/'.
+		http::browserUID(DC_MASTER_KEY.$core->auth->userID().$core->auth->getInfo('user_pwd')).
+		'/'.$post->post_url);
+		
+	
+	$form_comment = new dcForm($core,'add-comment','comment.php');
+	$form_comment
+		->addField(
+			new dcFieldText('comment_author','', array(
+				'maxlength'		=> 255,
+				'required'	=> true,
+				'label'		=> __('Name:'))))
+		->addField(
+			new dcFieldText('comment_email','', array(
+				'maxlength'		=> 255,
+				'required'	=> true,
+				'label'		=> __('Email:'))))
+		->addField(
+			new dcFieldText('comment_site','', array(
+				'maxlength'		=> 255,
+				'label'		=> __('Web site:'))))
+		->addField(
+			new dcFieldTextArea('comment_content','', array(
+				'required'	=> true,
+				'label'		=> __('Comment:'))))
+		->addField(
+			new dcFieldHidden('post_id',$post_id))
+		->addField(
+			new dcFieldSubmit('add',__('Save'),array(
+				'action' => 'addComment')))
+		;
+
+	
+}
+
+$form->setup();
+
+$sidebar_blocks = new ArrayObject(array(
+	'status-box' => array(
+		'title' => __('Status'),
+		'items' => array('post_status','post_dt','post_lang','post_format')),
+	'metas-box' => array(
+		'title' => __('Ordering'),
+		'items' => array('post_selected','cat_id')),
+	'options-box' => array(
+		'title' => __('Options'),
+		'items' => array('post_open_comment','post_open_tb','post_password','post_url'))
+));
+
+$main_blocks = new ArrayObject(array(
+	"post_title","post_excerpt","post_content","post_notes"
+));
+
+
+$_ctx->sidebar_blocks = $sidebar_blocks;
+$_ctx->main_blocks = $main_blocks;
+
 /* DISPLAY
 -------------------------------------------------------- */
@@ -345,541 +363,35 @@
 	$default_tab = 'comments';
 }
-elseif (!empty($_GET['tb'])) {
-	$default_tab = 'trackbacks';
-}
-
-if ($post_id) {
-	switch ($post_status) {
-		case 1:
-			$img_status = sprintf($img_status_pattern,__('Published'),'check-on.png');
-			break;
-		case 0:
-			$img_status = sprintf($img_status_pattern,__('Unpublished'),'check-off.png');
-			break;
-		case -1:
-			$img_status = sprintf($img_status_pattern,__('Scheduled'),'scheduled.png');
-			break;
-		case -2:
-			$img_status = sprintf($img_status_pattern,__('Pending'),'check-wrn.png');
-			break;
-		default:
-			$img_status = '';
-	}
-	$edit_entry_str = __('&ldquo;%s&rdquo;');
-	$page_title_edit = sprintf($edit_entry_str, html::escapeHTML($post_title)).' '.$img_status;
-} else {
-	$img_status = '';
-}
-
-
-dcPage::open($page_title.' - '.__('Entries'),
-	dcPage::jsDatePicker().
-	dcPage::jsToolBar().
-	dcPage::jsModal().
-	dcPage::jsMetaEditor().
-	dcPage::jsLoad('js/_post.js').
-	dcPage::jsConfirmClose('entry-form','comment-form').
-	# --BEHAVIOR-- adminPostHeaders
-	$core->callBehavior('adminPostHeaders').
-	dcPage::jsPageTabs($default_tab).
-	$next_headlink."\n".$prev_headlink,
-	dcPage::breadcrumb(
+$page_title_edit = __('Edit entry');
+$_ctx
+	->setBreadCrumb(
 		array(
 			html::escapeHTML($core->blog->name) => '',
 			__('Entries') => 'posts.php',
 			($post_id ? $page_title_edit : $page_title) => ''
-		))
-);
+	))
+	->default_tab = $default_tab;
+$_ctx->post_status = $form->post_status;
+$_ctx->post_title = $form->post_title;
+if ($form->post_status == 1) {
+	$_ctx->post_url = $post->getURL();
+}
 
 if (!empty($_GET['upd'])) {
-	dcPage::success(__('Entry has been successfully updated.'));
+	$_ctx->setAlert(__('Entry has been successfully updated.'));
 }
 elseif (!empty($_GET['crea'])) {
-	dcPage::success(__('Entry has been successfully created.'));
+	$_ctx->setAlert(__('Entry has been successfully created.'));
 }
 elseif (!empty($_GET['attached'])) {
-	dcPage::success(__('File has been successfully attached.'));
+	$_ctx->setAlert(__('File has been successfully attached.'));
 }
 elseif (!empty($_GET['rmattach'])) {
-	dcPage::success(__('Attachment has been successfully removed.'));
-}
-
+	$_ctx->setAlert(__('Attachment has been successfully removed.'));
+}
 if (!empty($_GET['creaco'])) {
-	dcPage::success(__('Comment has been successfully created.'));
-}
-if (!empty($_GET['tbsent'])) {
-	dcPage::success(__('All pings sent.'));
-}
-
-# XHTML conversion
-if (!empty($_GET['xconv']))
-{
-	$post_excerpt = $post_excerpt_xhtml;
-	$post_content = $post_content_xhtml;
-	$post_format = 'xhtml';
-
-	dcPage::message(__('Don\'t forget to validate your XHTML conversion by saving your post.'));
-}
-
-if ($post_id && $post->post_status == 1) {
-	echo '<p><a class="onblog_link outgoing" href="'.$post->getURL().'" title="'.$post_title.'">'.__('Go to this entry on the site').' <img src="images/outgoing-blue.png" alt="" /></a></p>';
-}
-if ($post_id)
-{
-	echo '<p class="nav_prevnext">';
-	if ($prev_link) { echo $prev_link; }
-	if ($next_link && $prev_link) { echo ' | '; }
-	if ($next_link) { echo $next_link; }
-
-	# --BEHAVIOR-- adminPostNavLinks
-	$core->callBehavior('adminPostNavLinks',isset($post) ? $post : null);
-
-	echo '</p>';
-}
-
-# Exit if we cannot view page
-if (!$can_view_page) {
-	dcPage::helpBlock('core_post');
-	dcPage::close();
-	exit;
-}
-/* Post form if we can edit post
--------------------------------------------------------- */
-if ($can_edit_post)
-{
-	$sidebar_items = new ArrayObject(array(
-		'status-box' => array(
-			'title' => __('Status'),
-			'items' => array(
-				'post_status' =>
-					'<p class="entry-status"><label for="post_status">'.__('Entry status').' '.$img_status.'</label>'.
-					form::combo('post_status',$status_combo,$post_status,'maximal','',!$can_publish).
-					'</p>',
-				'post_dt' =>
-					'<p><label for="post_dt">'.__('Publication date and hour').'</label>'.
-					form::field('post_dt',16,16,$post_dt,($bad_dt ? 'invalid' : '')).
-					'</p>',
-				'post_lang' =>
-					'<p><label for="post_lang">'.__('Entry language').'</label>'.
-					form::combo('post_lang',$lang_combo,$post_lang).
-					'</p>',
-				'post_format' =>
-					'<div>'.
-					'<h5 id="label_format"><label for="post_format" class="classic">'.__('Text formatting').'</label></h5>'.
-					'<p>'.form::combo('post_format',$formaters_combo,$post_format,'maximal').
-					'</p>'.
-					'<p class="format_control control_no_xhtml">'.
-					'<a id="convert-xhtml" class="button'.($post_id && $post_format != 'wiki' ? ' hide' : '').'" href="post.php?id='.$post_id.'&amp;xconv=1">'.
-					__('Convert to XHTML').'</a></p></div>')),
-		'metas-box' => array(
-			'title' => __('Filing'),
-			'items' => array(
-				'post_selected' =>
-					'<p><label for="post_selected" class="classic">'.
-					form::checkbox('post_selected',1,$post_selected).' '.
-					__('Selected entry').'</label></p>',
-				'cat_id' =>
-					'<div>'.
-					'<h5 id="label_cat_id">'.__('Category').'</h5>'.
-					'<p><label for="cat_id">'.__('Category:').'</label>'.
-					form::combo('cat_id',$categories_combo,$cat_id,'maximal').
-					'</p>'.
-					($core->auth->check('categories', $core->blog->id) ?
-						'<div>'.
-						'<h5 id="create_cat">'.__('Add a new category').'</h5>'.
-						'<p><label for="new_cat_title">'.__('Title:').' '.
-						form::field('new_cat_title',30,255,'','maximal').'</label></p>'.
-						'<p><label for="new_cat_parent">'.__('Parent:').' '.
-						form::combo('new_cat_parent',$categories_combo,'','maximal').
-						'</label></p>'.
-						'</div>'
-					: '').
-					'</div>')),
-		'options-box' => array(
-			'title' => __('Options'),
-			'items' => array(
-				'post_open_comment_tb' =>
-					'<div>'.
-					'<h5 id="label_comment_tb">'.__('Comments and trackbacks list').'</h5>'.
-					'<p><label for="post_open_comment" class="classic">'.
-					form::checkbox('post_open_comment',1,$post_open_comment).' '.
-					__('Accept comments').'</label></p>'.
-					($core->blog->settings->system->allow_comments ?
-						(isContributionAllowed($post_id,strtotime($post_dt),true) ?
-							'' :
-							'<p class="form-note warn">'.
-							__('Warning: Comments are not more accepted for this entry.').'</p>') :
-						'<p class="form-note warn">'.
-						__('Comments are not accepted on this blog so far.').'</p>').
-					'<p><label for="post_open_tb" class="classic">'.
-					form::checkbox('post_open_tb',1,$post_open_tb).' '.
-					__('Accept trackbacks').'</label></p>'.
-					($core->blog->settings->system->allow_trackbacks ?
-						(isContributionAllowed($post_id,strtotime($post_dt),false) ?
-							'' :
-							'<p class="form-note warn">'.
-							__('Warning: Trackbacks are not more accepted for this entry.').'</p>') :
-						'<p class="form-note warn">'.__('Trackbacks are not accepted on this blog so far.').'</p>').
-					'</div>',
-				'post_password' =>
-					'<p><label for="post_password">'.__('Password').'</label>'.
-					form::field('post_password',10,32,html::escapeHTML($post_password),'maximal').
-					'</p>',
-				'post_url' =>
-					'<div class="lockable">'.
-					'<p><label for="post_url">'.__('Edit basename').'</label>'.
-					form::field('post_url',10,255,html::escapeHTML($post_url),'maximal').
-					'</p>'.
-					'<p class="form-note warn">'.
-					__('Warning: If you set the URL manually, it may conflict with another entry.').
-					'</p></div>'
-	))));
-
-	$main_items = new ArrayObject(array(
-		"post_title" =>
-			'<p class="col">'.
-			'<label class="required no-margin bold"><abbr title="'.__('Required field').'">*</abbr> '.__('Title:').'</label>'.
-			form::field('post_title',20,255,html::escapeHTML($post_title),'maximal').
-			'</p>',
-
-		"post_excerpt" =>
-			'<p class="area" id="excerpt-area"><label for="post_excerpt" class="bold">'.__('Excerpt:').' <span class="form-note">'.
-			__('Introduction to the post.').'</span></label> '.
-			form::textarea('post_excerpt',50,5,html::escapeHTML($post_excerpt)).
-			'</p>',
-
-		"post_content" =>
-			'<p class="area" id="content-area"><label class="required bold" '.
-			'for="post_content"><abbr title="'.__('Required field').'">*</abbr> '.__('Content:').'</label> '.
-			form::textarea('post_content',50,$core->auth->getOption('edit_size'),html::escapeHTML($post_content)).
-			'</p>',
-
-		"post_notes" =>
-			'<p class="area" id="notes-area"><label for="post_notes" class="bold">'.__('Personal notes:').' <span class="form-note">'.
-			__('Unpublished notes.').'</span></label>'.
-			form::textarea('post_notes',50,5,html::escapeHTML($post_notes)).
-			'</p>'
-		)
-	);
-
-	# --BEHAVIOR-- adminPostFormItems
-	$core->callBehavior('adminPostFormItems',$main_items,$sidebar_items, isset($post) ? $post : null);
-
-	echo '<div class="multi-part" title="'.($post_id ? __('Edit entry') : __('New entry')).'" id="edit-entry">';
-	echo '<form action="post.php" method="post" id="entry-form">';
-	echo '<div id="entry-wrapper">';
-	echo '<div id="entry-content"><div class="constrained">';
-
-	echo '<h3 class="out-of-screen-if-js">'.__('Edit post').'</h3>';
-
-	foreach ($main_items as $id => $item) {
-		echo $item;
-	}
-
-	# --BEHAVIOR-- adminPostForm (may be deprecated)
-	$core->callBehavior('adminPostForm',isset($post) ? $post : null);
-
-	echo
-	'<p class="border-top">'.
-	($post_id ? form::hidden('id',$post_id) : '').
-	'<input type="submit" value="'.__('Save').' (s)" '.
-	'accesskey="s" name="save" /> ';
-	if ($post_id) {
-		$preview_url =
-		$core->blog->url.$core->url->getURLFor('preview',$core->auth->userID().'/'.
-		http::browserUID(DC_MASTER_KEY.$core->auth->userID().$core->auth->getInfo('user_pwd')).
-		'/'.$post->post_url);
-		echo '<a id="post-preview" href="'.$preview_url.'" class="button modal" accesskey="p">'.__('Preview').' (p)'.'</a> ';
-	} else {
-		echo
-		'<a id="post-cancel" href="index.php" class="button" accesskey="c">'.__('Cancel').' (c)</a>';
-	}
-
-	echo
-	($can_delete ? '<input type="submit" class="delete" value="'.__('Delete').'" name="delete" />' : '').
-	$core->formNonce().
-	'</p>';
-
-	echo '</div></div>';		// End #entry-content
-	echo '</div>';		// End #entry-wrapper
-
-	echo '<div id="entry-sidebar">';
-
-	foreach ($sidebar_items as $id => $c) {
-		echo '<div id="'.$id.'" class="sb-box">'.
-			'<h4>'.$c['title'].'</h4>';
-		foreach ($c['items'] as $e_name=>$e_content) {
-			echo $e_content;
-		}
-		echo '</div>';
-	}
-
-
-	# --BEHAVIOR-- adminPostFormSidebar (may be deprecated)
-	$core->callBehavior('adminPostFormSidebar',isset($post) ? $post : null);
-	echo '</div>';		// End #entry-sidebar
-
-	echo '</form>';
-
-	# --BEHAVIOR-- adminPostForm
-	$core->callBehavior('adminPostAfterForm',isset($post) ? $post : null);
-
-	echo '</div>';
-}
-
-if ($post_id)
-{
-	/* Comments
-	-------------------------------------------------------- */
-
-	$params = array('post_id' => $post_id, 'order' => 'comment_dt ASC');
-
-	$comments = $core->blog->getComments(array_merge($params,array('comment_trackback'=>0)));
-
-	echo
-	'<div id="comments" class="clear multi-part" title="'.__('Comments').'">';
-	$combo_action = $comments_actions_page->getCombo();
-	$has_action = !empty($combo_action) && !$comments->isEmpty();
-	echo
-	'<p class="top-add"><a class="button add" href="#comment-form">'.__('Add a comment').'</a></p>';
-
-	if ($has_action) {
-		echo '<form action="post.php" id="form-comments" method="post">';
-	}
-
-	echo '<h3>'.__('Comments').'</h3>';
-	if (!$comments->isEmpty()) {
-		showComments($comments,$has_action);
-	} else {
-		echo '<p>'.__('No comment').'</p>';
-	}
-
-	if ($has_action) {
-		echo
-		'<div class="two-cols">'.
-		'<p class="col checkboxes-helpers"></p>'.
-
-		'<p class="col right"><label for="action" class="classic">'.__('Selected comments action:').'</label> '.
-		form::combo('action',$combo_action).
-		form::hidden(array('section'),'comments').
-		form::hidden(array('id'),$post_id).
-		$core->formNonce().
-		'<input type="submit" value="'.__('ok').'" /></p>'.
-		'</div>'.
-		'</form>';
-	}
-	/* Add a comment
-	-------------------------------------------------------- */
-
-	echo
-	'<div class="fieldset clear">'.
-	'<h3>'.__('Add a comment').'</h3>'.
-
-	'<form action="comment.php" method="post" id="comment-form">'.
-	'<div class="constrained">'.
-	'<p><label for="comment_author" class="required"><abbr title="'.__('Required field').'">*</abbr> '.__('Name:').'</label>'.
-	form::field('comment_author',30,255,html::escapeHTML($core->auth->getInfo('user_cn'))).
-	'</p>'.
-
-	'<p><label for="comment_email">'.__('Email:').'</label>'.
-	form::field('comment_email',30,255,html::escapeHTML($core->auth->getInfo('user_email'))).
-	'</p>'.
-
-	'<p><label for="comment_site">'.__('Web site:').'</label>'.
-	form::field('comment_site',30,255,html::escapeHTML($core->auth->getInfo('user_url'))).
-	'</p>'.
-
-	'<p class="area"><label for="comment_content" class="required"><abbr title="'.__('Required field').'">*</abbr> '.
-	__('Comment:').'</label> '.
-	form::textarea('comment_content',50,8,html::escapeHTML('')).
-	'</p>'.
-
-	'<p>'.
-	form::hidden('post_id',$post_id).
-	$core->formNonce().
-	'<input type="submit" name="add" value="'.__('Save').'" /></p>'.
-	'</div>'. #constrained
-
-	'</form>'.
-	'</div>'. #add comment
-	'</div>'; #comments
-}
-
-if ($post_id && $post_status == 1)
-{
-	/* Trackbacks
-	-------------------------------------------------------- */
-
-	$params = array('post_id' => $post_id, 'order' => 'comment_dt ASC');
-	$trackbacks = $core->blog->getComments(array_merge($params, array('comment_trackback' => 1)));
-
-	# Actions combo box
-	$combo_action = $comments_actions_page->getCombo();
-	$has_action = !empty($combo_action) && !$trackbacks->isEmpty();
-
-	if (!empty($_GET['tb_auto'])) {
-		$tb_urls = implode("\n", $TB->discover($post_excerpt_xhtml.' '.$post_content_xhtml));
-	}
-
-	# Display tab
-	echo
-	'<div id="trackbacks" class="clear multi-part" title="'.__('Trackbacks').'">';
-
-	# tracbacks actions
-	if ($has_action) {
-		echo '<form action="post.php" id="form-trackbacks" method="post">';
-	}
-
-	echo '<h3>'.__('Trackbacks received').'</h3>';
-
-	if (!$trackbacks->isEmpty()) {
-		showComments($trackbacks, $has_action, true);
-	} else {
-		echo '<p>'.__('No trackback').'</p>';
-	}
-
-	if ($has_action) {
-		echo
-		'<div class="two-cols">'.
-		'<p class="col checkboxes-helpers"></p>'.
-
-		'<p class="col right"><label for="action" class="classic">'.__('Selected trackbacks action:').'</label> '.
-		form::combo('action', $combo_action).
-		form::hidden('id',$post_id).
-		form::hidden(array('section'),'trackbacks').
-		$core->formNonce().
-		'<input type="submit" value="'.__('ok').'" /></p>'.
-		'</div>'.
-		'</form>';
-	}
-
-	/* Add trackbacks
-	-------------------------------------------------------- */
-	if ($can_edit_post && $post->post_status) {
-		echo
-		'<div class="fieldset clear">';
-
-		echo
-		'<h3>'.__('Ping blogs').'</h3>'.
-		'<form action="post.php?id='.$post_id.'" id="trackback-form" method="post">'.
-		'<p><label for="tb_urls" class="area">'.__('URLs to ping:').'</label>'.
-		form::textarea('tb_urls', 60, 5, $tb_urls).
-		'</p>'.
-
-		'<p><label for="tb_excerpt" class="area">'.__('Excerpt to send:').'</label>'.
-		form::textarea('tb_excerpt', 60, 5, $tb_excerpt).'</p>'.
-
-		'<p>'.
-		$core->formNonce().
-		'<input type="submit" name="ping" value="'.__('Ping blogs').'" />'.
-		(empty($_GET['tb_auto']) ?
-			'&nbsp;&nbsp;<a class="button" href="'.
-			'post.php?id='.$post_id.'&amp;tb_auto=1&amp;tb=1'.
-			'">'.__('Auto discover ping URLs').'</a>'
-		: '').
-		'</p>'.
-		'</form>';
-
-		$pings = $TB->getPostPings($post_id);
-
-		if (!$pings->isEmpty())
-		{
-			echo '<h3>'.__('Previously sent pings').'</h3>';
-
-			echo '<ul class="nice">';
-			while ($pings->fetch()) {
-				echo
-				'<li>'.dt::dt2str(__('%Y-%m-%d %H:%M'), $pings->ping_dt).' - '.
-				$pings->ping_url.'</li>';
-			}
-			echo '</ul>';
-		}
-
-		echo '</div>';
-	}
-
-	echo '</div>'; #trackbacks
-}
-
-# Controls comments or trakbacks capabilities
-function isContributionAllowed($id,$dt,$com=true)
-{
-	global $core;
-
-	if (!$id) {
-		return true;
-	}
-	if ($com) {
-		if (($core->blog->settings->system->comments_ttl == 0) ||
-			(time() - $core->blog->settings->system->comments_ttl*86400 < $dt)) {
-			return true;
-		}
-	} else {
-		if (($core->blog->settings->system->trackbacks_ttl == 0) ||
-			(time() - $core->blog->settings->system->trackbacks_ttl*86400 < $dt)) {
-			return true;
-		}
-	}
-	return false;
-}
-
-# Show comments or trackbacks
-function showComments($rs,$has_action,$tb=false)
-{
-	echo
-	'<div class="table-outer">'.
-	'<table class="comments-list"><tr>'.
-	'<th colspan="2" class="first">'.__('Author').'</th>'.
-	'<th>'.__('Date').'</th>'.
-	'<th class="nowrap">'.__('IP address').'</th>'.
-	'<th>'.__('Status').'</th>'.
-	'<th>'.__('Edit').'</th>'.
-	'</tr>';
-	$comments = array();
-	if (isset($_REQUEST['comments'])) {
-		foreach ($_REQUEST['comments'] as $v) {
-			$comments[(integer)$v]=true;
-		}
-	}
-
-	while($rs->fetch())
-	{
-		$comment_url = 'comment.php?id='.$rs->comment_id;
-
-		$img = '<img alt="%1$s" title="%1$s" src="images/%2$s" />';
-		switch ($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;
-		}
-
-		echo
-		'<tr class="line'.($rs->comment_status != 1 ? ' offline' : '').'"'.
-		' id="c'.$rs->comment_id.'">'.
-
-		'<td class="nowrap">'.
-		($has_action ? form::checkbox(array('comments[]'),$rs->comment_id,isset($comments[$rs->comment_id]),'','',0,'title="'.($tb ? __('select this trackback') : __('select this comment')).'"') : '').'</td>'.
-		'<td class="maximal">'.html::escapeHTML($rs->comment_author).'</td>'.
-		'<td class="nowrap">'.dt::dt2str(__('%Y-%m-%d %H:%M'),$rs->comment_dt).'</td>'.
-		'<td class="nowrap"><a href="comments.php?ip='.$rs->comment_ip.'">'.$rs->comment_ip.'</a></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').'" /> '.__('Edit').'</a></td>'.
-
-		'</tr>';
-	}
-
-	echo '</table></div>';
-}
-
-dcPage::helpBlock('core_post','core_trackbacks','core_wiki');
-dcPage::close();
+	$_ctx->setAlert(__('Comment has been successfully created.'));
+}	
+	
+$core->tpl->display('post.html.twig');
+?>
Index: admin/posts.php
===================================================================
--- admin/posts.php	(revision 2566)
+++ admin/posts.php	(revision 2593)
@@ -4,5 +4,5 @@
 # This file is part of Dotclear 2.
 #
-# Copyright (c) 2003-2013 Olivier Meunier & Association Dotclear
+# Copyright (c) 2003-2011 Olivier Meunier & Association Dotclear
 # Licensed under the GPL version 2.0 license.
 # See LICENSE file or
@@ -12,5 +12,5 @@
 
 require dirname(__FILE__).'/../inc/admin/prepend.php';
-
+global $_ctx;
 dcPage::check('usage,contentadmin');
 
@@ -47,287 +47,151 @@
 {
 	# Filter form we'll put in html_block
-	$users_combo = array_merge(
-		array('-' => ''),
-		dcAdminCombos::getUsersCombo($users)
+	$users_combo = $categories_combo = array();
+	while ($users->fetch())
+	{
+		$user_cn = dcUtils::getUserCN($users->user_id,$users->user_name,
+		$users->user_firstname,$users->user_displayname);
+		
+		if ($user_cn != $users->user_id) {
+			$user_cn .= ' ('.$users->user_id.')';
+		}
+		
+		$users_combo[$user_cn] = $users->user_id; 
+	}
+	
+
+# Getting categories
+$categories_combo = array();
+try {
+	$categories = $core->blog->getCategories(array('post_type'=>'post'));
+	while ($categories->fetch()) {
+		$categories_combo[$categories->cat_id] = 
+			str_repeat('&nbsp;&nbsp;',$categories->level-1).
+			($categories->level-1 == 0 ? '' : '&bull; ').
+			html::escapeHTML($categories->cat_title);
+	}
+} catch (Exception $e) { }
+	$status_combo = array(
 	);
+	foreach ($core->blog->getAllPostStatus() as $k => $v) {
+		$status_combo[(string) $k] = (string)$v;
+	}
+	
+	$selected_combo = array(
+	'1' => __('is selected'),
+	'0' => __('is not selected')
+	);
+	
+	# Months array
+	while ($dates->fetch()) {
+		$dt_m_combo[$dates->year().$dates->month()] = dt::str('%B %Y',$dates->ts());
+	}
+	
+	while ($langs->fetch()) {
+		$lang_combo[$langs->post_lang] = $langs->post_lang;
+	}
+}
+$form = new dcForm($core,'post','post.php');
 
-	$categories_combo = array_merge(
-		array(
-			new formSelectOption('-',''),
-			new formSelectOption(__('(No cat)'),'NULL')),
-		dcAdminCombos::getCategoriesCombo($categories,false)
+
+# Actions combo box
+$combo_action = array();
+if ($core->auth->check('publish,contentadmin',$core->blog->id))
+{
+	$combo_action[__('Status')] = array(
+		__('Publish') => 'publish',
+		__('Unpublish') => 'unpublish',
+		__('Schedule') => 'schedule',
+		__('Mark as pending') => 'pending'
 	);
-	$categories_values = array();
-	foreach ($categories_combo as $cat) {
-		if (isset($cat->value)) {
-			$categories_values[$cat->value]=true;
-		}
+}
+$combo_action[__('Mark')] = array(
+	__('Mark as selected') => 'selected',
+	__('Mark as unselected') => 'unselected'
+);
+$combo_action[__('Change')] = array(
+	__('Change category') => 'category',
+	__('Change language') => 'lang');
+if ($core->auth->check('admin',$core->blog->id))
+{
+	$combo_action[__('Change')] = array_merge($combo_action[__('Change')],
+		array(__('Change author') => 'author'));
+}
+if ($core->auth->check('delete,contentadmin',$core->blog->id))
+{
+	$combo_action[__('Delete')] = array(__('Delete') => 'delete');
+}
+
+# --BEHAVIOR-- adminPostsActionsCombo
+$core->callBehavior('adminPostsActionsCombo',array(&$combo_action));
+
+
+
+class monthdcFilterCombo extends dcFilterCombo {
+	public function applyFilter($params) {
+		$month=$this->avalues['values'][0];
+		$params['post_month'] = substr($month,4,2);
+		$params['post_year'] = substr($month,0,4);
+	}
+}
+
+class PostsFetcher extends dcListFetcher {
+
+	public function getEntries($params,$offset,$limit) {
+		$params['limit'] = array($offset,$limit);
+		return $this->core->blog->getPosts($params);
 	}
 
-	$status_combo = array_merge(
-		array('-' => ''),
-		dcAdminCombos::getPostStatusesCombo()
-	);
-
-	$selected_combo = array(
-	'-' => '',
-	__('Selected') => '1',
-	__('Not selected') => '0'
-	);
-
-	$attachment_combo = array(
-	'-' => '',
-	__('With attachments') => '1',
-	__('Without attachments') => '0'
-	);
-
-	# Months array
-	$dt_m_combo = array_merge(
-		array('-' => ''),
-		dcAdminCombos::getDatesCombo($dates)
-	);
-
-	$lang_combo = array_merge(
-		array('-' => ''),
-		dcAdminCombos::getLangsCombo($langs,false)
-	);
-
-	$sortby_combo = array(
-	__('Date') => 'post_dt',
-	__('Title') => 'post_title',
-	__('Category') => 'cat_title',
-	__('Author') => 'user_id',
-	__('Status') => 'post_status',
-	__('Selected') => 'post_selected',
-	__('Number of comments') => 'nb_comment',
-	__('Number of trackbacks') => 'nb_trackback'
-	);
-
-	$order_combo = array(
-	__('Descending') => 'desc',
-	__('Ascending') => 'asc'
-	);
-}
-
-# Actions combo box
-
-$posts_actions_page = new dcPostsActionsPage($core,'posts.php');
-
-if ($posts_actions_page->process()) {
-	return;
-}
-
-/* Get posts
--------------------------------------------------------- */
-$user_id = !empty($_GET['user_id']) ?	$_GET['user_id'] : '';
-$cat_id = !empty($_GET['cat_id']) ?	$_GET['cat_id'] : '';
-$status = isset($_GET['status']) ?	$_GET['status'] : '';
-$selected = isset($_GET['selected']) ?	$_GET['selected'] : '';
-$attachment = isset($_GET['attachment']) ?	$_GET['attachment'] : '';
-$month = !empty($_GET['month']) ?		$_GET['month'] : '';
-$lang = !empty($_GET['lang']) ?		$_GET['lang'] : '';
-$sortby = !empty($_GET['sortby']) ?	$_GET['sortby'] : 'post_dt';
-$order = !empty($_GET['order']) ?		$_GET['order'] : 'desc';
-
-$show_filters = false;
-
-$page = !empty($_GET['page']) ? max(1,(integer) $_GET['page']) : 1;
-$nb_per_page =  30;
-
-if (!empty($_GET['nb']) && (integer) $_GET['nb'] > 0) {
-	if ($nb_per_page != $_GET['nb']) {
-		$show_filters = true;
+	public function getEntriesCount($params) {
+		$count = $this->core->blog->getPosts($params,true);
+		return $count->f(0);
 	}
-	$nb_per_page = (integer) $_GET['nb'];
-}
-
-$params['limit'] = array((($page-1)*$nb_per_page),$nb_per_page);
-$params['no_content'] = true;
-
-# - User filter
-if ($user_id !== '' && in_array($user_id,$users_combo)) {
-	$params['user_id'] = $user_id;
-	$show_filters = true;
-} else {
-	$user_id='';
-}
-
-# - Categories filter
-if ($cat_id !== '' && isset($categories_values[$cat_id])) {
-	$params['cat_id'] = $cat_id;
-	$show_filters = true;
-} else {
-	$cat_id='';
-}
-
-# - Status filter
-if ($status !== '' && in_array($status,$status_combo)) {
-	$params['post_status'] = $status;
-	$show_filters = true;
-} else {
-	$status='';
-}
-
-# - Selected filter
-if ($selected !== '' && in_array($selected,$selected_combo)) {
-	$params['post_selected'] = $selected;
-	$show_filters = true;
-} else {
-	$selected='';
-}
-
-# - Selected filter
-if ($attachment !== '' && in_array($attachment,$attachment_combo)) {
-	$params['media'] = $attachment;
-	$params['link_type'] = 'attachment';
-	$show_filters = true;
-} else {
-	$attachment='';
-}
-
-# - Month filter
-if ($month !== '' && in_array($month,$dt_m_combo)) {
-	$params['post_month'] = substr($month,4,2);
-	$params['post_year'] = substr($month,0,4);
-	$show_filters = true;
-} else {
-	$month='';
-}
-
-# - Lang filter
-if ($lang !== '' && in_array($lang,$lang_combo)) {
-	$params['post_lang'] = $lang;
-	$show_filters = true;
-} else {
-	$lang='';
-}
-
-# - Sortby and order filter
-if ($sortby !== '' && in_array($sortby,$sortby_combo)) {
-	if ($order !== '' && in_array($order,$order_combo)) {
-		$params['order'] = $sortby.' '.$order;
-	} else {
-		$order='desc';
-	}
-
-	if ($sortby != 'post_dt' || $order != 'desc') {
-		$show_filters = true;
-	}
-} else {
-	$sortby='post_dt';
-	$order='desc';
-}
-
-# Get posts
-try {
-	$posts = $core->blog->getPosts($params);
-	$counter = $core->blog->getPosts($params,true);
-	$post_list = new adminPostList($core,$posts,$counter->f(0));
-} catch (Exception $e) {
-	$core->error->add($e->getMessage());
 }
 
 /* DISPLAY
 -------------------------------------------------------- */
+$filterSet = new dcFilterSet($core,'fposts','posts.php');
 
-$form_filter_title = __('Show filters and display options');
-$starting_script  = dcPage::jsLoad('js/_posts_list.js');
-$starting_script .= dcPage::jsLoad('js/filter-controls.js');
-$starting_script .=
-	'<script type="text/javascript">'."\n".
-	"//<![CDATA["."\n".
-	dcPage::jsVar('dotclear.msg.show_filters', $show_filters ? 'true':'false')."\n".
-	dcPage::jsVar('dotclear.msg.filter_posts_list',$form_filter_title)."\n".
-	dcPage::jsVar('dotclear.msg.cancel_the_filter',__('Cancel filters and display options'))."\n".
-	"//]]>".
-	"</script>";
+$filterSet
+	->addFilter(new dcFilterRichCombo(
+		'users',__('Author'), __('Author'), 'user_id', $users_combo,array(
+			'multiple' => true)))
+	->addFilter(new dcFilterRichCombo(
+		'category',__('Category'), __('Category'), 'cat_id', $categories_combo,array(
+			'multiple' => true)))
+	->addFilter(new dcFilterRichCombo(
+		'post_status',__('Status'), __('Status'), 'post_status', $status_combo))
+	->addFilter(new dcFilterRichCombo(
+		'lang',__('Lang'), __('Lang'), 'post_lang', $lang_combo))
+	->addFilter(new dcFilterCombo(
+		'selected',__('Selected'), __('The post : '),'post_selected', $selected_combo))
+	->addFilter(new monthdcFilterCombo(
+		'month',__('Month'),__('Month'), 'post_month', $dt_m_combo,array('singleval' => 1)))
+	->addFilter(new dcFilterText(
+		'search',__('Contains'),__('The entry contains'), 'search',20,255));
 
-dcPage::open(__('Entries'),$starting_script,
-	dcPage::breadcrumb(
-		array(
-			html::escapeHTML($core->blog->name) => '',
-			__('Entries') => ''
-		))
-);
-if (!empty($_GET['upd'])) {
-	dcPage::success(__('Selected entries have been successfully updated.'));
-} elseif (!empty($_GET['del'])) {
-	dcPage::success(__('Selected entries have been successfully deleted.'));
-}
-if (!$core->error->flag())
-{
-	echo
-	'<p class="top-add"><a class="button add" href="post.php">'.__('New entry').'</a></p>'.
-	'<form action="posts.php" method="get" id="filters-form">'.
-	'<h3 class="out-of-screen-if-js">'.$form_filter_title.'</h3>'.
 
-	'<div class="table">'.
-	'<div class="cell">'.
-	'<h4>'.__('Filters').'</h4>'.
-	'<p><label for="user_id" class="ib">'.__('Author:').'</label> '.
-	form::combo('user_id',$users_combo,$user_id).'</p>'.
-	'<p><label for="cat_id" class="ib">'.__('Category:').'</label> '.
-	form::combo('cat_id',$categories_combo,$cat_id).'</p>'.
-	'<p><label for="status" class="ib">'.__('Status:').'</label> ' .
-	form::combo('status',$status_combo,$status).'</p> '.
-	'</div>'.
+$lfetcher = new PostsFetcher($core);
+$lposts = new dcItemList ($core,array('lposts','form-entries'),$lfetcher,'posts_actions.php');
+$lposts->setFilterSet($filterSet);
+$lposts->addTemplate('posts_cols.html.twig');
 
-	'<div class="cell filters-sibling-cell">'.
-	'<p><label for="selected" class="ib">'.__('Selected:').'</label> '.
-	form::combo('selected',$selected_combo,$selected).'</p>'.
-	'<p><label for="attachment" class="ib">'.__('Attachments:').'</label> '.
-	form::combo('attachment',$attachment_combo,$attachment).'</p>'.
-	'<p><label for="month" class="ib">'.__('Month:').'</label> '.
-	form::combo('month',$dt_m_combo,$month).'</p>'.
-	'<p><label for="lang" class="ib">'.__('Lang:').'</label> '.
-	form::combo('lang',$lang_combo,$lang).'</p> '.
-	'</div>'.
+$lposts
+	->addColumn(new dcColumn('title',__('Title'),'post_title'))
+	->addColumn(new dcColumn('cat',__('Category'),'cat_title'))
+	->addColumn(new dcColumn('date',__('Date'),'post_date'))
+	->addColumn(new dcColumn('datetime',__('Date and Time'),'post_dt'))
+	->addColumn(new dcColumn('author',__('Author'),'user_id'))
+	->addColumn(new dcColumn('status',__('Status'),'post_status'));
 
-	'<div class="cell filters-options">'.
-	'<h4>'.__('Display options').'</h4>'.
-	'<p><label for="sortby" class="ib">'.__('Order by:').'</label> '.
-	form::combo('sortby',$sortby_combo,$sortby).'</p>'.
-	'<p><label for="order" class="ib">'.__('Sort:').'</label> '.
-	form::combo('order',$order_combo,$order).'</p>'.
-	'<p><span class="label ib">'.__('Show').'</span> <label for="nb" class="classic">'.
-	form::field('nb',3,3,$nb_per_page).' '.
-	__('entries per page').'</label></p>'.
-	'</div>'.
-	'</div>'.
 
-	'<p><input type="submit" value="'.__('Apply filters and display options').'" />'.
-	'<br class="clear" /></p>'. //Opera sucks
-	'</form>';
+$lposts->setup();
 
-	# Show posts
-	$post_list->display($page,$nb_per_page,
-	'<form action="posts.php" method="post" id="form-entries">'.
+$_ctx
+	->setBreadCrumb(array(__('Entries') => 'posts.php'));
 
-	'%s'.
 
-	'<div class="two-cols">'.
-	'<p class="col checkboxes-helpers"></p>'.
+$core->tpl->display('posts.html.twig');
 
-	'<p class="col right"><label for="action" class="classic">'.__('Selected entries action:').'</label> '.
-	form::combo('action',$posts_actions_page->getCombo()).
-	'<input type="submit" value="'.__('ok').'" /></p>'.
-	form::hidden(array('user_id'),$user_id).
-	form::hidden(array('cat_id'),$cat_id).
-	form::hidden(array('status'),$status).
-	form::hidden(array('selected'),$selected).
-	form::hidden(array('attachment'),$attachment).
-	form::hidden(array('month'),$month).
-	form::hidden(array('lang'),$lang).
-	form::hidden(array('sortby'),$sortby).
-	form::hidden(array('order'),$order).
-	form::hidden(array('page'),$page).
-	form::hidden(array('nb'),$nb_per_page).
-	$core->formNonce().
-	'</div>'.
-	'</form>',
-	$show_filters
-	);
-}
 
-dcPage::helpBlock('core_posts');
-dcPage::close();
+?>
