<?php
/**
 * This file implements the UI controller for managing posts.
 *
 * b2evolution - {@link http://b2evolution.net/}
 * Released under GNU GPL License - {@link http://b2evolution.net/about/gnu-gpl-license}
 * @copyright (c)2003-2020 by Francois Planque - {@link http://fplanque.com/}
 *
 * @todo dh> AFAICS there are three params used for "item ID": "p", "post_ID"
 *       and "item_ID". This should get cleaned up.
 *       Side effect: "post_ID required" error if you switch tabs (expert/simple),
 *       after an error is display (e.g. entering an invalid issue time).
 *       (related to $tab_switch_params)
 * fp> Yes, it's a mess...
 *     Ironically the correct name would be itm_ID (which is what the DB uses,
 *     except for the Items table which should actually also use itm_ prefixes instead of post_
 *     ... a lot of history lead to this :p
 *
 * @package admin
 */
if( !defined('EVO_MAIN_INIT') ) die( 'Please, do not access this page directly.' );

/**
 * @var AdminUI
 */
global $AdminUI;

/**
 * @var UserSettings
 */
global $UserSettings;

/**
 * @var User
 */
global $current_User;

/**
 * @var Blog
 */
global $Collection, $Blog;

global $admin_url;

global $basehost;

$action = param_action( 'list' );
$orig_action = $action; // Used to know what action is called really

$AdminUI->set_path( 'collections', 'posts' );	// Sublevel may be attached below

// We should activate toolbar menu items for this controller
$activate_collection_toolbar = true;

/*
 * Init the objects we want to work on.
 *
 * Autoselect a blog where we have PERMISSION to browse (preferably the last used blog):
 * Note: for some actions, we'll get the blog from the post ID
 */

$mass_create = param( 'mass_create', 'integer' );
if( $action == 'new_switchtab' && !empty( $mass_create ) )
{	// Replace action with mass create action
	$action = 'new_mass';
}

// for post from files
if( $action == 'group_action' )
{ // Get the real action from the select:
	$action = param( 'group_action', 'string', '' );
}


switch( $action )
{
	case 'edit':
	case 'propose':
	case 'history':
	case 'history_lastseen':
	case 'history_details':
	case 'history_compare':
	case 'history_restore':
		param( 'p', 'integer', true, true );
		param( 'restore', 'integer', 0 );

		if( $restore )
		{ // Restore a post from Session
			$edited_Item = get_session_Item( $p );
		}

		if( empty( $edited_Item ) )
		{ // Load post to edit from DB:
			$ItemCache = & get_ItemCache();
			$edited_Item = & $ItemCache->get_by_ID( $p );
		}

		if( $action == 'propose' )
		{	// Action to propose a change
			// Check if current User can create a new proposed change:
			$edited_Item->can_propose_change( true );
			if( $last_proposed_Revision = $edited_Item->get_revision( 'last_proposed' ) )
			{	// Suggest item fields values from last proposed change when user creates new propose change:
				$edited_Item->set( 'revision', 'p'.$last_proposed_Revision->iver_ID );
			}
		}

		// Load the blog we're in:
		$Collection = $Blog = & $edited_Item->get_Blog();
		set_working_blog( $Blog->ID );

		// Where are we going to redirect to?
		param( 'redirect_to', 'url', url_add_param( $admin_url, 'ctrl=items&filter=restore&blog='.$Blog->ID.'&highlight='.$edited_Item->ID, '&' ) );

		// Check if the editing Item has at least one proposed change:
		if( $action == 'edit' &&
		    ! $edited_Item->check_proposed_change_restriction( 'warning' ) &&
		    ( $last_proposed_Revision = $edited_Item->get_revision( 'last_proposed' ) ) )
		{	// Use item fields values from last proposed change:
			$edited_Item->set( 'revision', 'p'.$last_proposed_Revision->iver_ID );
		}
		break;

	case 'mass_edit':
		break;

	case 'update_edit':
	case 'update':
	case 'update_publish':
	case 'update_status':
	case 'publish':
	case 'publish_now':
	case 'restrict':
	case 'deprecate':
	case 'delete':
	case 'merge':
	case 'append':
	// Note: we need to *not* use $p in the cases above or it will conflict with the list display
	case 'edit_switchtab': // this gets set as action by JS, when we switch tabs
	case 'edit_type': // this gets set as action by JS, when we switch tabs
	case 'extract_tags':
	case 'save_propose':
	case 'accept_propose':
	case 'reject_propose':
	case 'link_version':
	case 'unlink_version':
		if( $action != 'edit_switchtab' && $action != 'edit_type' )
		{ // Stop a request from the blocked IP addresses or Domains
			antispam_block_request();
		}

		param( 'post_ID', 'integer', true, true );

		if( $action == 'edit_type' )
		{ // Load post from Session
			$edited_Item = get_session_Item( $post_ID );

			// Memorize action for list of post types
			memorize_param( 'action', 'string', '', $action );

			// Use this param to know how to redirect after item type updating
			param( 'from_tab', 'string', '' );
		}

		if( empty( $edited_Item ) || ( $action == 'edit_type' && $from_tab == 'type' ) )
		{ // Load post to edit from DB:
			// ...force the loading of post from DB if we are changing the Item Type from a post list
			$ItemCache = & get_ItemCache();
			$edited_Item = & $ItemCache->get_by_ID( $post_ID );
		}

		// Load the blog we're in:
		$Collection = $Blog = & $edited_Item->get_Blog();
		set_working_blog( $Blog->ID );

		// Where are we going to redirect to?
		param( 'redirect_to', 'url', url_add_param( $admin_url, 'ctrl=items&filter=restore&blog='.$Blog->ID.'&highlight='.$edited_Item->ID, '&' ) );

		// What form button has been pressed?
		param( 'save', 'string', '' );
		$exit_after_save = ( $action != 'update_edit' && $action != 'extract_tags' );

		if( $action == 'update_edit' )
		{	// Get params to restore height and scroll position of item content field:
			$content_height = intval( param( 'content_height', 'string', 0 ) );
			$content_scroll = intval( param( 'content_scroll', 'string', 0 ) );
		}
		break;

	case 'update_type':
		break;

	case 'mass_save' :
		param( 'redirect_to', 'url', url_add_param( $admin_url, 'ctrl=items&filter=restore&blog=' . $Blog->ID, '&' ) );
		break;

	case 'new':
	case 'new_switchtab': // this gets set as action by JS, when we switch tabs
	case 'new_mass':
	case 'new_type':
	case 'copy':
	case 'new_version':
	case 'create_edit':
	case 'create_link':
	case 'create':
	case 'create_publish':
	case 'list':
		if( in_array( $action, array( 'create_edit', 'create_link', 'create', 'create_publish' ) ) )
		{ // Stop a request from the blocked IP addresses or Domains
			antispam_block_request();
		}

		if( $action == 'list' )
		{	// We only need view permission
			$selected = autoselect_blog( 'blog_ismember', 'view' );
		}
		else
		{	// We need posting permission
			$selected = autoselect_blog( 'blog_post_statuses', 'edit' );
		}

		if( ! $selected  )
		{ // No blog could be selected
			$Messages->add( TB_('Sorry, you have no permission to post yet.'), 'error' );
			$action = 'nil';
		}
		else
		{
			if( set_working_blog( $selected ) )	// set $blog & memorize in user prefs
			{	// Selected a new blog:
				$BlogCache = & get_BlogCache();
				$Collection = $Blog = & $BlogCache->get_by_ID( $blog );
			}

			// Where are we going to redirect to?
			param( 'redirect_to', 'url', url_add_param( $admin_url, 'ctrl=items&filter=restore&blog='.$Blog->ID, '&' ) );

			// What form buttton has been pressed?
			param( 'save', 'string', '' );
			$exit_after_save = ( $action != 'create_edit' && $action != 'create_link' );
		}
		break;

	case 'make_posts_pre':
		// form for edit several posts

		if( empty( $Blog ) )
		{
			$Messages->add( TB_('No destination blog is selected.'), 'error' );
			break;
		}

		// Check perms:
		check_user_perm( 'blog_post_statuses', 'edit', true, $Blog->ID );
		break;

	case 'make_posts_from_files':
		// Make posts with selected images:

		// Stop a request from the blocked IP addresses or Domains
		antispam_block_request();

		// Check that this action request is not a CSRF hacked request:
		$Session->assert_received_crumb( 'file' );

		$FileRootCache = & get_FileRootCache();
		// getting root
		$root = param( 'root', 'string' );

		$fm_FileRoot = & $FileRootCache->get_by_ID( $root, true );

		// fp> TODO: this block should move to a general level
		// Try to go to the right blog:
		if( $fm_FileRoot->type == 'collection' )
		{
			set_working_blog( $fm_FileRoot->in_type_ID );
			// Load the blog we're in:
			$Collection = $Blog = & $BlogCache->get_by_ID( $blog );
		}
		// ---

		if( empty( $Blog ) )
		{
			$Messages->add( TB_('No destination blog is selected.'), 'error' );
			break;
		}

		// Get status (includes PERM CHECK):
		$item_status = param( 'post_status', 'string', $Blog->get_allowed_item_status() );
		check_user_perm( 'blog_post!'.$item_status, 'create', true, $Blog->ID );

		load_class( 'files/model/_filelist.class.php', 'FileList' );
		$selected_Filelist = new Filelist( $fm_FileRoot, false );
		$fm_selected = param( 'fm_selected', 'array:filepath' );
		foreach( $fm_selected as $l_source_path )
		{
			$selected_Filelist->add_by_subpath( $l_source_path, true );
		}
		// make sure we have loaded metas for all files in selection!
		$selected_Filelist->load_meta();

		// Ready to create post(s):
		load_class( 'items/model/_item.class.php', 'Item' );

		$fileNum = 0;
		$cat_Array = param( 'category', 'array:string' );
		$title_Array = param( 'post_title', 'array:string' );
		$new_categories = param( 'new_categories', 'array:string', array() );
		while( $l_File = & $selected_Filelist->get_next() )
		{
			// Create a post:
			$edited_Item = new Item();
			$edited_Item->set( 'status', $item_status );

			// replacing category if selected at preview screen
			if( isset( $cat_Array[ $fileNum ] ) )
			{
				// checking if selected "same as above" category option
				switch( $cat_Array[ $fileNum ] )
				{
					case 'same':
						// Get a category ID from previous item
						$cat_Array[ $fileNum ] = $cat_Array[ $fileNum - 1 ];
						break;

					case 'new':
						// Create a new category from an entered name

						// Check permissions:
						check_user_perm( 'blog_cats', '', true, $blog );

						$ChapterCache = & get_ChapterCache();
						$new_Chapter = & $ChapterCache->new_obj( NULL, $blog );	// create new category object
						$new_Chapter->set( 'name', $new_categories[ $fileNum ] );
						if( $new_Chapter->dbinsert() !== false )
						{ // Category is created successfully
							$Messages->add_to_group( sprintf( TB_('New category %s created.'), '<b>'.$new_categories[ $fileNum ].'</b>' ), 'success', TB_('Creating posts:') );
							$ChapterCache->clear();
						}
						else
						{ // Error on creating new category
							$Messages->add( sprintf( TB_('New category %s creation failed.'), '<b>'.$new_categories[ $fileNum ].'</b>' ), 'error' );
							continue 2; // Skip this post
						}
						$cat_Array[ $fileNum ] = $new_Chapter->ID;
						break;
				}
				$edited_Item->set( 'main_cat_ID', intval( $cat_Array[ $fileNum ] ) );
			}
			else
			{ // Use default category ID if it was not selected on the form
				$edited_Item->set( 'main_cat_ID', $Blog->get_default_cat_ID() );
			}

			$title = $l_File->get('title');
			if( empty( $title ) )
			{
				$title = $l_File->get('name');
			}

			$edited_Item->set( 'title', $title );

			// replacing category if selected at preview screen
			if( isset( $title_Array[ $fileNum ] ) ) {
				$edited_Item->set( 'title', $title_Array[ $fileNum ] );
			}

			$DB->begin( 'SERIALIZABLE' );
			// INSERT NEW POST INTO DB:
			if( $edited_Item->dbinsert() )
			{
				// echo '<br>file meta: '.$l_File->meta;
				if( $l_File->meta == 'notfound' )
				{ // That file has no meta data yet, create it now!
					$l_File->dbsave();
				}

				// Let's make the link!
				$edited_Link = new Link();
				$edited_Link->set( 'itm_ID', $edited_Item->ID );
				$edited_Link->set( 'file_ID', $l_File->ID );
				$edited_Link->set( 'position', 'teaser' );
				$edited_Link->set( 'order', 1 );
				$edited_Link->dbinsert();

				$DB->commit();

				// Invalidate blog's media BlockCache
				BlockCache::invalidate_key( 'media_coll_ID', $edited_Item->get_blog_ID() );

				$Messages->add_to_group( sprintf( TB_('&laquo;%s&raquo; has been posted.'), $l_File->dget('name') ), 'success', TB_('Creating posts:') );
				$fileNum++;
			}
			else
			{
				$DB->rollback();
				$Messages->add( sprintf( TB_('&laquo;%s&raquo; couldn\'t be posted.'), $l_File->dget('name') ), 'error' );
			}
		}

		// Note: we redirect without restoring filter. This should allow to see the new files.
		// &filter=restore
		header_redirect( $admin_url.'?ctrl=items&blog='.$blog );	// Will save $Messages

		// Note: we should have EXITED here. In case we don't (error, or sth...)

		// Reset stuff so it doesn't interfere with upcomming display
		unset( $edited_Item );
		unset( $edited_Link );
		$selected_Filelist = new Filelist( $fm_Filelist->get_FileRoot(), false );
		break;

	case 'hide_quick_button':
	case 'show_quick_button':
		// Show/Hide quick button to publish a post

		// Check that this action request is not a CSRF hacked request:
		$Session->assert_received_crumb( 'item' );

		// Update setting:
		$UserSettings->set( 'show_quick_publish_'.$blog, ( $action == 'show_quick_button' ? 1 : 0 ) );
		$UserSettings->dbupdate();

		$prev_action = param( 'prev_action', 'string', '' );
		$item_ID = param( 'p', 'integer', 0 );

		// REDIRECT / EXIT
		header_redirect( $admin_url.'?ctrl=items&action='.$prev_action.( $item_ID > 0 ? '&p='.$item_ID : '' ).'&blog='.$blog );
		break;

	case 'reset_quick_settings':
		// Reset quick default settings for current user on the edit item screen:

		// Check that this action request is not a CSRF hacked request:
		$Session->assert_received_crumb( 'item' );

		// Reset settings to default values
		$DB->query( 'DELETE FROM T_users__usersettings
			WHERE uset_name LIKE "fold_itemform_%_'.$blog.'"
			   OR uset_name = "show_quick_publish_'.$blog.'"' );

		$prev_action = param( 'prev_action', 'string', '' );
		$item_ID = param( 'p', 'integer', 0 );

		// REDIRECT / EXIT
		header_redirect( $admin_url.'?ctrl=items&action='.$prev_action.( $item_ID > 0 ? '&p='.$item_ID : '' ).'&blog='.$blog );
		break;

	case 'create_comments_post':
		// Create new post from selected comments:

		// Check that this action request is not a CSRF hacked request:
		$Session->assert_received_crumb( 'comments' );

		$item_ID = param( 'p', 'integer', 0 );
		$selected_comments = param( 'selected_comments', 'array:integer' );

		if( empty( $selected_comments ) )
		{	// If no comments selected:
			$Messages->add( TB_('Please select at least one comment.'), 'error' );
			// REDIRECT / EXIT:
			header_redirect( $admin_url.'?ctrl=items&blog='.$blog.'&p='.$item_ID.'&comment_type=feedback#comments' );
		}

		// Check perm:
		check_user_perm( 'blog_post_statuses', 'edit', true, $blog );

		$new_post_creation_result = false;
		$CommentCache = & get_CommentCache();
		$moved_comments_IDs = array();
		$reattached_comments_IDs = array();
		foreach( $selected_comments as $s => $selected_comment_ID )
		{
			$selected_Comment = & $CommentCache->get_by_ID( $selected_comment_ID, false, false );
			if( ! $selected_Comment || $selected_Comment->item_ID != $item_ID )
			{	// Skip wrong comment:
				continue;
			}

			$moved_comments_IDs[] = $selected_Comment->ID;

			if( $s == 0 )
			{	// Create post from first comment:
				if( empty( $selected_Comment->author_user_ID ) )
				{	// Don't create a post from comment with anonymous user:
					$Messages->add( TB_('Could not create new post from comment without author.'), 'error' );
					break;
				}

				$comment_Item = & $selected_Comment->get_Item();

				// Use same chapters of the parent Item:
				$comment_item_chapters = $comment_Item->get_Chapters();
				$comment_item_chapters_IDs = array();
				foreach( $comment_item_chapters as $comment_item_Chapter )
				{
					$comment_item_chapters_IDs[] = $comment_item_Chapter->ID;
				}

				$new_Item = new Item();
				$new_Item->set( $new_Item->creator_field, $selected_Comment->author_user_ID );
				$new_Item->set( 'status', $comment_Item->status );
				$new_Item->set( 'main_cat_ID', $comment_Item->main_cat_ID );
				$new_Item->set( 'extra_cat_IDs', $comment_item_chapters_IDs );
				$new_Item->set( 'title', substr( sprintf( TB_('Branched from: %s'), $comment_Item->title ), 0, 255 ) );
				$new_Item->set( 'content', $selected_Comment->content );
				$new_Item->set( 'ityp_ID', $comment_Item->ityp_ID );
				$new_Item->set( 'renderers', $selected_Comment->get_renderers() );
				if( $new_Item->dbinsert() )
				{	// New post creation is success:
					$Messages->add( sprintf( TB_('New post has been created from comment #%d'), $selected_Comment->ID ), 'success' );
					$new_post_creation_result = true;
					// Move all links/attachments from old comment to new created post:
					$DB->query( 'UPDATE T_links
						  SET link_itm_ID = '.$new_Item->ID.', link_cmt_ID = NULL
						WHERE link_cmt_ID = '.$selected_Comment->ID );
					// Delete source comment after creating of new post:
					$selected_Comment->dbdelete( true );
				}
				else
				{	// New post creation is failed:
					$Messages->add( sprintf( TB_('Could not create new post from comment #%d'), $selected_Comment->ID ), 'error' );
					break;
				}
			}
			else
			{	// Append all next comments for new created post which has been created from first comment:
				$selected_Comment->set( 'item_ID', $new_Item->ID );
				// Set proper parent Comment if comment has been not moved to new Item:
				$selected_Comment->set_correct_parent_comment();
				// Update comment with new data:
				if( $selected_Comment->dbupdate() )
				{
					$reattached_comments_IDs[] = $selected_Comment->ID;
				}
			}
		}

		// Set proper parent Comment for all child comments if the parent comment has been moved to other new Item:
		foreach( $moved_comments_IDs as $moved_comments_ID )
		{
			$moved_Comment = & $CommentCache->get_by_ID( $moved_comments_ID );
			$child_comment_IDs = $moved_Comment->get_child_comment_IDs();
			foreach( $child_comment_IDs as $child_comment_ID )
			{
				$child_Comment = & $CommentCache->get_by_ID( $child_comment_ID );
				$old_in_reply_to_cmt_ID = $child_Comment->get( 'in_reply_to_cmt_ID' );
				$child_Comment->set_correct_parent_comment();
				if( $old_in_reply_to_cmt_ID != $child_Comment->get( 'in_reply_to_cmt_ID' ) )
				{	// Update only if parent comment has been really corrected:
					if( $child_Comment->dbupdate() )
					{
						$reattached_comments_IDs[] = $child_Comment->ID;
					}
				}
			}
		}

		if( count( $reattached_comments_IDs ) )
		{	// Display a message about the reattached comments:
			$Messages->add( sprintf( TB_('Comments #%s have been attached to new post.'), implode( ',', $reattached_comments_IDs ) ), 'success' );
		}

		// REDIRECT / EXIT
		header_redirect( $admin_url.'?ctrl=items&blog='.$blog.'&p='.( $new_post_creation_result ? $new_Item->ID : $item_ID.'&comment_type=feedback#comments' ) );
		break;

	case 'items_visibility':
		// Change visibility of selected items:

		// Check that this action request is not a CSRF hacked request:
		$Session->assert_received_crumb( 'items' );

		$selected_items = param( 'selected_items', 'array:integer' );
		$page = param( 'page', 'integer', 1 );
		$tab = param( 'tab', 'string', 'type' );
		$tab_type = param( 'tab_type', 'string', '' );

		// Set an URL to redirect to items list after this action:
		$redirect_to = param( 'redirect_to', 'url', NULL );

		if( empty( $redirect_to ) )
		{
			$redirect_to = $admin_url.'?ctrl=items&blog='.$blog.'&tab='.$tab.( $page > 1 ? '&items_'.$tab.'_paged='.$page : '' );
		}
		if( $tab == 'type' && ! empty( $tab_type ) )
		{
			$redirect_to .= '&tab_type='.$tab_type;
		}

		if( empty( $selected_items ) )
		{	// If no items selected:
			$Messages->add( TB_('Please select at least one item.'), 'error' );
			// REDIRECT / EXIT:
			header_redirect( $redirect_to );
		}

		$item_status = param( 'post_status', 'string' );
		$status_options = get_visibility_statuses();
		$item_status_title = isset( $status_options[ $item_status ] ) ? $status_options[ $item_status ] : $item_status;

		$ItemCache = & get_ItemCache();
		$items_success = 0;
		$items_restricted = array();
		$items_failed = 0;
		foreach( $selected_items as $selected_item_ID )
		{
			if( ( $selected_Item = & $ItemCache->get_by_ID( $selected_item_ID, false, false ) ) &&
			    check_user_perm( 'item_post!CURSTATUS', 'edit', false, $selected_Item ) )
			{	// If current User has a permission to edit the selected Item:
				$selected_Item->set( 'status', $item_status );
				if( $selected_Item->dbupdate() )
				{
					if( $item_status == $selected_Item->get( 'status' ) )
					{	// If the item has been updated to the requested status:
						$items_success++;
					}
					else
					{	// If the item could not be updated to the requested status because of restriction e.g. by post status:
						if( ! isset( $items_restricted[ $selected_Item->get( 'status' ) ] ) )
						{
							$items_restricted[ $selected_Item->get( 'status' ) ] = 0;
						}
						$items_restricted[ $selected_Item->get( 'status' ) ]++;
					}
					continue;
				}
			}
			// Wrong item or current User has no perm to edit the selected item:
			$items_failed++;
		}

		if( $items_success )
		{	// Inform about success updates:
			$Messages->add( sprintf( TB_('Visibility of %d items have been updated to %s.'), $items_success, $item_status_title ), 'success' );
		}
		foreach( $items_restricted as $restricted_status => $restricted_status_num )
		{	// Inform about restricted updates:
			$Messages->add( sprintf( TB_('Visibility of %d items have been restricted to %s.'), $restricted_status_num, isset( $status_options[ $restricted_status ] ) ? $status_options[ $restricted_status ] : $restricted_status ), 'note' );
		}
		if( $items_failed )
		{	// Inform about failed updates:
			$Messages->add( sprintf( TB_('Visibility of %d items could not be updated to %s.'), $items_failed, $item_status_title ), 'error' );
		}

		// REDIRECT / EXIT:
		header_redirect( $redirect_to );
		break;

	case 'mass_delete':
		// Delete selected items:

		// Check that this action request is not a CSRF hacked request:
		$Session->assert_received_crumb( 'items' );

		$selected_items = param( 'selected_items', 'array:integer' );
		$page = param( 'page', 'integer', 1 );
		$tab = param( 'tab', 'string', 'type' );
		$tab_type = param( 'tab_type', 'string', '' );
		$confirm = param( 'confirm', 'integer', 0 );

		// Set an URL to redirect to items list after this action:
		$redirect_to = $admin_url.'?ctrl=items&blog='.$blog.'&tab='.$tab.( $page > 1 ? '&items_'.$tab.'_paged='.$page : '' );
		if( $tab == 'type' && ! empty( $tab_type ) )
		{
			$redirect_to .= '&tab_type='.$tab_type;
		}

		if( empty( $selected_items ) )
		{	// If no items selected:
			$Messages->add( TB_('Please select at least one item.'), 'error' );
			// REDIRECT / EXIT:
			header_redirect( $redirect_to );
		}

		if( $confirm )
		{	// Mass deleting of the selected items after confirmation:
			$ItemCache = & get_ItemCache();
			$items_success = 0;
			$items_restricted = array();
			$items_failed = 0;
			foreach( $selected_items as $selected_item_ID )
			{
				if( ( $selected_Item = & $ItemCache->get_by_ID( $selected_item_ID, false, false ) ) &&
				    check_user_perm( 'item_post!CURSTATUS', 'delete', false, $selected_Item ) &&
				    $selected_Item->dbdelete() )
				{	// If current User has a permission to delete the selected Item:
					$items_success++;
				}
				else
				{	// Wrong item or current User has no perm to delete the selected item:
					$items_failed++;
				}
			}
			if( $items_success )
			{	// Inform about success deletes:
				$Messages->add( sprintf( TB_('%d items have been deleted.'), $items_success ), 'success' );
			}
			if( $items_failed )
			{	// Inform about failed updates:
				$Messages->add( sprintf( TB_('%d items could not be deleted.'), $items_failed ), 'error' );
			}
		}
		else
		{	// Redirect to page for confirmation of mass deleting items:
			$redirect_to .= '&confirm_action=mass_delete&selected_items='.implode( ',', $selected_items );
		}

		// REDIRECT / EXIT:
		header_redirect( $redirect_to );
		break;

	case 'comments_visibility':
		// Set visibility of selected comments:

		// Check that this action request is not a CSRF hacked request:
		$Session->assert_received_crumb( 'comments' );

		$item_ID = param( 'p', 'integer', 0 );
		$selected_comments = param( 'selected_comments', 'array:integer' );
		$page = param( 'page', 'integer', 1 );

		if( $item_ID > 0 )
		{	// Set an URL to redirect to item view details page after this action:
			$redirect_to = $admin_url.'?ctrl=items&blog='.$blog.'&p='.$item_ID.'&comment_type=feedback#comments';
		}
		else
		{	// Set an URL to redirect to comments list after this action:
			$redirect_to = $admin_url.'?ctrl=comments&blog='.$blog.( $page > 1 ? '&cmnt_fullview_paged='.$page : '' );
		}

		if( empty( $selected_comments ) )
		{	// If no comments selected:
			$Messages->add( TB_('Please select at least one comment.'), 'error' );
			// REDIRECT / EXIT:
			header_redirect( $redirect_to );
		}

		$comment_status = param( 'comment_status', 'string' );
		$status_options = get_visibility_statuses();
		$comment_status_title = isset( $status_options[ $comment_status ] ) ? $status_options[ $comment_status ] : $comment_status;

		$CommentCache = & get_CommentCache();
		$comments_success = 0;
		$comments_restricted = array();
		$comments_failed = 0;
		foreach( $selected_comments as $selected_comment_ID )
		{
			if( ( $selected_Comment = & $CommentCache->get_by_ID( $selected_comment_ID, false, false ) ) &&
			    check_user_perm( 'comment!CURSTATUS', 'moderate', false, $selected_Comment ) &&
			    check_user_perm( 'comment!'.$comment_status, 'moderate', false, $selected_Comment ) )
			{	// If current User has a permission to edit the selected Comment:
				$selected_Comment->set( 'status', $comment_status );
				if( $selected_Comment->dbupdate() )
				{
					if( $comment_status == $selected_Comment->get( 'status' ) )
					{	// If the comment has been updated to the requested status:
						$comments_success++;
					}
					else
					{	// If the comment could not be updated to the requested status because of restriction e.g. by post status:
						if( ! isset( $comments_restricted[ $selected_Comment->get( 'status' ) ] ) )
						{
							$comments_restricted[ $selected_Comment->get( 'status' ) ] = 0;
						}
						$comments_restricted[ $selected_Comment->get( 'status' ) ]++;
					}
					continue;
				}
			}
			// Wrong comment or current User has no perm to edit the selected comment:
			$comments_failed++;
		}

		if( $comments_success )
		{	// Inform about success updates:
			$Messages->add( sprintf( TB_('Visibility of %d comments have been updated to %s.'), $comments_success, $comment_status_title ), 'success' );
		}
		foreach( $comments_restricted as $restricted_status => $restricted_status_num )
		{	// Inform about restricted updates:
			$Messages->add( sprintf( TB_('Visibility of %d comments have been restricted to %s.'), $restricted_status_num, isset( $status_options[ $restricted_status ] ) ? $status_options[ $restricted_status ] : $restricted_status ), 'note' );
		}
		if( $comments_failed )
		{	// Inform about failed updates:
			$Messages->add( sprintf( TB_('Visibility of %d comments could not be updated to %s.'), $comments_failed, $comment_status_title ), 'error' );
		}

		// REDIRECT / EXIT:
		header_redirect( $redirect_to );
		break;

	case 'recycle_comments':
	case 'delete_comments':
		// Recycle/Delete the selected comments:

		// Check that this action request is not a CSRF hacked request:
		$Session->assert_received_crumb( 'comments' );

		$item_ID = param( 'p', 'integer', 0 );
		$selected_comments = param( 'selected_comments', 'array:integer' );

		if( $item_ID > 0 )
		{	// Set an URL to redirect to item view details page after this action:
			$redirect_to = $admin_url.'?ctrl=items&blog='.$blog.'&p='.$item_ID.'&comment_type=feedback#comments';
		}
		else
		{	// Set an URL to redirect to comments list after this action:
			$redirect_to = $admin_url.'?ctrl=comments&blog='.$blog;
		}

		if( empty( $selected_comments ) )
		{	// If no comments selected:
			$Messages->add( TB_('Please select at least one comment.'), 'error' );
			// REDIRECT / EXIT:
			header_redirect( $redirect_to );
		}

		// Force a permanent deletion for given action even if comments were not recycled:
		$force_permanent_delete = ( $action == 'delete_comments' );

		$CommentCache = & get_CommentCache();
		$comments_success_recycled = 0;
		$comments_success_deleted = 0;
		$comments_failed_recycled = 0;
		$comments_failed_deleted = 0;
		foreach( $selected_comments as $selected_comment_ID )
		{
			$comment_status = false;
			if( ( $selected_Comment = & $CommentCache->get_by_ID( $selected_comment_ID, false, false ) ) &&
			    check_user_perm( 'comment!CURSTATUS', 'delete', false, $selected_Comment ) )
			{	// If current User has a permission to recycle/delete the selected Comment:
				$comment_status = $selected_Comment->get( 'status' );
				if( $selected_Comment->dbdelete( $force_permanent_delete ) )
				{
					if( $force_permanent_delete || $comment_status == 'trash' )
					{	// If a selected comment has been deleted completely:
						$comments_success_deleted++;
					}
					else
					{	// If a selected comment has been moved to recycle bin:
						$comments_success_recycled++;
					}
					continue;
				}
			}
			// Wrong comment or current User has no perm to delete the selected comment:
			if( $force_permanent_delete || $comment_status == 'trash' )
			{	// If a selected comment has NOT been deleted completely:
				$comments_failed_deleted++;
			}
			else
			{	// If a selected comment has NOT been moved to recycle bin:
				$comments_failed_recycled++;
			}
		}

		if( $comments_success_recycled )
		{	// Inform about success recycling:
			$Messages->add( sprintf( TB_('%d comments have been recycled.'), $comments_success_recycled ), 'success' );
		}
		if( $comments_success_deleted )
		{	// Inform about success deleted:
			$Messages->add( sprintf( TB_('%d comments have been deleted.'), $comments_success_deleted ), 'success' );
		}
		if( $comments_failed_recycled )
		{	// Inform about failed deletions:
			$Messages->add( sprintf( TB_('%d comments could not be recycled.'), $comments_failed_recycled ), 'error' );
		}
		if( $comments_failed_deleted )
		{	// Inform about failed deletions:
			$Messages->add( sprintf( TB_('%d comments could not be deleted.'), $comments_failed_deleted ), 'error' );
		}

		// REDIRECT / EXIT:
		header_redirect( $redirect_to );
		break;

	case 'mass_change_cat':
		// Mass change main category or add extra categories of selected items:

		// Check that this action request is not a CSRF hacked request:
		$Session->assert_received_crumb( 'items' );

		$selected_items = param( 'selected_items', 'array:integer' );
		$cat_type = param( 'cat_type', 'string' );

		// Set an URL to redirect to items list after this action:
		$redirect_to = param( 'redirect_to', 'url', NULL );

		if( empty( $selected_items ) )
		{	// If no items selected:
			$Messages->add( TB_('Please select at least one item.'), 'error' );
			// REDIRECT / EXIT:
			header_redirect( $redirect_to );
		}

		$ChapterCache = & get_ChapterCache();
		if( $cat_type == 'main' )
		{	// Get a selected main category:
			$main_cat_ID = param( 'post_category', 'integer', true );
			$main_Chapter = & $ChapterCache->get_by_ID( $main_cat_ID );
		}
		else
		{	// Get the selected extra categories:
			$extra_categories = param( 'post_extracats', 'array:integer' );
		}

		if( empty( $main_cat_ID ) && empty( $extra_categories ) )
		{	// If no categories selected:
			$Messages->add( TB_('Please select a category.'), 'error' );
			// REDIRECT / EXIT:
			header_redirect( $redirect_to );
		}

		$ItemCache = & get_ItemCache();
		$items_success = 0;
		$items_failed = 0;
		foreach( $selected_items as $selected_item_ID )
		{
			if( ( $selected_Item = & $ItemCache->get_by_ID( $selected_item_ID, false, false ) ) &&
			    check_user_perm( 'item_post!CURSTATUS', 'edit', false, $selected_Item ) )
			{	// If current User has a permission to edit the selected Item:
				$current_extra_categories = postcats_get_byID( $selected_Item->ID );
				if( $cat_type == 'main' )
				{	// Change main category:
					$selected_Item->set( 'main_cat_ID', $main_cat_ID );
					// Don't lose current extra categories:
					$selected_Item->set( 'extra_cat_IDs', $current_extra_categories );
				}
				elseif( $cat_type == 'extra' )
				{	// Add extra categories to previous linked categories:
					$selected_Item->set( 'extra_cat_IDs', array_unique( array_merge( $current_extra_categories, $extra_categories ) ) );
				}
				elseif( $cat_type == 'remove_extra' )
				{	// Remove extra categories from previous linked categories except if an extra category is also the primary category:
					$main_cat_ID = $selected_Item->get( 'main_cat_ID' );
					$remove_extra_categories = $extra_categories;
					if( ( $key = array_search( $main_cat_ID, $remove_extra_categories ) ) !== false )
					{
						unset( $remove_extra_categories[$key] );
					}
					
					if( empty( $remove_extra_categories ) )
					{	// Nothing to remove, skip to next Item:
						continue;
					}

					$selected_Item->set( 'extra_cat_IDs', array_diff( $current_extra_categories, $remove_extra_categories ) );
				}
				if( $selected_Item->dbupdate() )
				{	// If the item has been updated to the requested categories:
					$items_success++;
					continue;
				}
			}
			// Wrong item or current User has no perm to edit the selected item:
			$items_failed++;
		}

		if( $cat_type == 'main' )
		{	// Report about changed main category:
			if( $items_success )
			{	// Inform about success updates:
				$Messages->add( sprintf( TB_('Main category of %d items have been changed to %s.'), $items_success, '"'.$main_Chapter->get( 'name' ).'"' ), 'success' );
			}
			if( $items_failed )
			{	// Inform about failed updates:
				$Messages->add( sprintf( TB_('Main category of %d items could not be changed to %s.'), $items_failed, '"'.$main_Chapter->get( 'name' ).'"' ), 'error' );
			}
		}
		elseif( $cat_type == 'extra' )
		{	// Report about added extra categories:
			$extra_cats_names = array();
			foreach( $extra_categories as $extra_cat_ID )
			{
				if( $extra_Chapter = & $ChapterCache->get_by_ID( $extra_cat_ID, false, false ) )
				{
					$extra_cats_names[] = '"'.$extra_Chapter->get( 'name' ).'"';
				}
			}
			if( $items_success )
			{	// Inform about success updates:
				$Messages->add( sprintf( TB_('Extra categories %s of %d items have been added.'), implode( ', ', $extra_cats_names ), $items_success ), 'success' );
			}
			if( $items_failed )
			{	// Inform about failed updates:
				$Messages->add( sprintf( TB_('Extra categories %s of %d items could not be added.'), implode( ', ', $extra_cats_names ), $items_failed ), 'error' );
			}
		}
		elseif( $cat_type == 'remove_extra' )
		{	// Report about removed extra categories:
			$extra_cats_names = array();
			foreach( $extra_categories as $extra_cat_ID )
			{
				if( $extra_Chapter = & $ChapterCache->get_by_ID( $extra_cat_ID, false, false ) )
				{
					$extra_cats_names[] = '"'.$extra_Chapter->get( 'name' ).'"';
				}
			}
			if( $items_success )
			{	// Inform about success updates:
				$Messages->add( sprintf( TB_('Extra categories %s of %d items have been removed.'), implode( ', ', $extra_cats_names ), $items_success ), 'success' );
			}
			if( $items_failed )
			{	// Inform about failed updates:
				$Messages->add( sprintf( TB_('Extra categories %s of %d items could not be removed.'), implode( ', ', $extra_cats_names ), $items_failed ), 'error' );
			}
		}

		// REDIRECT / EXIT:
		header_redirect( $redirect_to );
		break;

	case 'mass_change_renderer':
		// Mass change renderers of selected items:

		// Check that this action request is not a CSRF hacked request:
		$Session->assert_received_crumb( 'items' );

		$selected_items = param( 'selected_items', 'array:integer' );
		$renderer_change_type = param( 'renderer_change_type', 'string' );

		// Set an URL to redirect to items list after this action:
		$redirect_to = param( 'redirect_to', 'url', NULL );

		if( empty( $selected_items ) )
		{	// If no items selected:
			$Messages->add( TB_('Please select at least one item.'), 'error' );
			// REDIRECT / EXIT:
			header_redirect( $redirect_to );
		}

		// Get the selected renderers:
		$renderers = param( 'renderers', 'array:string' );

		if( empty( $renderers ) )
		{	// If no categories selected:
			$Messages->add( TB_('Please select a renderer.'), 'error' );
			// REDIRECT / EXIT:
			header_redirect( $redirect_to );
		}

		$ItemCache = & get_ItemCache();
		$items_success = 0;
		$items_failed = 0;
		foreach( $selected_items as $selected_item_ID )
		{
			if( ( $selected_Item = & $ItemCache->get_by_ID( $selected_item_ID, false, false ) ) &&
			    check_user_perm( 'item_post!CURSTATUS', 'edit', false, $selected_Item ) )
			{	// If current User has a permission to edit the selected Item:
				if( $renderer_change_type == 'add_renderer' )
				{
					foreach( $renderers as $renderer )
					{
						$selected_Item->add_renderer( $renderer );
					}
				}
				elseif( $renderer_change_type == 'remove_renderer' )
				{
					foreach( $renderers as $renderer )
					{
						$selected_Item->remove_renderer( $renderer );
					}
				}

				// In any case, remove 'default' renderer:
				$selected_Item->remove_renderer( 'default' );
				
				if( $selected_Item->dbupdate() )
				{	// If the item has been updated with the requested renderers:
					$items_success++;
					continue;
				}
			}
			// Wrong item or current User has no perm to edit the selected item:
			$items_failed++;
		}

		global $Plugins;

		if( $renderer_change_type == 'add_renderer' )
		{	// Report about added renderers:
			$renderer_names = array();
			foreach( $renderers as $code )
			{
				if( $renderer_Plugin = & $Plugins->get_by_code( $code	) )
				{
					$renderer_names[] = '"'.$renderer_Plugin->name.'"';
				}
			}
			if( $items_success )
			{	// Inform about success updates:
				$Messages->add( sprintf( TB_('Renderers %s of %d items have been added.'), implode( ', ', $renderer_names ), $items_success ), 'success' );
			}
			if( $items_failed )
			{	// Inform about failed updates:
				$Messages->add( sprintf( TB_('Renderers %s of %d items could not be added.'), implode( ', ', $renderer_names ), $items_failed ), 'error' );
			}
		}
		elseif( $renderer_change_type == 'remove_renderer' )
		{	// Report about removed extra categories:
			$renderer_names = array();
			foreach( $renderers as $code )
			{
				if( $renderer_Plugin = & $Plugins->get_by_code( $code ) )
				{
					$renderer_names[] = '"'.$renderer_Plugin->name.'"';
				}
			}
			if( $items_success )
			{	// Inform about success updates:
				$Messages->add( sprintf( TB_('Renderers %s of %d items have been removed.'), implode( ', ', $renderer_names ), $items_success ), 'success' );
			}
			if( $items_failed )
			{	// Inform about failed updates:
				$Messages->add( sprintf( TB_('Renderers %s of %d items could not be removed.'), implode( ', ', $renderer_names ), $items_failed ), 'error' );
			}
		}

		// REDIRECT / EXIT:
		header_redirect( $redirect_to );
		break;

	default:
		// Try to handle action by modules:
		$module_result = modules_call_method( 'handle_backoffice_action', array(
				'ctrl'        => 'items',
				'action'      => $action,
				'action_type' => 'action1',
			) );
		if( $module_result === NULL )
		{	// Deny wrong action if it is not handled by any module:
			debug_die( 'unhandled action 1:'.htmlspecialchars($action) );
		}
}

$AdminUI->breadcrumbpath_init( true, array( 'text' => TB_('Collections'), 'url' => $admin_url.'?ctrl=collections' ) );
$AdminUI->breadcrumbpath_add( TB_('Posts'), $admin_url.'?ctrl=items&amp;blog=$blog$&amp;tab=full&amp;filter=restore' );

/**
 * Perform action:
 */
switch( $action )
{
	case 'nil':
		// Do nothing
		break;

	case 'new':
	case 'new_mass':
		// $set_issue_date = 'now';
		$item_issue_date = date_i18n( locale_datefmt(), $localtimenow );
		$item_issue_time = date( 'H:i:s', $localtimenow );
		// pre_dump( $item_issue_date, $item_issue_time );
	case 'new_switchtab': // this gets set as action by JS, when we switch tabs
	case 'new_type': // this gets set as action by JS, when we switch tabs
		// New post form  (can be a bookmarklet form if mode == bookmarklet )

		// We don't check the following earlier, because we want the blog switching buttons to be available:
		if( ! blog_has_cats( $blog ) )
		{
			break;
		}

		// Used when we change a type of the duplicated item:
		$duplicated_item_ID = param( 'p', 'integer', NULL );

		if( in_array( $action, array( 'new', 'new_type' ) ) )
		{
			param( 'restore', 'integer', 0 );
			if( $restore || $action == 'new_type' )
			{ // Load post from Session
				$edited_Item = get_session_Item( 0 );

				// Memorize action for list of post types
				memorize_param( 'action', 'string', '', $action );
			}
		}

		load_class( 'items/model/_item.class.php', 'Item' );
		if( empty( $edited_Item ) )
		{ // Create new Item object
			$edited_Item = new Item();
			// Prefill data from url:
			$edited_Item->set( 'title', param( 'post_title', 'string' ) );
			$edited_Item->set( 'urltitle', param( 'post_urltitle', 'string' ) );
		}

		$edited_Item->set('main_cat_ID', $Blog->get_default_cat_ID());

		// We use the request variables to fill the edit form, because we need to be able to pass those values
		// from tab to tab via javascript when the editor wants to switch views...
		// Also used by bookmarklet
		$edited_Item->load_from_Request( true ); // needs Blog set

		// Set default locations from current user
		$edited_Item->set_creator_location( 'country' );
		$edited_Item->set_creator_location( 'region' );
		$edited_Item->set_creator_location( 'subregion' );
		$edited_Item->set_creator_location( 'city' );

		$edited_Item->status = param( 'post_status', 'string', $Blog->get_setting( 'default_post_status' ) );		// 'published' or 'draft' or ...
		// We know we can use at least one status,
		// but we need to make sure the requested/default one is ok:
		$edited_Item->status = $Blog->get_allowed_item_status( $edited_Item->status, $edited_Item );

		// Check if new category was started to create. If yes then set up parameters for next page:
		check_categories_nosave( $post_category, $post_extracats, $edited_Item, ( $action == 'new_switchtab' ? 'frontoffice' : 'backoffice' ) );

		$edited_Item->set ( 'main_cat_ID', $post_category );
		if( $edited_Item->main_cat_ID && ( get_allow_cross_posting() < 2 ) && $edited_Item->get_blog_ID() != $blog )
		{ // the main cat is not in the list of categories; this happens, if the user switches blogs during editing:
			$edited_Item->set('main_cat_ID', $Blog->get_default_cat_ID());
		}
		$post_extracats = param( 'post_extracats', 'array:integer', $post_extracats );
		$edited_Item->set( 'extra_cat_IDs', $post_extracats );

		param( 'item_tags', 'string', '' );

		// Trackback addresses (never saved into item)
		param( 'trackback_url', 'string', '' );

		// Item type ID:
		param( 'item_typ_ID', 'integer', 1 );

		// Initialize a page title depending on item type:
		if( empty( $item_typ_ID ) )
		{	// No selected item type, use default:
			$title = TB_('New post');
		}
		else
		{	// Get item type to set a pge title:
			$ItemTypeCache = & get_ItemTypeCache();
			$ItemType = & $ItemTypeCache->get_by_ID( $item_typ_ID );
			$title = sprintf( TB_('New [%s]'), $ItemType->get_name() );
		}

		$AdminUI->breadcrumbpath_add( $title, '?ctrl=items&amp;action=new&amp;blog='.$Blog->ID.'&amp;item_typ_ID='.$item_typ_ID );

		$AdminUI->title_titlearea = $title.': ';;

		// Params we need for tab switching:
		$tab_switch_params = 'blog='.$blog;

		if( $action == 'new_type' )
		{	// Save the changes of Item to Session:
			set_session_Item( $edited_Item );
			// Initialize original item ID that is used on diplicating action:
			param( 'p', 'integer', NULL );
		}
		break;


	case 'copy': // Duplicate post
	case 'new_version': // Add version
		$item_ID = param( 'p', 'integer', true );
		$ItemCache = &get_ItemCache();
		$edited_Item = & $ItemCache->get_by_ID( $item_ID );

		// Load tags of the duplicated item:
		$item_tags = implode( ', ', $edited_Item->get_tags() );

		// Load all settings of the duplicating item and copy them to new item:
		$edited_Item->load_ItemSettings();
		$edited_Item->ItemSettings->_load( $edited_Item->ID, NULL );
		$edited_Item->ItemSettings->cache[0] = $edited_Item->ItemSettings->cache[ $edited_Item->ID ];

		// Load all custom fields:
		$edited_Item->get_custom_fields_defs();

		// Set parent item ID to find category order
		$edited_Item->set( 'parent_item_ID', $edited_Item->ID );

		// Set ID of copied post to 0, because some functions can update current post, e.g. $edited_Item->get( 'excerpt' )
		$edited_Item->ID = 0;

		// Change creator user to current user for correct permission checking:
		$edited_Item->set( 'creator_user_ID', $current_User->ID );

		$edited_Item->load_Blog();
		$item_status = $edited_Item->Blog->get_allowed_item_status();

		$edited_Item->set( 'status', $item_status );
		$edited_Item->set( 'dateset', 0 );	// Date not explicitly set yet
		$edited_Item->set( 'issue_date', date( 'Y-m-d H:i:s', $localtimenow ) );

		if( $action == 'new_version' )
		{	// Creating new version
			if( param( 'post_locale', 'string', NULL ) !== NULL )
			{	// Set locale:
				$edited_Item->set_from_Request( 'locale' );
			}

			if( param( 'post_create_child', 'integer', NULL ) === 1 )
			{	// Set parent Item:
				$edited_Item->set( 'parent_ID', $item_ID );
			}

			// Duplicate same images depending on setting from modal window:
			$duplicate_same_images = param( 'post_same_images', 'integer', NULL );

			if( param( 'post_coll_ID', 'integer', NULL ) !== NULL &&
			    $post_coll_ID != $edited_Item->get_blog_ID() )
			{	// Create Item in different collection:
				$BlogCache = & get_BlogCache();
				$linked_Blog = $BlogCache->get_by_ID( $post_coll_ID, false, false );
				if( ! check_user_perm( 'blog_post_statuses', 'edit', false, $post_coll_ID ) )
				{	// If current User cannot create an Item in the selected locale collection,
					// Redirect back to edit Item form:
					$Messages->add( sprintf( TB_('You don\'t have a permission to create new Item in the collection "%s"!'), $linked_Blog ? $linked_Blog->get( 'name' ) : '#'.$post_coll_ID ) );
					header_redirect( $admin_url.'?ctrl=items&blog='.$edited_Item->get_blog_ID().'&action=edit&p='.$p );
					// Exit here.
				}
				// Reset Item collection to new selected:
				set_working_blog( $post_coll_ID );
				unset( $edited_Item->Blog );
				// Set default category in different selected collection:
				$edited_Item->set( 'main_cat_ID', $linked_Blog->get_default_cat_ID() );
				$post_extracats = array( $edited_Item->get( 'main_cat_ID' ) );
			}

			if( param( 'item_typ_ID', 'integer', NULL ) !== NULL )
			{	// Set Item Type:
				if( ! $edited_Item->get_Blog()->is_item_type_enabled( $item_typ_ID ) )
				{	// Use default Item Type if it is NOT enabled for the selected collection:
					$default_ItemType = & $edited_Item->get_Blog()->get_default_new_ItemType();
					$item_typ_ID = $default_ItemType->ID;
				}
				$edited_Item->set( 'ityp_ID', $item_typ_ID );
			}
		}
		else
		{	// Always duplicate images on action=copy:
			$duplicate_same_images = true;
		}

		// Set post comment status and extracats
		$post_comment_status = $edited_Item->get( 'comment_status' );
		if( ! isset( $post_extracats ) )
		{
			$post_extracats = postcats_get_byID( $p );
		}

		// Check if new category was started to create. If yes then set up parameters for next page:
		check_categories_nosave( $post_category, $post_extracats, $edited_Item );

		if( $duplicate_same_images )
		{	// Duplicate attachments from source Item:
			$edited_Item->duplicate_attachments( $item_ID );
		}

		// Initialize a page title depending on item type:
		$ItemTypeCache = & get_ItemTypeCache();
		$ItemType = & $ItemTypeCache->get_by_ID( $edited_Item->ityp_ID );
		$title = sprintf( TB_('Duplicate %s'), $ItemType->get_name() );

		$AdminUI->breadcrumbpath_add( $title, '?ctrl=items&amp;action=copy&amp;blog='.$Blog->ID.'&amp;p='.$edited_Item->ID );

		$AdminUI->title_titlearea = $title.': ';

		// Params we need for tab switching:
		$tab_switch_params = 'blog='.$blog;
		break;


	case 'edit_switchtab': // this gets set as action by JS, when we switch tabs
	case 'edit_type': // this gets set as action by JS, when we switch tabs
		// This is somewhat in between new and edit...

		// Check permission based on DB status:
		check_user_perm( 'item_post!CURSTATUS', 'edit', true, $edited_Item );

		$edited_Item->status = param( 'post_status', 'string', NULL );		// 'published' or 'draft' or ...
		// We know we can use at least one status,
		// but we need to make sure the requested/default one is ok:
		$edited_Item->status = $Blog->get_allowed_item_status( $edited_Item->status, $edited_Item );

		param( 'load_from_request', 'integer', 1 );
		param( 'from_tab', 'string', NULL );

		if( $load_from_request && !( $action == 'edit_type' && $from_tab == 'type' ) )
		{	// We use the request variables to fill the edit form, because we need to be able to pass those values
			// from tab to tab via javascript when the editor wants to switch views...
			// ...except when we are changing the Item Type from a post list where no request variables are available for the Item
			$edited_Item->load_from_Request( true ); // needs Blog set
		}

		// Check if new category was started to create. If yes then set up parameters for next page:
		check_categories_nosave( $post_category, $post_extracats,$edited_Item, ( $action == 'edit_switchtab' ? 'frontoffice' : 'backoffice' ) );

		$edited_Item->set( 'main_cat_ID', $post_category );
		if( $edited_Item->main_cat_ID && ( get_allow_cross_posting() < 2 ) && $edited_Item->get_blog_ID() != $blog )
		{ // the main cat is not in the list of categories; this happens, if the user switches blogs during editing:
			$edited_Item->set('main_cat_ID', $Blog->get_default_cat_ID());
		}
		$post_extracats = param( 'post_extracats', 'array:integer', $post_extracats );
		$edited_Item->set( 'extra_cat_IDs', $post_extracats );

		param( 'item_tags', 'string', '' );

		// Trackback addresses (never saved into item)
		param( 'trackback_url', 'string', '' );

		// Page title:
		$AdminUI->title_titlearea = sprintf( TB_('Editing post #%d: %s'), $edited_Item->ID, $Blog->get('name') );

		$AdminUI->breadcrumbpath_add( sprintf( /* TRANS: noun */ TB_('Post').' #%s', $edited_Item->ID ), '?ctrl=items&amp;blog='.$Blog->ID.'&amp;p='.$edited_Item->ID );
		$AdminUI->breadcrumbpath_add( TB_('Edit'), '?ctrl=items&amp;action=edit&amp;blog='.$Blog->ID.'&amp;p='.$edited_Item->ID );

		// Params we need for tab switching:
		$tab_switch_params = 'p='.$edited_Item->ID;

		if( $action == 'edit_type' )
		{ // Save the changes of Item to Session
			set_session_Item( $edited_Item );
		}
		break;

	case 'history':
		// Check permission:
		check_user_perm( 'item_post!CURSTATUS', 'edit', true, $edited_Item );
		break;

	case 'history_lastseen':
		// Check permission:
		check_user_perm( 'item_post!CURSTATUS', 'edit', true, $edited_Item );

		$SQL = new SQL( 'Find last not seen revision of the Item #'.$edited_Item->ID.' by current User' );
		$SQL->SELECT( 'iver_ID' );
		$SQL->FROM( 'T_items__version' );
		$SQL->WHERE( 'iver_itm_ID = '.$DB->quote( $edited_Item->ID ) );
		$SQL->WHERE_and( 'iver_type = "archived"' );
		$SQL->WHERE_and( 'iver_edit_last_touched_ts <= '.$DB->quote( $edited_Item->get_user_data( 'item_date' ) ) );
		$SQL->ORDER_BY( 'iver_edit_last_touched_ts DESC' );
		$SQL->LIMIT( '1' );
		$lastnotseen_revision_ID = $DB->get_var( $SQL );

		if( $lastnotseen_revision_ID > 0 && $edited_Item->get_user_data( 'item_date' ) < $edited_Item->get( 'last_touched_ts' ) )
		{	// Redirect to compare last not seen revision with current version:
			header_redirect( $admin_url.'?ctrl=items&action=history_compare&p='.$edited_Item->ID.'&r1=a'.$lastnotseen_revision_ID.'&r2=c' );
		}
		else
		{	// Redirect to view current version because User have already seen all changes before:
			$Messages->add( TB_('You have already seen all changes of this Item.'), 'note' );
			header_redirect( $admin_url.'?ctrl=items&action=history_details&p='.$edited_Item->ID.'&r=c' );
		}
		break;

	case 'history_details':
		// Check permission:
		check_user_perm( 'item_post!CURSTATUS', 'edit', true, $edited_Item );

		$Revision = $edited_Item->get_revision( param( 'r', 'string' ) );

		if( ! $Revision )
		{	// Redirect to history list on wrong requested revision:
			if( substr( get_param( 'r' ), 0, 1 ) == 'p' )
			{	// When view old(not existing) proposed change:
				$Messages->add( TB_('The changes have already been accepted or rejected.'), 'error' );
			}
			else
			{	// When view archived version:
				// Don't translate because it should not happens on normal work:
				$Messages->add( 'The requested version does not exist.', 'error' );
			}
			header_redirect( $admin_url.'?ctrl=items&action=history&p='.$edited_Item->ID );
		}
		break;

	case 'history_compare':
		// Check permission:
		if( ! check_user_perm( 'item_post!CURSTATUS', 'edit', false, $edited_Item ) )
		{
			$Messages->add( TB_('You have no permission to view history for this item.'), 'error' );
			header_redirect( $admin_url );
		}

		$Revision_1 = $edited_Item->get_revision( param( 'r1', 'string' ) );
		$Revision_2 = $edited_Item->get_revision( param( 'r2', 'string' ) );

		if( ! $Revision_1 || ! $Revision_2 )
		{	// Redirect to history list on wrong requested revision:
			if( substr( get_param( 'r1' ), 0, 1 ) == 'c' && substr( get_param( 'r2' ), 0, 1 ) == 'p' )
			{	// When compare current version with old(not existing) proposed change(e.g. on opening url from old email message):
				$Messages->add( TB_('The changes have already been accepted or rejected.'), 'error' );
			}
			else
			{	// When compare all other cases:
				// Don't translate because it should not happens on normal work:
				$Messages->add( 'The requested version does not exist.', 'error' );
			}
			header_redirect( $admin_url.'?ctrl=items&action=history&p='.$edited_Item->ID );
		}

		load_class( '_core/model/_diff.class.php', 'Diff' );

		// Compare the titles of two revisions:
		$revisions_difference_title = new Diff( explode( "\n", $Revision_1->iver_title ), explode( "\n", $Revision_2->iver_title ) );
		$TitleDiffFormatter = new TitleDiffFormatter();
		$revisions_difference_title = $TitleDiffFormatter->format( $revisions_difference_title );

		// Compare the contents of two revisions:
		$revisions_difference_content = new Diff( explode( "\n", $Revision_1->iver_content ), explode( "\n", $Revision_2->iver_content ) );
		$TableDiffFormatter = new TableDiffFormatter();
		$revisions_difference_content = $TableDiffFormatter->format( $revisions_difference_content );

		// Compare the custom fields of two revisions:
		$oneline_TableDiffFormatter = new TableDiffFormatter();
		$oneline_TableDiffFormatter->block_header = '';
		// Switch to 1st revision:
		$edited_Item->set( 'revision', get_param( 'r1' ) );
		$custom_fields = $edited_Item->get_type_custom_fields();
		$r1_custom_fields = array();
		foreach( $custom_fields as $custom_field )
		{
			$r1_custom_fields[ $custom_field['name'] ] = $edited_Item->get_custom_field_value( $custom_field['name'] );
		}
		// Switch to 2nd revision:
		$edited_Item->set( 'revision', get_param( 'r2' ) );
		$r2_custom_fields = $edited_Item->get_type_custom_fields();
		foreach( $r2_custom_fields as $r2_custom_field )
		{
			if( ! isset( $custom_fields[ $r2_custom_field['name'] ] ) )
			{	// Append custom fields which don't exist in 1st revision but exist in 2nd revision:
				$custom_fields[ $r2_custom_field['name'] ] = $r2_custom_field;
			}
		}
		$revisions_difference_custom_fields = empty( $custom_fields ) ? false : array(); // FALSE means Item has no custom fields
		foreach( $custom_fields as $custom_field )
		{
			// Get custom field values of both revisions:
			$r1_custom_field_value = isset( $r1_custom_fields[ $custom_field['name'] ] ) ? $r1_custom_fields[ $custom_field['name'] ] : false;
			$r2_custom_field_value = $edited_Item->get_custom_field_value( $custom_field['name'], false, false );
			$revisions_difference_custom_field = '';
			if( $r1_custom_field_value != $r2_custom_field_value )
			{	// If values are different:
				if( $r1_custom_field_value !== false &&
				    $r2_custom_field_value !== false )
				{	// Compare custom field values of 1st and 2nd revisions if they are used for both revisions:
					$revisions_difference_custom_field = new Diff(
							explode( "\n", $r1_custom_field_value ),
							explode( "\n", $r2_custom_field_value )
						);
					if( $custom_field['type'] == 'html' || $custom_field['type'] == 'text' )
					{	// Display a line number for custom fields with multiple lines:
						$revisions_difference_custom_field = $TableDiffFormatter->format( $revisions_difference_custom_field );
					}
					else
					{	// Don't display a line number for custom fields with single line:
						$revisions_difference_custom_field = $oneline_TableDiffFormatter->format( $revisions_difference_custom_field );
					}
				}
				elseif( $r1_custom_field_value !== false ||
				        $r2_custom_field_value !== false)
				{	// Don't compare custom field values if at least one is not used in revision but however they are different:
					$revisions_difference_custom_field = NULL;
				}
			}

			if( $revisions_difference_custom_field !== '' )
			{	// Display custom fields only with differences:
				$revision_custom_fields = array(
						'r1_label' => $custom_field['label'],
						'r2_label' => isset( $r2_custom_fields[ $custom_field['name'] ] ) ? $r2_custom_fields[ $custom_field['name'] ]['label'] : $custom_field['label'],
					);
				if( $revisions_difference_custom_field === NULL )
				{	// Store field value instead of difference if one field is not used in some revision:
					if( $r1_custom_field_value !== false )
					{	// If only old revision has the field:
						$revision_custom_fields['r1_value'] = $r1_custom_field_value;
					}
					else
					{	// If only new revision has the field:
						$revision_custom_fields['r2_value'] = $r2_custom_field_value;
					}
				}
				else
				{	// Store a difference if field is used in both revisions:
					$revision_custom_fields['diff_value'] = $revisions_difference_custom_field;
				}
				if( $revision_custom_fields['r1_label'] != $revision_custom_fields['r2_label'] )
				{	// Compare the labels of custom fields:
					$revisions_difference_label = new Diff( explode( "\n", $revision_custom_fields['r1_label'].':' ), explode( "\n", $revision_custom_fields['r2_label'].':' ) );
					$TitleDiffFormatter = new TitleDiffFormatter();
					$revisions_difference_label = $TitleDiffFormatter->format( $revisions_difference_label );
					if( ! empty( $revisions_difference_label ) )
					{
						$revision_custom_fields['diff_label'] = preg_replace( '/^<tr/', '<tr class="diff-custom-field"', $revisions_difference_label );
					}
				}
				$revisions_difference_custom_fields[] = $revision_custom_fields;
			}
		}

		// Compare the links of two revisions:
		$LinkOwner = new LinkItem( $edited_Item );
		// Switch to 1st revision:
		$edited_Item->set( 'revision', get_param( 'r1' ) );
		$revisions_difference_links = false; // FALSE means Item has no attached files
		if( $r1_LinkList = $LinkOwner->get_attachment_LinkList() )
		{
			$revisions_difference_links = array();
			while( $r1_Link = & $r1_LinkList->get_next() )
			{
				$revisions_difference_links[ $r1_Link->ID ]['r1'] = array(
						'icon'     => $r1_Link->get_preview_thumb(),
						'path'     => ( $r1_link_File = & $r1_Link->get_File() ? $r1_link_File->get_view_link() : false ),
						'order'    => $r1_Link->get( 'order' ),
						'position' => $r1_Link->get( 'position' ),
						'file_ID'  => $r1_Link->get( 'file_ID' ),
					);
			}
		}
		// Switch to 2nd revision:
		$edited_Item->set( 'revision', get_param( 'r2' ) );
		if( $r2_LinkList = $LinkOwner->get_attachment_LinkList() )
		{
			if( ! is_array( $revisions_difference_links ) )
			{
				$revisions_difference_links = array();
			}
			while( $r2_Link = & $r2_LinkList->get_next() )
			{
				$r_link_data = array(
						'icon'     => $r2_Link->get_preview_thumb(),
						'path'     => ( $r2_link_File = & $r2_Link->get_File() ? $r2_link_File->get_view_link() : false ),
						'order'    => $r2_Link->get( 'order' ),
						'position' => $r2_Link->get( 'position' ),
						'file_ID'  => $r2_Link->get( 'file_ID' ),
					);
				if( isset( $revisions_difference_links[ $r2_Link->ID ]['r1'] ) && $revisions_difference_links[ $r2_Link->ID ]['r1'] == $r_link_data )
				{	// The links/attachments of both revisions are equal:
					unset( $revisions_difference_links[ $r2_Link->ID ] );
				}
				else
				{	// The links/attachments of both revisions have at least one difference:
					$revisions_difference_links[ $r2_Link->ID ]['r2'] = $r_link_data;
				}
			}
		}

		// Clear revision in order to display current data on the form:
		$edited_Item->clear_revision();
		break;

	case 'history_restore':
		// Check that this action request is not a CSRF hacked request:
		$Session->assert_received_crumb( 'item' );

		// Check permission:
		check_user_perm( 'item_post!CURSTATUS', 'edit', true, $edited_Item );

		param( 'r', 'integer', 0 );

		if( $r > 0 && $edited_Item->update_from_revision( $r ) )
		{	// Update item only from revisions ($r == 0 for current version):
			$Messages->add( sprintf( TB_('Item has been restored from revision #%s'), $r ), 'success' );
		}

		header_redirect( regenerate_url( 'action', 'action=history', '', '&' ) );
		break;

	case 'edit':
		// Check permission:
		check_user_perm( 'item_post!CURSTATUS', 'edit', true, $edited_Item );

		// Restrict Item status by Collection access restriction AND by CURRENT USER write perm:
		$edited_Item->restrict_status();

		$post_comment_status = $edited_Item->get( 'comment_status' );
		$post_extracats = postcats_get_byID( $p ); // NOTE: dh> using $edited_Item->get_Chapters here instead fails (empty list, since no postIDlist).

		$item_tags = implode( ', ', $edited_Item->get_tags() );
		$trackback_url = '';

		// Page title:
		$AdminUI->title_titlearea = sprintf( TB_('Editing post #%d: %s'), $edited_Item->ID, $Blog->get('name') );

		$AdminUI->breadcrumbpath_add( sprintf( /* TRANS: noun */ TB_('Post').' #%s', $edited_Item->ID ), '?ctrl=items&amp;blog='.$Blog->ID.'&amp;p='.$edited_Item->ID );
		$AdminUI->breadcrumbpath_add( TB_('Edit'), '?ctrl=items&amp;action=edit&amp;blog='.$Blog->ID.'&amp;p='.$edited_Item->ID );

		// Params we need for tab switching:
		$tab_switch_params = 'p='.$edited_Item->ID;
		break;

	case 'propose':
		// Check permission:
		check_user_perm( 'blog_item_propose', 'edit', true, $Blog->ID );

		$AdminUI->breadcrumbpath_add( sprintf( /* TRANS: noun */ TB_('Post').' #%s', $edited_Item->ID ), '?ctrl=items&amp;blog='.$Blog->ID.'&amp;p='.$edited_Item->ID );
		$AdminUI->breadcrumbpath_add( TB_('Propose change'), '?ctrl=items&amp;action=propose&amp;blog='.$Blog->ID.'&amp;p='.$edited_Item->ID );
		break;


	case 'create_edit':
	case 'create_link':
	case 'create':
	case 'create_publish':
		// Check that this action request is not a CSRF hacked request:
		$Session->assert_received_crumb( 'item' );

		// Get params to skip/force/mark notifications and pings:
		if( check_user_perm( 'blog_edit_ts', 'edit', false, $Blog->ID ) )
		{	// If user has a permission to edit advanced properties of items:
			param( 'item_members_notified', 'string', NULL );
			param( 'item_community_notified', 'string', NULL );
			param( 'item_pings_sent', 'string', NULL );
		}
		else
		{	// Use auto mode for notifications depending on the edited Item status:
			$item_members_notified = false;
			$item_community_notified = false;
			$item_pings_sent = false;
		}

		// We need early decoding of these in order to check permissions:
		param( 'post_status', 'string', 'published' );

		if( $action == 'create_publish' )
		{ // load publish status from param, because a post can be published to many status
			$post_status = load_publish_status( true );
		}

		// Check if new category was started to create. If yes check if it is valid:
		check_categories( $post_category, $post_extracats );

		// Check if allowed to cross post.
		check_cross_posting( $post_category, $post_extracats );

		// Check permission on statuses:
		check_user_perm( 'cats_post!'.$post_status, 'create', true, $post_extracats );

		// Get requested Post Type:
		$item_typ_ID = param( 'item_typ_ID', 'integer', true /* require input */ );
		// Check permission on post type: (also verifies that post type is enabled and NOT reserved)
		$valid_item_type = check_perm_posttype( $item_typ_ID, $post_extracats, false );

		// Update the folding positions for current user
		save_fieldset_folding_values( $Blog->ID );
		
		// Update the active tab pane for current user
		save_active_tab_pane_value( $Blog->ID );

		// CREATE NEW POST:
		load_class( 'items/model/_item.class.php', 'Item' );
		$edited_Item = new Item();

		// Set the params we already got:
		$edited_Item->set( 'status', $post_status );
		$edited_Item->set( 'main_cat_ID', $post_category );
		$edited_Item->set( 'extra_cat_IDs', $post_extracats );

		// Set object params:
		$edited_Item->load_from_Request( /* editing? */ ($action == 'create_edit' || $action == 'create_link'), /* creating? */ true );

		$Plugins->trigger_event ( 'AdminBeforeItemEditCreate', array ('Item' => & $edited_Item ) );

		// Validate first enabled captcha plugin:
		$Plugins->trigger_event_first_return( 'ValidateCaptcha', array( 'form_type' => 'item' ) );

		if( !empty( $mass_create ) )
		{	// ------ MASS CREATE ------
			$Items = & create_multiple_posts( $edited_Item, param( 'paragraphs_linebreak', 'boolean', 0 ) );
			if( empty( $Items ) )
			{
				param_error( 'content', TB_( 'Content must not be empty.' ) );
			}
		}

		$result = !$Messages->has_errors();

		if( $result )
		{ // There are no validation errors
			if( isset( $Items ) && !empty( $Items ) )
			{	// We can create multiple posts from single post
				foreach( $Items as $edited_Item )
				{	// INSERT NEW POST INTO DB:
					$result = $edited_Item->dbinsert();
				}
			}
			else
			{	// INSERT NEW POST INTO DB:
				$result = $edited_Item->dbinsert();
			}
			if( !$result )
			{ // Add error message
				$Messages->add( TB_('Couldn\'t create the new post'), 'error' );
			}
		}

		if( $result && $action == 'create_link' )
		{	// If the item has been inserted correctly and we should copy all links from the duplicated item:
			if( check_user_perm( 'item_post!CURSTATUS', 'edit', false, $edited_Item )
			    && check_user_perm( 'files', 'view', false ) )
			{	// Allow this action only if current user has a permission to view the links of new created item:
				$original_item_ID = param( 'p', 'integer', NULL );
				$ItemCache = & get_ItemCache();
				if( $original_Item = & $ItemCache->get_by_ID( $original_item_ID, false, false ) )
				{	// Copy the links only if the requested item is correct:
					if( check_user_perm( 'item_post!CURSTATUS', 'view', false, $original_Item ) )
					{	// Current user must has a permission to view an original item
						$DB->query( 'INSERT INTO T_links ( link_datecreated, link_datemodified, link_creator_user_ID,
								link_lastedit_user_ID, link_itm_ID, link_file_ID, link_position, link_order )
							SELECT '.$DB->quote( date2mysql( $localtimenow ) ).', '.$DB->quote( date2mysql( $localtimenow ) ).', '.$current_User->ID.',
								'.$current_User->ID.', '.$edited_Item->ID.', link_file_ID, link_position, link_order
									FROM T_links
									WHERE link_itm_ID = '.$original_Item->ID );
					}
					else
					{	// Display error if user tries to duplicate the disallowed item:
						$Messages->add( TB_('You have no permission to duplicate the original post.'), 'error' );
						$result = false;
					}
				}
			}
		}

		if( !$result )
		{ // could not insert the new post ( validation errors or unsuccessful db insert )
			if( !empty( $mass_create ) )
			{
				$action = 'new_mass';
			}
			// Params we need for tab switching:
			$tab_switch_params = 'blog='.$blog;
			break;
		}

		// post post-publishing operations:
		param( 'trackback_url', 'string' );
		if( !empty( $trackback_url ) )
		{
			if( $edited_Item->status != 'published' )
			{
				$Messages->add( TB_('Post not publicly published: skipping trackback...'), 'note' );
			}
			else
			{ // trackback now:
				load_funcs('comments/_trackback.funcs.php');
				trackbacks( $trackback_url, $edited_Item );
			}
		}

		// Execute or schedule notifications & pings:
		$edited_Item->handle_notifications( NULL, true, $item_members_notified, $item_community_notified, $item_pings_sent );

		$Messages->add( TB_('Post has been created.'), 'success' );

		// Delete Item from Session
		delete_session_Item( 0 );

		if( ! $exit_after_save && check_user_perm( 'item_post!CURSTATUS', 'edit', false, $edited_Item ) )
		{	// We want to continue editing...
			$tab_switch_params = 'p='.$edited_Item->ID;
			$action = 'edit';	// It's basically as if we had updated
			break;
		}

		// We want to highlight the edited object on next list display:
		$Session->set( 'fadeout_array', array( 'item-'.$edited_Item->ID ) );

		if( ! $valid_item_type )
		{	// Item Type is not enabled for this collection, we will redirect to item type selection to allow user to change it t a valid one:
			$Messages->add( sprintf( TB_('You just edited an Item of Type "%s" which is not valid for this collection. Please select a new Item type below...'), $edited_Item->get( 't_type' ) ), 'warning' );

			// load_from_request param set to 0 will prevent loading of Item data from request because we are not passing any item data from request!
			$redirect_to = url_add_param( $admin_url, 'ctrl=items&action=edit_type&post_ID='.$edited_Item->ID.'&load_from_request=0' );
		}
		elseif( $edited_Item->status != 'redirected' &&
		    ! strpos( $redirect_to, 'tab=tracker' ) &&
		    ! strpos( $redirect_to, 'tab=manual' ) )
		{	// Where to go after creating the post?
			// yura> When a post is created from "workflow" or "manual" we should display a post list

			// Where do we want to go after creating?
			if( $edited_Item->Blog->get_setting( 'enable_goto_blog' ) == 'blog' && $edited_Item->can_be_displayed() )
			{	// Redirect to collection home page ONLY if current user can view it:
				$edited_Item->load_Blog();
				$redirect_to = $edited_Item->Blog->gen_blogurl();
			}
			elseif( $edited_Item->Blog->get_setting( 'enable_goto_blog' ) == 'post' && $edited_Item->can_be_displayed() )
			{	// Redirect to item page to view new created item ONLY if current user can view it:
				$redirect_to = $edited_Item->get_permanent_url();
			}
			else// 'no'
			{	// redirect to posts list:
				$redirect_to = NULL;
				// header_redirect( regenerate_url( '', '&highlight='.$edited_Item->ID, '', '&' ) );
			}
		}

		if( $redirect_to !== NULL )
		{	// The redirect url was NOT set to NULL ( $blog_redirect_setting == 'no' )

			// TRY TO REDIRECT / EXIT
			header_redirect( $redirect_to, 303, false, true /* RETURN if forbidden */ );

			// If we have not Exited yet, it means the redirect was refused because it was on a different domain
		}

		// Set highlight
		$Session->set( 'highlight_id', $edited_Item->ID );

		// REDIRECT / EXIT:
		header_redirect( regenerate_url( '', '&highlight='.$edited_Item->ID, '', '&' ) );
		/* EXITED */
		break;

	case 'link_version':
		// Link the edited Post with the selected Post:

		// Check that this action request is not a CSRF hacked request:
		$Session->assert_received_crumb( 'item' );

		// Check edit permission:
		check_user_perm( 'item_post!CURSTATUS', 'edit', true, $edited_Item );

		param( 'dest_post_ID', 'integer', true );

		$ItemCache = & get_ItemCache();
		if( $dest_Item = & $ItemCache->get_by_ID( $dest_post_ID, false, false ) )
		{	// Do the linking:
			$dest_Item->set_group_ID( $edited_Item->ID );
			$dest_Item->dbupdate();

			// Remember what last collection was used for linking in order to display it by default on next linking:
			$UserSettings->set( 'last_linked_coll_ID', $dest_Item->get_blog_ID() );
			$UserSettings->dbupdate();

			// Inform user about duplicated locale in the same group:
			$other_version_items = $dest_Item->get_other_version_items();
			foreach( $other_version_items as $other_version_Item )
			{
				if( $dest_Item->get( 'locale' ) == $other_version_Item->get( 'locale' ) )
				{	// This is a duplicate locale
					$Messages->add( sprintf( TB_('WARNING: several versions of this Item use the same locale %s.'), '<code>'.$dest_Item->get( 'locale' ).'</code>' ), 'warning' );
					break;
				}
			}

			// Display result message after redirect:
			$Messages->add( sprintf( TB_('The Item "%s" (%s) has been linked to the current Item.'), $dest_Item->get( 'title' ), $dest_Item->get( 'locale' ) ), 'success' );
		}

		// REDIRECT / EXIT:
		header_redirect( $edited_Item->get_edit_url( array( 'glue' => '&' ) ) );
		/* EXITED */
		break;

	case 'unlink_version':
		// Unlink the selected Post from the edited Post:

		// Check that this action request is not a CSRF hacked request:
		$Session->assert_received_crumb( 'item' );

		// Check edit permission:
		check_user_perm( 'item_post!CURSTATUS', 'edit', true, $edited_Item );

		param( 'unlink_item_ID', 'integer', true );

		$ItemCache = & get_ItemCache();
		if( $unlink_Item = & $ItemCache->get_by_ID( $unlink_item_ID, false, false ) )
		{	// Do the unlinking:
			$unlink_Item->set( 'igrp_ID', NULL, true );
			$unlink_Item->dbupdate();

			// Display result message after redirect:
			$Messages->add( sprintf( TB_('The Item %s (%s) has been unlinked from the current Item.'), $unlink_Item->get( 'title' ), $unlink_Item->get( 'locale' ) ), 'success' );
		}

		// REDIRECT / EXIT:
		header_redirect( $edited_Item->get_edit_url( array( 'glue' => '&' ) ) );
		/* EXITED */
		break;

	case 'update_edit':
	case 'update':
	case 'update_publish':
	case 'extract_tags':
		// Check that this action request is not a CSRF hacked request:
		$Session->assert_received_crumb( 'item' );

		// Check edit permission:
		check_user_perm( 'item_post!CURSTATUS', 'edit', true, $edited_Item );

		// Update the folding positions for current user
		save_fieldset_folding_values( $Blog->ID );
		
		// Update the active tab pane for current user
		save_active_tab_pane_value( $Blog->ID );

		// Get params to skip/force/mark notifications and pings:
		if( check_user_perm( 'blog_edit_ts', 'edit', false, $Blog->ID ) )
		{	// If user has a permission to edit advanced properties of items:
			param( 'item_members_notified', 'string', NULL );
			param( 'item_community_notified', 'string', NULL );
			param( 'item_pings_sent', 'string', NULL );
		}
		else
		{	// Use auto mode for notifications depending on the edited Item status:
			$item_members_notified = false;
			$item_community_notified = false;
			$item_pings_sent = false;
		}

		// We need early decoding of these in order to check permissions:
		param( 'post_status', 'string', 'published' );

		if( $action == 'update_publish' )
		{ // load publish status from param, because a post can be published to many status
			$post_status = load_publish_status();
		}

		// Check if new category was started to create.  If yes check if it is valid:
		$isset_category = check_categories( $post_category, $post_extracats, $edited_Item );

		// Check if allowed to cross post.
		if( ! check_cross_posting( $post_category, $post_extracats, $edited_Item->main_cat_ID ) )
		{
			break;
		}

		// Get requested Post Type:
		$item_typ_ID = param( 'item_typ_ID', 'integer', true /* require input */ );
		// Check permission on post type: (also verifies that post type is enabled and NOT reserved)
		$valid_item_type = check_perm_posttype( $item_typ_ID, $post_extracats, false );

		// UPDATE POST:
		// Set the params we already got:
		$edited_Item->set( 'status', $post_status );

		if( $isset_category )
		{ // we change the categories only if the check was succesful

			// get current extra_cats that are in collections where current user is not a coll admin
			$ChapterCache = & get_ChapterCache();

			$prev_extra_cat_IDs = postcats_get_byID( $edited_Item->ID );
			$off_limit_cats = array();
			$r = array();

			foreach( $prev_extra_cat_IDs as $cat )
			{
				$cat_blog = get_catblog( $cat );
				if( ! check_user_perm( 'blog_admin', '', false, $cat_blog ) )
				{
					$Chapter = $ChapterCache->get_by_ID( $cat );
					$off_limit_cats[$cat] = $Chapter;
					$r[] = '<a href="'.$Chapter->get_permanent_url().'">'.$Chapter->dget( 'name' ).'</a>';
				}
			}

			if( $off_limit_cats )
			{
				$Messages->add( sprintf( TB_('Please note: this item is also cross-posted to the following other categories/collections: %s'),
						implode( ', ', $r ) ), 'note' );
			}

			$post_extracats = array_unique( array_merge( $post_extracats,array_keys( $off_limit_cats ) ) );

			$edited_Item->set( 'main_cat_ID', $post_category );
			$edited_Item->set( 'extra_cat_IDs', $post_extracats );
		}

		// Set object params:
		$edited_Item->load_from_Request( false );

		$Plugins->trigger_event( 'AdminBeforeItemEditUpdate', array( 'Item' => & $edited_Item ) );

		// Params we need for tab switching (in case of error or if we save&edit)
		$tab_switch_params = 'p='.$edited_Item->ID;

		if( $Messages->has_errors() )
		{	// There have been some validation errors:
			break;
		}

		// UPDATE POST IN DB:
		if( !$edited_Item->dbupdate() )
		{ // Could not update successful
			$Messages->add( TB_('The post couldn\'t be updated.'), 'error' );
			break;
		}

		if( strpos( $action, 'update' ) === 0 )
		{	// Update attachments folder:
			$edited_Item->update_attachments_folder();
		}

		// post post-publishing operations:
		param( 'trackback_url', 'string' );
		if( !empty( $trackback_url ) )
		{
			if( $edited_Item->status != 'published' )
			{
				$Messages->add( TB_('Post not publicly published: skipping trackback...'), 'note' );
			}
			else
			{ // trackback now:
				load_funcs('comments/_trackback.funcs.php');
				trackbacks( $trackback_url, $edited_Item );
			}
		}

		// Execute or schedule notifications & pings:
		$edited_Item->handle_notifications( NULL, false, $item_members_notified, $item_community_notified, $item_pings_sent );

		if( in_array( $action, array( 'update_edit', 'update', 'update_publish' ) ) )
		{	// Clear all proposed changes of the updated Item:
			$edited_Item->clear_proposed_changes();
		}

		$Messages->add( TB_('Post has been updated.'), 'success' );

		if( $action == 'extract_tags' )
		{	// Extract all possible tags from item contents:
			$searched_tags = $edited_Item->search_tags_by_content();
			// Append new searched tags to existing item's tags:
			$item_tags .= ','.implode( ',', $searched_tags );
			// Clear temp commas:
			$item_tags = utf8_trim( $item_tags, ',' );
		}

		// Delete Item from Session
		delete_session_Item( $edited_Item->ID );

		if( ! $exit_after_save )
		{ // We want to continue editing...
			break;
		}

		// Where to go after editing the post?
		if( ! $valid_item_type )
		{	// Item Type is not enabled for this collection, we will redirect to item type selection to allow user to change it t a valid one:
			$Messages->add( sprintf( TB_('You just edited an Item of Type "%s" which is not valid for this collection. Please select a new Item type below...'), $edited_Item->get( 't_type' ) ), 'warning' );
			$blog_redirect_setting = 'post_type';
		}
		elseif( $edited_Item->status == 'redirected' ||
		    strpos( $redirect_to, 'tab=tracker' ) )
		{ // We should show the posts list if:
			//    a post is in "Redirected" status
			//    a post is updated from "workflow" view tab
			$blog_redirect_setting = 'no';
		}
		elseif( strpos( $redirect_to, 'tab=manual' ) )
		{	// Use the original $redirect_to if a post is updated from "manual" view tab:
			$blog_redirect_setting = 'orig';
		}
		else
		{	// The post was changed:
			$blog_redirect_setting = $edited_Item->Blog->get_setting( 'editing_goto_blog' );
		}

		if( $blog_redirect_setting == 'blog' && $edited_Item->can_be_displayed() )
		{	// Redirect to collection home page ONLY if current user can view it:
			$edited_Item->load_Blog();
			$redirect_to = $edited_Item->Blog->gen_blogurl();
		}
		elseif( $blog_redirect_setting == 'post' && $edited_Item->can_be_displayed() )
		{	// Redirect to item page to view new created item ONLY if current user can view it:
			$redirect_to = $edited_Item->get_permanent_url();
		}
		elseif( $blog_redirect_setting == 'orig' )
		{ // Use original $redirect_to:
			// Set highlight:
			$Session->set( 'highlight_id', $edited_Item->ID );
			$Session->set( 'fadeout_array', array( 'item-'.$edited_Item->ID ) );
			$redirect_to = url_add_param( $redirect_to, 'highlight_id='.$edited_Item->ID, '&' );
		}
		elseif( $blog_redirect_setting == 'post_type' )
		{	// load_from_request param set to 0 will prevent loading of Item data from request because we are not passing any item data from request!
			$redirect_to = url_add_param( $admin_url, 'ctrl=items&action=edit_type&post_ID='.$edited_Item->ID.'&load_from_request=0' );
		}
		else
		{ // $blog_redirect_setting == 'no', set redirect_to = NULL which will redirect to posts list
			$redirect_to = NULL;
		}

		if( $redirect_to !== NULL )
		{	// The redirect url was NOT set to NULL ( $blog_redirect_setting == 'no' )

			// TRY TO REDIRECT / EXIT
			header_redirect( $redirect_to, 303, false, true /* RETURN if forbidden */ );

			// If we have not Exited yet, it means the redirect was refused because it was on a different domain
		}

		// Set highlight
		$Session->set( 'highlight_id', $edited_Item->ID );

		// REDIRECT / EXIT:
		header_redirect( regenerate_url( '', '&highlight='.$edited_Item->ID, '', '&' ) );
		/* EXITED */
		break;

	case 'update_type':
		// Check that this action request is not a CSRF hacked request:
		$Session->assert_received_crumb( 'item' );

		param( 'from_tab', 'string', NULL );
		param( 'post_ID', 'integer', true, true );
		param( 'ityp_ID', 'integer', true );

		// Used when we change a type of the duplicated item:
		$duplicated_item_ID = param( 'p', 'integer', NULL );

		// Load post from Session
		$edited_Item = get_session_Item( $post_ID );

		if( $Blog->is_item_type_enabled( $ityp_ID ) )
		{ // Set new post type only if it is enabled for the Blog:
			$edited_Item->set( 'ityp_ID', $ityp_ID );

			// Check if current post status is still applicable for new post type
			if( $edited_Item->get( 'pst_ID' ) != NULL )
			{
				$ItemTypeCache = & get_ItemTypeCache();
				$current_ItemType = $ItemTypeCache->get_by_ID( $ityp_ID );
				if( ! in_array( $edited_Item->get( 'pst_ID' ), $current_ItemType->get_applicable_post_status() ) )
				{
					$edited_Item->set( 'pst_ID', NULL );
					$Messages->add( TB_('The current item status is no longer valid for the new item type and has been reset.'), 'warning' );
				}
			}
		}

		// Unset old object of Post Type to reload it on next page
		unset( $edited_Item->ItemType );

		// Check edit permission:
		check_user_perm( 'item_post!CURSTATUS', 'edit', true, $edited_Item );

		set_session_Item( $edited_Item );

		if( ! empty( $from_tab ) && $from_tab == 'type' )
		{ // It goes from items lists to update item type immediately:

			if( $Blog->is_item_type_enabled( $ityp_ID ) )
			{ // Update only when the selected item type is enabled for the Blog:
				$Messages->add( TB_('Post type has been updated.'), 'success' );

				// Update item to set new type right now
				$edited_Item->dbupdate();

				// Highlight the updated item in list
				$Session->set( 'highlight_id', $edited_Item->ID );
			}

			// Set redirect back to items list with new item type tab
			$redirect_to = $admin_url.'?ctrl=items&blog='.$Blog->ID.'&tab=type&tab_type='.$edited_Item->get_type_setting( 'usage' ).'&filter=restore';
		}
		else
		{ // Set default redirect urls (It goes from the item edit form)
			if( $post_ID > 0 )
			{ // Edit item form
				$redirect_to = $admin_url.'?ctrl=items&blog='.$Blog->ID.'&action=edit&restore=1&p='.$edited_Item->ID;
			}
			elseif( $duplicated_item_ID > 0 )
			{ // Copy item form
				$redirect_to = $admin_url.'?ctrl=items&blog='.$Blog->ID.'&action=new&restore=1&p='.$duplicated_item_ID;
			}
			else
			{ // New item form
				$redirect_to = $admin_url.'?ctrl=items&blog='.$Blog->ID.'&action=new&restore=1';
			}
		}

		// REDIRECT / EXIT
		header_redirect( $redirect_to );
		/* EXITED */
		break;

	case 'update_status':
		// Check that this action request is not a CSRF hacked request:
		$Session->assert_received_crumb( 'item' );

		param( 'status', 'string', true );
		param( 'cat_ID', 'integer', NULL );

		// Check edit permission:
		check_user_perm( 'item_post!CURSTATUS', 'edit', true, $edited_Item );
		check_user_perm( 'item_post!'.$status, 'edit', true, $edited_Item );

		// Set new post type
		$edited_Item->set( 'status', $status );

		if( $edited_Item->get( 'status' ) == 'redirected' && empty( $edited_Item->url ) )
		{ // Note: post_url is not part of the simple form, so this message can be a little bit awkward there
			param_error( 'post_url',
				TB_('If you want to redirect this post, you must specify an URL!').' ('.TB_('Advanced properties panel').')',
				TB_('If you want to redirect this post, you must specify an URL!') );
		}

		if( param_errors_detected() )
		{ // If errors then redirect to edit form
			$redirect_to = $admin_url.'?ctrl=items&blog='.$Blog->ID.'&action=edit&restore=1&p='.$edited_Item->ID;
		}
		else
		{ // No errors, Update the item and redirect back to list
			$Messages->add( TB_('Post status has been updated.'), 'success' );

			// Update item to set new type right now
			$edited_Item->dbupdate();

			// Execute or schedule notifications & pings:
			$edited_Item->handle_notifications();

			// Set redirect back to items list with new item type tab:
			$tab = param( 'tab', 'string', 'type' );
			$tab_type = get_tab_by_item_type_usage( $edited_Item->get_type_setting( 'usage' ) );
			$tab_type_param = ( $tab == 'type' ? '&tab_type='.( $tab_type ? $tab_type[0] : 'post' ) : '' );
			$cat_param = ( $cat_ID === NULL ? '' : '&cat_ID='.$cat_ID );
			$redirect_to = $admin_url.'?ctrl=items&blog='.$Blog->ID.'&tab='.$tab.$tab_type_param.$cat_param.'&filter=restore';

			// Highlight the updated item in list
			$Session->set( 'highlight_id', $edited_Item->ID );
		}

		// REDIRECT / EXIT
		header_redirect( $redirect_to );
		/* EXITED */
		break;


	case 'mass_save' :
		// Check that this action request is not a CSRF hacked request:
		$Session->assert_received_crumb ( 'item' );

		init_list_mode ();
		$ItemList->query ();

		global $DB;

		$update_nr = 0;

		while ( $Item = & $ItemList->get_item () )
		{	// check user permission
			check_user_perm( 'item_post!CURSTATUS', 'edit', true, $Item );

			// Not allow html content on post titles
			$title = param ( 'mass_title_' . $Item->ID, 'htmlspecialchars', NULL );
			$urltitle = param ( 'mass_urltitle_' . $Item->ID, 'string', NULL );
			$titletag = param ( 'mass_titletag_' . $Item->ID, 'string', NULL );

			if ($title != NULL)
			{
				$Item->set ( 'title', $title );
			}

			if ($urltitle != NULL)
			{
				$Item->set ( 'urltitle', $urltitle );
			}

			if ($titletag != NULL)
			{
				$Item->set ( 'titletag', $titletag );
			}

			if( $Item->dbupdate ())
			{
				$update_nr++;	// successfully updated post number
			}
		}

		if( $update_nr > 0 )
		{
			$Messages->add( $update_nr == 1 ?
				TB_('One post has been updated!') :
				sprintf( TB_('%d posts have been updated!'), $update_nr ), 'success' );
		}
		else
		{
			$Messages->add( TB_('No update executed!') );
		}
		// REDIRECT / EXIT
		header_redirect ( $redirect_to, 303 );
		/* EXITED */
		break;

	case 'publish' :
	case 'publish_now' :
		// Publish NOW:

		// Check that this action request is not a CSRF hacked request:
		$Session->assert_received_crumb( 'item' );

		$post_status = ( $action == 'publish_now' ) ? 'published' : param( 'post_status', 'string', 'published' );

		// Check permissions:
		/* TODO: Check extra categories!!! */
		check_user_perm( 'item_post!'.$post_status, 'edit', true, $edited_Item );

		$edited_Item->set( 'status', $post_status );

		if( $action == 'publish_now' )
		{ // Update post dates
			check_user_perm( 'blog_edit_ts', 'edit', true, $Blog->ID );
			// fp> TODO: remove seconds ONLY if date is in the future
			$edited_Item->set( 'datestart', remove_seconds($localtimenow) );
			$edited_Item->set( 'datemodified', date('Y-m-d H:i:s', $localtimenow) );
		}

		// UPDATE POST IN DB:
		if( $edited_Item->dbupdate() )
		{
			// Update attachments folder:
			$edited_Item->update_attachments_folder();

			// Clear all proposed changes of the updated Item:
			$edited_Item->clear_proposed_changes();
		}

		// Get params to skip/force/mark notifications and pings:
		if( check_user_perm( 'blog_edit_ts', 'edit', false, $Blog->ID ) )
		{	// If user has a permission to edit advanced properties of items:
			param( 'item_members_notified', 'string', NULL );
			param( 'item_community_notified', 'string', NULL );
			param( 'item_pings_sent', 'string', NULL );
		}
		else
		{	// Use auto mode for notifications depending on the edited Item status:
			$item_members_notified = false;
			$item_community_notified = false;
			$item_pings_sent = false;
		}

		// Execute or schedule notifications & pings:
		$edited_Item->handle_notifications( NULL, false, $item_members_notified, $item_community_notified, $item_pings_sent );

		// Set the success message corresponding for the new status
		switch( $edited_Item->status )
		{
			case 'published':
				$success_message = TB_('Post has been published.');
				break;
			case 'community':
				$success_message = TB_('The post is now visible by the community.');
				break;
			case 'protected':
				$success_message = TB_('The post is now visible by the members.');
				break;
			case 'review':
				$success_message = TB_('The post is now visible by moderators.');
				break;
			default:
				$success_message = TB_('Post has been updated.');
				break;
		}
		$Messages->add( $success_message, 'success' );

		// Delete Item from Session
		delete_session_Item( $edited_Item->ID );

		// fp> I noticed that after publishing a new post, I always want to see how the blog looks like
		// If anyone doesn't want that, we can make this optional...

		// REDIRECT / EXIT
		if( $action == 'publish' && !empty( $redirect_to ) && ( strpos( $redirect_to, $admin_url ) !== 0 ) )
		{ // We clicked publish button from the front office
			header_redirect( $redirect_to );
		}
		elseif( $edited_Item->Blog->get_setting( 'enable_goto_blog' ) == 'blog' && $edited_Item->can_be_displayed() )
		{	// Redirect to collection home page ONLY if current user can view it:
			$edited_Item->load_Blog();
			header_redirect( $edited_Item->Blog->gen_blogurl() );
		}
		elseif( $edited_Item->Blog->get_setting( 'enable_goto_blog' ) == 'post' && $edited_Item->can_be_displayed() )
		{	// Redirect to item page to view new created item ONLY if current user can view it:
			header_redirect( $edited_Item->get_permanent_url() );
		}
		else// 'no'
		{	// Redirect to posts list:
			header_redirect( regenerate_url( '', '&highlight='.$edited_Item->ID, '', '&' ) );
		}
		// Switch to list mode:
		// $action = 'list';
		// init_list_mode();
		break;

	case 'restrict':
		// Check that this action request is not a CSRF hacked request:
		$Session->assert_received_crumb( 'item' );

		$post_status = param( 'post_status', 'string', true );
		// Check permissions:
		check_user_perm( 'item_post!'.$post_status, 'moderate', true, $edited_Item );

		$edited_Item->set( 'status', $post_status );

		// UPDATE POST IN DB:
		$edited_Item->dbupdate();

		$Messages->add( TB_('Post has been restricted.'), 'success' );

		// REDIRECT / EXIT
		header_redirect( $redirect_to );
		break;

	case 'deprecate':
		// Check that this action request is not a CSRF hacked request:
		$Session->assert_received_crumb( 'item' );

		$post_status = 'deprecated';
		// Check permissions:
		/* TODO: Check extra categories!!! */
		check_user_perm( 'item_post!'.$post_status, 'edit', true, $edited_Item );

		$edited_Item->set( 'status', $post_status );
		$edited_Item->set( 'datemodified', date('Y-m-d H:i:s',$localtimenow) );

		// UPDATE POST IN DB:
		$edited_Item->dbupdate();

		$Messages->add( TB_('Post has been deprecated.'), 'success' );

		// REDIRECT / EXIT
		header_redirect( $redirect_to );
		// Switch to list mode:
		// $action = 'list';
		// init_list_mode();
		break;


	case 'delete':
		// Delete an Item:

		// Check that this action request is not a CSRF hacked request:
		$Session->assert_received_crumb( 'item' );

		// Check permission:
		check_user_perm( 'blog_del_post', '', true, $blog );

		// fp> TODO: non javascript confirmation
		// $AdminUI->title = TB_('Deleting post...');

		$Plugins->trigger_event( 'AdminBeforeItemEditDelete', array( 'Item' => & $edited_Item ) );

		if( ! $Messages->has_errors() )
		{	// There have been no validation errors:
			// DELETE POST FROM DB:
			$edited_Item->dbdelete();

			$Messages->add( TB_('Post has been deleted.'), 'success' );
		}

		// REDIRECT / EXIT
		header_redirect( $redirect_to );
		// Switch to list mode:
		// $action = 'list';
		// init_list_mode();
		break;


	case 'mass_edit' :
		init_list_mode ();
		break;


	case 'list':
		init_list_mode();

		if( $ItemList->single_post )
		{	// We have requested to view a SINGLE specific post:
			$action = 'view';
		}
		break;

	case 'make_posts_pre':
		// Make posts with selected images action:
		break;

	case 'merge':
	case 'append':
		// Merge/Append the edited Item to another selected Item:

		// Check that this action request is not a CSRF hacked request:
		$Session->assert_received_crumb( 'item' );

		// Check edit permission:
		check_user_perm( 'item_post!CURSTATUS', 'edit', true, $edited_Item );

		$dest_post_ID = param( 'dest_post_ID', 'integer', true );

		if( $edited_Item->ID == $dest_post_ID )
		{	// If same Item is used as source for merging:
			// DO NOT translate, because it is a wrong request:
			$Messages->add( 'Don\'t use the same item for merging!', 'error' );
			// REDIRECT / EXIT
			header_redirect();
		}

		$ItemCache = & get_ItemCache();
		if( ! ( $dest_Item = & $ItemCache->get_by_ID( $dest_post_ID, false, false ) ) )
		{	// If Item doesn't exist in DB:
			$Messages->add( 'Item to merge does not exist any more.', 'error' );
			// REDIRECT / EXIT
			header_redirect();
		}

		if( $action == 'append' )
		{	// If we should append item and comments at the end with new dates
			$SQL = new SQL( 'Get the latest comment of Item #'.$dest_Item->ID.' to append Item #'.$edited_Item->ID );
			$SQL->SELECT( 'MAX( comment_date )' );
			$SQL->FROM( 'T_comments' );
			$SQL->WHERE( 'comment_item_ID = '.$dest_Item->ID );
			$latest_comment_time = $DB->get_var( $SQL );
			if( $latest_comment_time !== NULL )
			{	// If target Item has at lest one comment use new date/time for new appended comments:
				$append_comment_timestamp = strtotime( $latest_comment_time ) + 60;
			}
		}

		// Convert the source Item to comment of the target Item:
		$Comment = new Comment();
		$Comment->set( 'item_ID', $dest_Item->ID );
		$Comment->set( 'content', $edited_Item->get( 'content' ) );
		$Comment->set_renderers( $edited_Item->get_renderers() );
		$Comment->set( 'status', $edited_Item->get( 'status' ) == 'redirected' ? 'draft' : $edited_Item->get( 'status' ) );
		$Comment->set( 'author_user_ID', $edited_Item->get( 'creator_user_ID' ) );
		if( isset( $append_comment_timestamp ) )
		{	// Append action with 1 minute incrementing:
			$Comment->set( 'date', date2mysql( $append_comment_timestamp ) );
			$append_comment_timestamp += 60;
		}
		else
		{	// Merge action with saving date/time:
			$Comment->set( 'date', $edited_Item->get( 'datestart' ) );
		}
		$Comment->set( 'notif_status', $edited_Item->get( 'notifications_status' ) );
		$notifications_flags = $edited_Item->get( 'notifications_flags' );
		if( is_array( $notifications_flags ) )
		{
			foreach( $notifications_flags as $n => $notifications_flag )
			{
				if( ! in_array( $notifications_flag, array( 'moderators_notified', 'members_notified', 'community_notified' ) ) )
				{	// Skip values which are not allowed for comment:
					unset( $notifications_flags[ $n ] );
				}
			}
		}
		$Comment->set( 'notif_flags', $notifications_flags );
		if( $Comment->dbinsert() )
		{	// If comment has been created try to copy all attachments from source Item:
			$DB->query( 'UPDATE T_links
				  SET link_itm_ID = NULL, link_cmt_ID = '.$Comment->ID.'
				WHERE link_itm_ID = '.$edited_Item->ID );
			$DB->query( 'UPDATE T_links
				  SET link_position = "aftermore"
				WHERE link_cmt_ID = '.$Comment->ID.'
				  AND link_position != "teaser"
				  AND link_position != "aftermore"' );
		}
		// Move all comments of the source Item to the target Item:
		if( isset( $append_comment_timestamp ) )
		{	// Append comments with new dates after the latest comment of the target Item:
			$SQL = new SQL( 'Get all comments of source Item #'.$edited_Item->ID.' in order to append to target Item #'.$dest_Item->ID );
			$SQL->SELECT( 'comment_ID' );
			$SQL->FROM( 'T_comments' );
			$SQL->WHERE( 'comment_item_ID = '.$edited_Item->ID );
			$SQL->ORDER_BY( 'comment_date' );
			$source_comment_IDs = $DB->get_col( $SQL );
			foreach( $source_comment_IDs as $source_comment_ID )
			{
				$DB->query( 'UPDATE T_comments
					  SET comment_item_ID = '.$dest_Item->ID.',
					      comment_date = '.$DB->quote( date2mysql( $append_comment_timestamp ) ).'
					WHERE comment_ID = '.$source_comment_ID );
				// Increment 1 minute for each next appending comment:
				$append_comment_timestamp += 60;
			}
		}
		else
		{	// Merge comments with saving their dates:
			$DB->query( 'UPDATE T_comments
				  SET comment_item_ID = '.$dest_Item->ID.'
				WHERE comment_item_ID = '.$edited_Item->ID );
		}
		// Copy all slugs from source Item to destination Item:
		$DB->query( 'UPDATE T_slug
				  SET slug_itm_ID = '.$dest_Item->ID.'
				WHERE slug_itm_ID = '.$edited_Item->ID );
		// Delete the source Item completely:
		$edited_Item_ID = $edited_Item->ID;
		$edited_Item->dbdelete();

		$Messages->add( sprintf( ( $action == 'append' )
			? TB_('Item #%d has been appended to current Item.')
			: TB_('Item #%d has been merged to current Item.'), $edited_Item_ID ), 'success' );

		// REDIRECT / EXIT
		header_redirect( $admin_url.'?ctrl=items&blog='.$blog.'&p='.$dest_Item->ID );
		break;

	case 'save_propose':
		// Save new proposed change:

		// Check that this action request is not a CSRF hacked request:
		$Session->assert_received_crumb( 'item' );

		// Check edit permission:
		check_user_perm( 'blog_item_propose', 'edit', true, $Blog->ID );

		// Check if current User can create a new proposed change:
		$edited_Item->can_propose_change( true );

		if( $edited_Item->create_proposed_change() )
		{	// If new proposed changes has been inserted in DB successfully:
			$Messages->add( TB_('New proposed change has been recorded.'), 'success' );
			if( check_user_perm( 'item_post!CURSTATUS', 'edit', false, $edited_Item ) )
			{	// Redirect to item history page with new poroposed change if current User has a permisson:
				header_redirect( $admin_url.'?ctrl=items&action=history&p='.$edited_Item->ID );
			}
			else
			{	// Redirect to item view page:
				header_redirect( $admin_url.'?ctrl=items&blog='.$edited_Item->get_blog_ID().'&p='.$edited_Item->ID );
			}
		}

		// If some errors on creating new proposed change,
		// Display the same submitted form of new proposed change:
		$action = 'propose';
		break;

	case 'accept_propose':
	case 'reject_propose':
		// Accept/Reject the proposed change:

		// Check that this action request is not a CSRF hacked request:
		$Session->assert_received_crumb( 'item' );

		// Check edit permission:
		check_user_perm( 'item_post!CURSTATUS', 'edit', true, $edited_Item );

		// Try to get a proposed change by requested ID:
		$Revision = $edited_Item->get_revision( param( 'r', 'string' ) );

		if( ! $Revision || $Revision->iver_type != 'proposed' )
		{	// Stop on wrong requested proposed change:
			debug_die( 'The proposed change #'.$r.' could not be found for Item #'.$edited_Item->ID.' in DB!' );
		}

		if( $action == 'accept_propose' )
		{	// Accept the proposed change:
			// Update current Item with values from the requested proposed change:
			$result = $edited_Item->update_from_revision( get_param( 'r' ) );
			$success_message = sprintf( TB_('The proposed change #%d has been accepted.'), $Revision->iver_ID );
		}
		else
		{	// Reject the proposed change:
			$result = true;
			$success_message = sprintf( TB_('The proposed change #%d has been rejected.'), $Revision->iver_ID );
		}
		if( $result )
		{	// Delete also the proposed changes with custom fields and links to complete accept/reject action:
			$edited_Item->clear_proposed_changes( $action, $Revision->iver_ID );
			// Display success message:
			$Messages->add( $success_message, 'success' );
		}

		// Redirect to item history page with new poroposed change:
		header_redirect( $admin_url.'?ctrl=items&action=history&p='.$edited_Item->ID );
		break;

	default:
		debug_die( 'unhandled action 2: '.htmlspecialchars($action) );
}


/**
 * Initialize list mode; Several actions need this.
 */
function init_list_mode()
{
	global $tab, $tab_type, $Collection, $Blog, $UserSettings, $ItemList, $AdminUI;

	// set default itemslist param prefix
	$items_list_param_prefix = 'items_';

	if ( param( 'p', 'integer', NULL ) || param( 'title', 'string', NULL ) )
	{	// Single post requested, do not filter any post types. If the user
		// has clicked a post link on the dashboard and previously has selected
		// a tab which would filter this post, it wouldn't be displayed now.
		$tab = 'full';
		// in case of single item view params prefix must be empty
		$items_list_param_prefix = NULL;
	}
	else
	{	// Store/retrieve preferred tab from UserSettings:
		$UserSettings->param_Request( 'tab', 'pref_browse_tab', 'string', NULL, true /* memorize */, true /* force */ );
		$UserSettings->param_Request( 'tab_type', 'pref_browse_tab_type', 'string', NULL, true /* memorize */, true /* force */ );
		if( ! in_array( $tab_type, array( 'post', 'page', 'intro', 'content-block', 'special' ) ) )
		{	// Fix wrong requested type:
			$tab_type = 'post';
		}
	}

	if( $tab == 'tracker' && ( ! $Blog->get_setting( 'use_workflow' ) || ! check_user_perm( 'blog_can_be_assignee', 'edit', false, $Blog->ID ) ) )
	{ // Display workflow view only if it is enabled
		global $Messages;
		$Messages->add( TB_('Workflow feature has not been enabled for this collection.'), 'note' );
		$tab = 'full';
	}

	/*
	 * Init list of posts to display:
	 */
	load_class( 'items/model/_itemlist.class.php', 'ItemList2' );

	if( !empty( $tab ) && !empty( $items_list_param_prefix ) )
	{	// Use different param prefix for each tab
		$items_list_param_prefix .= substr( $tab, 0, 7 ).'_';//.utf8_strtolower( $tab_type ).'_';
	}

	// Set different filterset name for each different tab and tab_type
	$filterset_name = ( $tab == 'type' ) ? $tab.'_'.utf8_strtolower( $tab_type ) : $tab;
	// Append collection ID to filterset in order to keep filters separately per collection:
	$filterset_name .= $Blog->ID;

	// Create empty List:
	$ItemList = new ItemList2( $Blog, NULL, NULL, $UserSettings->get('results_per_page'), 'ItemCache', $items_list_param_prefix, $filterset_name /* filterset name */ ); // COPY (func)

	$ItemList->set_default_filters( array(
			'visibility_array' => get_visibility_statuses('keys'),
		) );

	if( $Blog->get_setting('orderby') == 'RAND' )
	{	// Do not display random posts in backoffice for easy management
		$ItemList->set_default_filters( array(
				'orderby' => 'datemodified',
			) );
	}

	switch( $tab )
	{
		case 'full':
			$ItemList->set_default_filters( array(
					'itemtype_usage' => NULL // All types
				) );
			// $AdminUI->breadcrumbpath_add( TB_('All items'), '?ctrl=items&amp;blog=$blog$&amp;tab='.$tab.'&amp;filter=restore' );

			// require colorbox js
			require_js_helper( 'colorbox' );

			// require clipboardjs
			require_js_async( '#clipboardjs#' );

			$AdminUI->breadcrumbpath_add( TB_('All'), '?ctrl=items&amp;blog=$blog$&amp;tab=full&amp;filter=restore' );
			break;

		case 'summary':
			$ItemList->set_default_filters( array(
					'itemtype_usage' => NULL // All types
				) );

			// require colorbox js
			require_js_helper( 'colorbox' );

			$AdminUI->breadcrumbpath_add( TB_('Summary'), '?ctrl=items&amp;blog=$blog$&amp;tab=summary&amp;filter=restore' );
			break;

		case 'manual':
			if( $Blog->get( 'type' ) != 'manual' )
			{	// Display this tab only for manual blogs
				global $admin_url;
				header_redirect( $admin_url.'?ctrl=items&blog='.$Blog->ID.'&tab=type&tab_type=post&filter=restore' );
			}

			global $ReqURI, $blog;

			init_field_editor_js( array(
					'action_url' => $ReqURI.'&blog='.$blog.'&order_action=update&order_data=',
				) );

			$AdminUI->breadcrumbpath_add( TB_('Manual view'), '?ctrl=items&amp;blog=$blog$&amp;tab='.$tab.'&amp;filter=restore' );
			break;

		case 'type':
			// Filter a posts list by type
			$ItemList->set_default_filters( array(
					'itemtype_usage' => implode( ',', get_item_type_usage_by_tab( $tab_type ) ),
				) );
			$AdminUI->breadcrumbpath_add( TB_( $tab_type ), '?ctrl=items&amp;blog=$blog$&amp;tab='.$tab.'&amp;tab_type='.urlencode( $tab_type ).'&amp;filter=restore' );

			// JS to edit an order of items from list view:
			require_js_defer( 'customized:jquery/jeditable/jquery.jeditable.js', 'rsc_url' );
			break;

		case 'tracker':
			// In tracker mode, we want a different default sort:
			$ItemList->set_default_filters( array(
					'orderby' => 'priority',
					'order' => 'ASC' ) );
			$AdminUI->breadcrumbpath_add( TB_( 'Workflow view' ), '?ctrl=items&amp;blog=$blog$&amp;tab=tracker&amp;filter=restore' );

			$AdminUI->set_page_manual_link( 'workflow-features' );

			// JS to edit priority of items from list view
			require_js_defer( 'customized:jquery/jeditable/jquery.jeditable.js', 'rsc_url' );
			break;

		default:
			// Delete the pref_browse_tab setting so that the default
			// (full) gets used the next time the user wants to browse
			// a blog and we don't run into the same error again.
			$UserSettings->delete( 'pref_browse_tab' );
			$UserSettings->dbupdate();
			debug_die( 'Unknown filterset ['.$tab.']' );
	}

	// Init filter params:
	if( ! $ItemList->load_from_Request() )
	{ // If we could not init a filterset from request
		// typically happens when we could no fall back to previously saved filterset...
		// echo ' no filterset!';
	}
}

/**
 * Configure page navigation:
 */
switch( $action )
{
	case 'new':
	case 'new_switchtab': // this gets set as action by JS, when we switch tabs
	case 'new_type': // this gets set as action by JS, when we switch tabs
	case 'copy':
	case 'new_version':
	case 'create_edit':
	case 'create_link':
	case 'create':
	case 'create_publish':
		// Generate available blogs list:
		$AdminUI->set_coll_list_params( 'blog_post_statuses', 'edit', array( 'ctrl' => 'items', 'action' => 'new' ) );

		// We don't check the following earlier, because we want the blog switching buttons to be available:
		if( ! blog_has_cats( $blog ) )
		{
			$error_message = TB_('Since this blog has no categories, you cannot post into it.');
			if( check_user_perm( 'blog_cats', 'edit', false, $blog ) )
			{ // If current user has a permission to create a category
				global $admin_url;
				$error_message .= ' '.sprintf( TB_('You must <a %s>create categories</a> first.'), 'href="'.$admin_url.'?ctrl=chapters&amp;blog='.$blog.'"');
			}
			$Messages->add( $error_message, 'error' );
			$action = 'nil';
			break;
		}

		/* NOBREAK */

	case 'edit':
	case 'edit_switchtab': // this gets set as action by JS, when we switch tabs
	case 'edit_type': // this gets set as action by JS, when we switch tabs
	case 'propose':
	case 'update_edit':
	case 'update': // on error
	case 'update_publish': // on error
	case 'history':
	case 'history_details':
	case 'history_compare':
	case 'extract_tags':
		// Generate available blogs list:
		$AdminUI->set_coll_list_params( 'blog_ismember', 'view', array( 'ctrl' => 'items', 'filter' => 'restore' ) );

		$display_permalink = false;

		// Display item's title and ID in <title> tag instead of default breadcrumb path:
		$AdminUI->htmltitle = $edited_Item->get_title( array(
				'title_field' => 'short_title,title',
				'link_type'   => 'none',
			) );
		if( empty( $AdminUI->htmltitle ) )
		{	// Display collection short name when item has no yet e.g. on creating or when titles are disabled for current Item Type:
			$AdminUI->htmltitle = $Blog->get( 'shortname' );
		}
		$AdminUI->htmltitle .= ' ('.( empty( $edited_Item->ID ) ? TB_('New') : '#'.$edited_Item->ID ).')';

		switch( $action )
		{
			case 'edit':
			case 'edit_switchtab': // this gets set as action by JS, when we switch tabs
			case 'update_edit':
			case 'update': // on error
			case 'update_publish': // on error
			case 'extract_tags':
				$item_permanent_url = $edited_Item->get_permanent_url( '', '', '&amp;', array( 'none' ) );
				if( $item_permanent_url !== false )
				{	// Display item permanent URL only if permanent type is not 'none':
					$AdminUI->global_icon( TB_('Permanent link to full entry'), 'permalink', $item_permanent_url,
							' '.TB_('Permalink'), 4, 3, array(
									'style' => 'margin-right: 3ex',
							) );
					$display_permalink = true;
				}

				if( $Blog->get_setting( 'allow_comments' ) != 'never' )
				{
					$comments_number = generic_ctp_number( $edited_Item->ID, 'comments', 'total', true );
					$item_feedback_title = ( $comments_number == 0 ? TB_('no comment') : ( $comments_number == 1 ? TB_('1 comment') : sprintf( TB_('%d comments'), $comments_number ) ) );
					$AdminUI->global_icon( $item_feedback_title, ( $comments_number > 0 ? 'comments' : 'nocomment' ), $admin_url.'?ctrl=items&amp;blog='.$Blog->ID.'&amp;p='.$edited_Item->ID.'#comments',
						' '.$item_feedback_title, 4, 3, array(
								'style' => 'margin-right: 3ex;',
						) );
				}

				$edited_item_url = $edited_Item->get_copy_url();
				if( ! empty( $edited_item_url ) )
				{	// If user has a permission to copy the edited Item:
					$AdminUI->global_icon( TB_('Duplicate this post...'), 'copy', $edited_item_url,
						' '.TB_('Duplicate...'), 4, 3, array(
								'style' => 'margin-right: 3ex;',
						) );
				}

				if( check_user_perm( 'item_post!CURSTATUS', 'edit', false, $edited_Item ) )
				{	// If user has a permission to merge the edited Item:
					$AdminUI->global_icon( TB_('Merge with...'), 'merge', '#',
						' '.TB_('Merge with...'), 4, 3, array(
								'style' => 'margin-right: 3ex;',
								'onclick' => 'return evo_merge_load_window( '.$edited_Item->ID.' )',
						) );
				}

				if( check_user_perm( 'item_post!CURSTATUS', 'delete', false, $edited_Item ) )
				{	// User has permissions to delete this post
					$AdminUI->global_icon( TB_('Delete this post'), 'delete', $admin_url.'?ctrl=items&amp;action=delete&amp;post_ID='.$edited_Item->ID.'&amp;'.url_crumb('item'),
						' '.TB_('Delete'), 4, 3, array(
								'onclick' => 'return confirm(\''.TS_('You are about to delete this post!\\nThis cannot be undone!').'\')',
								'style' => 'margin-right: 3ex;',	// Avoid misclicks by all means!
						) );
				}
				break;
		}

		if( ! in_array( $action, array( 'new_type', 'edit_type', 'history', 'history_details', 'history_compare' ) ) )
		{
			if( $edited_Item->ID > 0 )
			{ // Display a link to history if Item exists in DB
				$AdminUI->global_icon( TB_('Changes'), '', $edited_Item->get_history_url(),
					$edited_Item->history_info_icon().' '.TB_('Changes'), 4, 3, array(
							'style' => 'margin-right: 3ex'
					) );

				// Params we need for tab switching
				$tab_switch_params = 'p='.$edited_Item->ID;
			}
			else
			{
				$tab_switch_params = '';
			}

			// Rearrange global icons to show: Permalink - History - Duplicate - Delete - Close
			if( count( $AdminUI->global_icons ) > 1 && $edited_Item->ID > 0 )
			{
				$history_icon = array_pop( $AdminUI->global_icons );

				if( $display_permalink )
				{ // Insert the history icon right after the permalink icon
					array_splice( $AdminUI->global_icons, 1, 0, array( $history_icon ) );
				}
				else
				{ // Move the history icon in front
					array_unshift( $AdminUI->global_icons, $history_icon );
				}
			}

			if( $action != 'propose' && $Blog->get_setting( 'in_skin_editing' ) && ( check_user_perm( 'blog_post!published', 'edit', false, $Blog->ID ) || get_param( 'p' ) > 0 ) )
			{ // Show 'In skin' link if Blog setting 'In-skin editing' is ON and User has a permission to publish item in this blog
				$mode_inskin_url = url_add_param( $Blog->get( 'url' ), 'disp=edit&amp;'.$tab_switch_params );
				$mode_inskin_action = get_htsrv_url().'item_edit.php';
				$AdminUI->global_icon( TB_('In-skin editing'), 'edit', $mode_inskin_url,
						' '.TB_('In-skin editing'), 4, 3, array(
						'style' => 'margin-right: 3ex',
						'data-shortcut' => 'f2',
						'onclick' => 'return b2edit_reload( \'#item_checkchanges\', \''.$mode_inskin_action.'\' );'
				) );
			}

			$AdminUI->global_icon( TB_('Cancel editing').'!', 'close', $redirect_to, TB_('Cancel'), 4, 2 );

			init_tokeninput_js( 'blog' );
			init_hotkeys_js( 'blog', array( 'f2', 'f9' ) );
		}

		if( in_array( $action, array( 'history', 'history_details', 'history_compare' ) ) )
		{	// History tabs:
			if( check_user_perm( 'item_post!CURSTATUS', 'delete', false, $edited_Item ) )
			{	// User has permissions to edit this Item:
				$AdminUI->global_icon( TB_('Edit current version'), 'edit',  $admin_url.'?ctrl=items&amp;action=edit&amp;p='.$edited_Item->ID, TB_('Edit current version'), 4, 3, array( 'style' => 'margin-right:3ex' ) );
			}

			$item_permanent_url = $edited_Item->get_permanent_url( '', '', '&amp;', array( 'none' ) );
			if( $item_permanent_url !== false )
			{	// Display item permanent URL only if permanent type is not 'none':
				$AdminUI->global_icon( TB_('Permanent link to full entry'), 'permalink', $item_permanent_url,
						' '.TB_('Permalink'), 4, 3, array(
								'style' => 'margin-right: 3ex',
						) );
			}

			$AdminUI->global_icon( TB_('Cancel editing').'!', 'close', regenerate_url( 'action', 'action=history' ), TB_('Cancel'), 4, 2 );
		}

		break;

	case 'new_mass':

		$AdminUI->set_coll_list_params( 'blog_post_statuses', 'edit', array( 'ctrl' => 'items', 'action' => 'new' ) );

		// We don't check the following earlier, because we want the blog switching buttons to be available:
		if( ! blog_has_cats( $blog ) )
		{
			$error_message = TB_('Since this blog has no categories, you cannot post into it.');
			if( check_user_perm( 'blog_cats', 'edit', false, $blog ) )
			{ // If current user has a permission to create a category
				global $admin_url;
				$error_message .= ' '.sprintf( TB_('You must <a %s>create categories</a> first.'), 'href="'.$admin_url.'?ctrl=chapters&amp;blog='.$blog.'"');
			}
			$Messages->add( $error_message, 'error' );
			$action = 'nil';
			break;
		}

	break;

	case 'view':
	case 'history_compare':
	case 'history_details':
		// We're displaying a SINGLE specific post:
		$item_ID = param( 'p', 'integer', true );

		$AdminUI->title_titlearea = TB_('View post & comments');

		if( ! isset( $tab ) )
		{
			$tab = 'full';
		}

		init_hotkeys_js( 'blog', array( 'f2', 'ctrl+f2' ) );

		// Generate available blogs list:
		$AdminUI->set_coll_list_params( 'blog_ismember', 'view', array( 'ctrl' => 'items', 'tab' => $tab, 'filter' => 'restore' ) );

		$AdminUI->breadcrumbpath_add( sprintf( /* TRANS: noun */ TB_('Post').' #%s', $item_ID ), '?ctrl=items&amp;blog='.$Blog->ID.'&amp;p='.$item_ID );
		$AdminUI->breadcrumbpath_add( TB_('View post & comments'), '?ctrl=items&amp;blog='.$Blog->ID.'&amp;p='.$item_ID );
		break;

	case 'list':
		// We're displaying a list of posts:

		$AdminUI->title_titlearea = TB_('Browse blog');

		// Generate available blogs list:
		$AdminUI->set_coll_list_params( 'blog_ismember', 'view', array( 'ctrl' => 'items', 'tab' => $tab, 'filter' => 'restore' ) );

		break;
}

/*
 * Add sub menu entries:
 * We do this here instead of _header because we need to include all filter params into regenerate_url()
 */
attach_browse_tabs();


if( isset( $edited_Item ) && ( $ItemType = & $edited_Item->get_ItemType() ) )
{	// Set a tab type for edited/viewed item:
	$tab = 'type';
	if( $tab_type = get_tab_by_item_type_usage( $ItemType->usage ) )
	{	// Only if tab exists for current item type usage:
		$tab_type = $tab_type[0];
	}
	else
	{
		$tab_type = 'all';
	}
}

if( ! empty( $tab ) && $tab == 'type' )
{ // Set a path from dynamic tabs
	$AdminUI->append_path_level( 'type_'.str_replace( ' ', '_', utf8_strtolower( $tab_type ) ) );
}
else
{ // Set a path to selected tab
	$AdminUI->append_path_level( empty( $tab ) ? 'full' : $tab );
}

if( ( isset( $tab ) && in_array( $tab, array( 'full', 'summary', 'type', 'tracker' ) ) ) || strpos( $action, 'edit' ) === 0 )
{ // Init JS to autcomplete the user logins
	init_autocomplete_login_js( 'rsc_url', $AdminUI->get_template( 'autocomplete_plugin' ) );
	// Initialize date picker for _item_expert.form.php
	init_datepicker_js();
}

// Load the appropriate blog navigation styles (including calendar, comment forms...):
require_css( $AdminUI->get_template( 'blog_base.css' ) ); // Default styles for the blog navigation
init_popover_js( 'rsc_url', $AdminUI->get_template( 'tooltip_plugin' ) );

/* fp> I am disabling this. We haven't really used per-blof styles yet and at the moment it creates interference with boostrap Admin
// Load the appropriate ITEM/POST styles depending on the blog's skin:
// It's possible that we have no Blog on the restricted admin interface, when current User doesn't have permission to any blog
if( !empty( $Blog ) )
{ // set blog skin ID if the Blog is set
	$blog_sking_ID = $Blog->get_skin_ID();
	if( ! empty( $blog_sking_ID ) )
	{
		$SkinCache = & get_SkinCache();
		$Skin = $SkinCache->get_by_ID( $blog_sking_ID );
		require_css( 'basic_styles.css', 'blog' ); // the REAL basic styles
		require_css( 'item_base.css', 'blog' ); // Default styles for the post CONTENT
		require_css( $skins_url.$Skin->folder.'/item.css' ); // fp> TODO: this needs to be a param... "of course" -- if none: else item_default.css ?
		// else: $item_css_url = $rsc_url.'css/item_base.css';
	}
	// else item_default.css ? is it still possible to have no skin set?
}
*/

if( $action == 'view' || $action == 'history_compare' || strpos( $action, 'edit' ) !== false || strpos( $action, 'new' ) !== false || $action == 'copy' )
{	// Initialize js to autocomplete usernames in post/comment form
	init_autocomplete_usernames_js();
	// Require colorbox js:
	require_js_helper( 'colorbox' );
}

if( in_array( $action, array( 'new', 'new_version', 'copy', 'create_edit', 'create_link', 'create', 'create_publish', 'edit', 'update_edit', 'update', 'update_publish', 'extract_tags' ) ) )
{ // Set manual link for edit expert mode
	$AdminUI->set_page_manual_link( 'expert-edit-screen' );
}

// Set an url for manual page:
switch( $action )
{
	case 'history':
	case 'history_details':
		$AdminUI->set_page_manual_link( 'item-revision-history' );
		break;
	case 'new':
	case 'new_switchtab':
	case 'edit':
	case 'edit_switchtab':
	case 'copy':
	case 'new_version':
	case 'create':
	case 'create_edit':
	case 'create_link':
	case 'create_publish':
	case 'update':
	case 'update_edit':
	case 'update_publish':
	case 'extract_tags':
		$AdminUI->set_page_manual_link( 'expert-edit-screen' );
		break;
	case 'edit_type':
		$AdminUI->set_page_manual_link( 'change-post-type' );
		break;
	case 'new_mass':
		$AdminUI->set_page_manual_link( 'mass-new-screen' );
		break;
	case 'mass_edit':
		$AdminUI->set_page_manual_link( 'mass-edit-screen' );
		break;
	default:
		switch( get_param( 'tab' ) )
		{
			case 'tracker':
				$AdminUI->set_page_manual_link( 'task-list' );
				break;
			case 'manual':
				$AdminUI->set_page_manual_link( 'manual-pages-editor' );
				break;
			case 'type':
				$AdminUI->set_page_manual_link( $tab_type.'-list' );
				break;
			default:
				$AdminUI->set_page_manual_link( 'browse-edit-tab' );
		}
		break;
}

// Display <html><head>...</head> section! (Note: should be done early if actions do not redirect)
$AdminUI->disp_html_head();

// Display title, menu, messages, etc. (Note: messages MUST be displayed AFTER the actions)
$AdminUI->disp_body_top( $mode != 'iframe' );	// do NOT display stupid messages in iframe (UGLY UGLY UGLY!!!!)


/*
 * Display payload:
 */
switch( $action )
{
	case 'nil':
		// Do nothing
		break;

	case 'new_switchtab': // this gets set as action by JS, when we switch tabs
	case 'edit_switchtab': // this gets set as action by JS, when we switch tabs
	case 'new_type': // this gets set as action by JS, when we switch tabs
	case 'edit_type': // this gets set as action by JS, when we switch tabs
		$bozo_start_modified = true;	// We want to start with a form being already modified
	case 'new':
	case 'copy':
	case 'new_version':
	case 'create_edit':
	case 'create_link':
	case 'create':
	case 'create_publish':
	case 'edit':
	case 'propose':
	case 'update_edit':
	case 'update':	// on error
	case 'update_publish':	// on error
	case 'extract_tags':
		// Begin payload block:
		$AdminUI->disp_payload_begin();

		// We never allow HTML in titles, so we always encode and decode special chars.
		$item_title = htmlspecialchars_decode( $edited_Item->get( 'title' ) );

		$item_content = $edited_Item->get( 'content' );
		if( $item_content === NULL )
		{	// Use text template for new creating Item:
			$item_content = $edited_Item->get_type_setting( 'text_template' );
		}
		$item_content = prepare_item_content( $item_content );

		if( ! $edited_Item->get_type_setting( 'allow_html' ) )
		{ // HTML is disallowed for this post, content is encoded in DB and we need to decode it for editing:
			$item_content = htmlspecialchars_decode( $item_content );
		}

		// Format content for editing, if we were not already in editing...
		$Plugins_admin = & get_Plugins_admin();
		$edited_Item->load_Blog();
		$params = array( 'object_type' => 'Item', 'object_Blog' => & $edited_Item->Blog );
		$Plugins_admin->unfilter_contents( $item_title /* by ref */, $item_content /* by ref */, $edited_Item->get_renderers_validated(), $params );

		// Display VIEW:
		switch( $action )
		{
			case 'new_type':
			case 'edit_type':
				// Form to change post type:
				$AdminUI->disp_view( 'items/views/_item_edit_type.form.php' );
				break;

			case 'propose':
				// Form to change post type:
				$AdminUI->disp_view( 'items/views/_item_propose.form.php' );
				break;

			default:
				// Form to edit item
				$AdminUI->disp_view( 'items/views/_item_expert.form.php' );
		}

		// End payload block:
		$AdminUI->disp_payload_end();
		break;


	case 'new_mass':
		// Begin payload block:
		$AdminUI->disp_payload_begin();

		// We never allow HTML in titles, so we always encode and decode special chars.
		$item_title = htmlspecialchars_decode( $edited_Item->title );

		$item_content = prepare_item_content( $edited_Item->content );

		if( ! $edited_Item->get_type_setting( 'allow_html' ) )
		{ // HTML is disallowed for this post, content is encoded in DB and we need to decode it for editing:
			$item_content = htmlspecialchars_decode( $item_content );
		}

		// Format content for editing, if we were not already in editing...
		$Plugins_admin = & get_Plugins_admin();
		$edited_Item->load_Blog();
		$params = array( 'object_type' => 'Item', 'object_Blog' => & $edited_Item->Blog );
		$Plugins_admin->unfilter_contents( $item_title /* by ref */, $item_content /* by ref */, $edited_Item->get_renderers_validated(), $params );

		$AdminUI->disp_view( 'items/views/_item_mass.form.php' );

		// End payload block:
		$AdminUI->disp_payload_end();

		break;


	case 'view':
	case 'delete':
		// View a single post:

		// Memorize 'p' in case we reload while changing some display settings
		memorize_param( 'p', 'integer', NULL );

		// What comments view, 'feedback' - all user comments, 'meta' - internal comments of the admins
		param( 'comment_type', 'string', 'feedback', true );

		// Begin payload block:
		$AdminUI->disp_payload_begin();

		// We use the "full" view for displaying single posts:
		$AdminUI->disp_view( 'items/views/_item_list_full.view.php' );

		// End payload block:
		$AdminUI->disp_payload_end();

		break;

	case 'history':
		memorize_param( 'action', 'string', NULL );

		// Begin payload block:
		$AdminUI->disp_payload_begin();

		// view:
		$AdminUI->disp_view( 'items/views/_item_history.view.php' );

		// End payload block:
		$AdminUI->disp_payload_end();
		break;

	case 'history_details':
		// Begin payload block:
		$AdminUI->disp_payload_begin();

		// view:
		$AdminUI->disp_view( 'items/views/_item_history_details.view.php' );

		// End payload block:
		$AdminUI->disp_payload_end();
		break;

	case 'history_compare':
		// Begin payload block:
		$AdminUI->disp_payload_begin();

		// view:
		$AdminUI->disp_view( 'items/views/_item_history_compare.view.php' );

		// End payload block:
		$AdminUI->disp_payload_end();
		break;

	case 'mass_edit' :
		// Begin payload block:
		$AdminUI->disp_payload_begin ();

		// view:
		$AdminUI->disp_view ( 'items/views/_item_mass_edit.view.php' );

		// End payload block:
		$AdminUI->disp_payload_end ();
		break;

	case 'make_posts_pre':
		// Make posts with selected images action:

		$FileRootCache = & get_FileRootCache();
		// getting root
		$root = param( 'root', 'string' );
		global $fm_FileRoot;
		$fm_FileRoot = & $FileRootCache->get_by_ID($root, true);

		// Begin payload block:
		$AdminUI->disp_payload_begin();
		// Check that this action request is not a CSRF hacked request:
		$Session->assert_received_crumb( 'file' );

		$AdminUI->disp_view( 'items/views/_file_create_posts.form.php' );
		// End payload block:
		$AdminUI->disp_payload_end();
		break;

	case 'list':
	default:
		// Begin payload block:
		$AdminUI->disp_payload_begin();

		// fplanque> Note: this is depressing, but I have to put a table back here
		// just because IE supports standards really badly! :'(
		$table_browse_template = $AdminUI->get_template( 'table_browse' );
		echo $table_browse_template['table_start'];

		if( $tab == 'manual' )
		{
			echo $table_browse_template['full_col_start'];
		}
		else{
			echo $table_browse_template['left_col_start'];
		}

			switch( $tab )
			{
				case 'tracker':
					// Display VIEW:
					$AdminUI->disp_view( 'items/views/_item_list_track.view.php' );
					break;

				case 'full':
					// Display VIEW:
					$AdminUI->disp_view( 'items/views/_item_list_full.view.php' );
					break;

				case 'summary':
					// Display VIEW:
					$AdminUI->disp_view( 'items/views/_item_list_summary.view.php' );
					break;

				case 'manual':
					// Display VIEW:
					$AdminUI->disp_view( 'items/views/_item_list_manual.view.php' );
					break;

				case 'type':
				default:
					// Display VIEW:
					$AdminUI->disp_view( 'items/views/_item_list_table.view.php' );
					break;
			}

			// TODO: a specific field for the backoffice, at the bottom of the page
			// would be used for moderation rules.
			if( $Blog->get( 'notes' ) )
			{
				$edit_link = '';
				if( check_user_perm( 'blog_properties', 'edit', false, $blog ) )
				{
					$edit_link = action_icon( TB_('Edit').'...', 'edit_button', $admin_url.'?ctrl=coll_settings&amp;tab=general&amp;blog='.$Blog->ID, ' '.TB_('Edit').'...', 3, 4, array( 'class' => 'btn btn-default btn-sm' ) );
				}
				$block_item_Widget = new Widget( 'block_item' );
				$block_item_Widget->title = '<span class="pull-right panel_heading_action_icons">'.$edit_link.'</span>'.TB_('Notes');
				$block_item_Widget->disp_template_replaced( 'block_start' );
				$Blog->disp( 'notes', 'htmlbody' );
				$block_item_Widget->disp_template_replaced( 'block_end' );
			}

		echo $table_browse_template['left_col_end'];

		if( $tab != 'manual' )
		{
			echo $table_browse_template['right_col_start'];
				// Display VIEW:
				$AdminUI->disp_view( 'items/views/_item_list_sidebar.view.php' );
			echo $table_browse_template['right_col_end'];
		}

		echo $table_browse_template['table_end'];

		// End payload block:
		$AdminUI->disp_payload_end();
		break;
}

// Display body bottom, debug info and close </html>:
$AdminUI->disp_global_footer();

?>
