How To Exclude Page Permanently In WordPress Without Using Any Plugin

Excluding page in WordPress is fairly easily when we were given some particular function in WordPress to list out pages  such as wp_list_pages or get_pages where these methods all gives us certain parameter for us to exclude certain  pages that we do not wish to display. And these methods are usually used on WordPress theme although occasional we would put them into WordPress plugin for other reason. However, using these methods will only exclude page on the THAT particular function call which really doesn't suit my need. I want something permanent which excludes every page whenever i use those methods. If you are like me who do not wish to use plugin such as the Exclude Pages plugin by Simon for whatever reason you have (mine was because of efficiency and customization wise), this may be an article that will definitely help you.

Excluding Page Permanently In WordPress

Anything that you wished to permanently done on WordPress will usually associate closely with action or filter hook in WordPress. In this case where we want to exclude page permanently, we will use filter hook.

add_filter('wp_list_pages_excludes', array($this, 'page_filter'));
function page_filter($exclude_array) {
	global $wpdb;
	$table = $wpdb->prefix . "posts";
	$sql = "SELECT ID FROM ".$table." WHERE post_title ='Exclude' OR post_title ='This' OR post_title ='Pages' OR post_title ='Search'";
	$id_array = $wpdb->get_col($sql);
	$exclude_array=array_merge($id_array, $exclude_array);
	return $exclude_array;
}

The method we will be using on our filter would be wp_list_pages_excludes which helps to exclude pages. For my case, i wanted to exclude some of the page in WordPress and when i said some i meant more than one. Hence, i wrote my own sql query and retrieve as an array of id's. This way, we can merge with the one that you or anyone who has already excluded and return to the core function to deal with it. (this way we reduce the number of sql query needed to retrieve the same amount of id's, if we use get_post method there will be 4 additional query instead of one.)

For people who are not familiar to what i am talking about, you can just copy and paste the above code into your function.php file in your theme and change post_title = 'Exclude' and post_title etc. to your own page name instead of Exclude, This and Pages. Cheers~

Disable Automatic Conversion Of URL To HTML Link In WordPress Comment

As usual, i'm busy working on WordPress development and found something missing on the web (may be not much people needs it). So i dig into WordPress core for such information and write this down here in case someone find it useful. I found that WordPress will automatically convert all URL or Email addresses from string to HTML link for all comment text. This is good thing for some users but its not such a good thing for my project. Therefore, i wanted to disable it but couldn't find any resources around the web. So i decideto write this down in case someone like me who wished to disable WordPress automatic conversion of url addresses to html links on the comment text. It's pretty simple to disable this feature. You can remove this feature from WordPress by adding the following sentence to remove the automatic conversion.

			remove_filter('comment_text', 'make_clickable', 9);

Once this is added into your plugin or function.php file in your template, WordPress will not automatic convert all URL it sees to HTML link on the comment text. Have fun 🙂

Multiple Upload Using Single Upload File With jQuery

This is a trick to perform multiple upload using a single upload bar instead of multiple upload bars created by stickman. If you are looking for multiple upload using iframe or asynchronous method, you can visit this simplest way tutorial that explains how iframe method can be done and whether ajax method is available. I first came across stickman article was when i was doing research for multiple upload without having the file uploaded into the server. His idea was brilliant. However, i hardly have the need for such function until recently when my project submit form required multiple upload using a single upload bar again. Stickman method was pure JavaScript which is great and efficient but i needed something from jQuery. Here, i will try to simplify his method into jQuery form.

Concept of Single Multiple Upload File

In order to achieve single multiple upload file, we will have to understand that multiple upload is still possible even without asynchronous upload, that is, we will need multiple upload bar displaying on the screen and having user to click on each upload bar to upload multiple item at once. In stickman concept, its almost the same. The only differences is that he uses one upload bar and insert a new one on top of the existing upload bar AFTER a user has finished selected the item to be uploaded. Therefore it seems like there is only one upload bar. (the process is too fast for our eyes to follow. Thus, we all get trick when this happen) But in reality, all these upload bars were only hidden from user but not from codes. Once, the submit button is pressed, all the visible and non-visible upload bars are being uploaded to the server! This way we get our cheap multiple upload using a single upload bar method.

jQuery Single Multiple Upload File

I won't be displaying stickman code in this article as you can download directly from his website. We can now proceed with the coding after we have understand how stickman method works.

HTML code

All we need on the HTML section are these:

<div>
	<input type="file" name="files[]">
	<div id="upload_list"></div>
</div>

jQuery code

jQuery code is fairly simple. Let's don't complicate stuff. All we need to do is this.

jQuery(document).ready(function($){
var replaceMe = function(){
	var obj = $(this);
	$(obj).css({'position':'absolute','left':'-9999px','display':'none'}).parent().prepend('<input type="file" name="'+obj.attr('name')+'"/>')
	$('#upload_list').append('<div>'+obj.val()+'<input type="button" value="cancel"/><div>');
	$("input[type='file']").change(replaceMe);
	$("input[type='button']").click(function(){
		$(this).parent().remove();
		$(obj).remove();
	});
}
$("input[type='file']").change(replaceMe);
});

The above takes care of the basic. If you wish to restrict the number of upload, you can place a restriction variable.

jQuery(document).ready(function($){
var max = 2;
var replaceMe = function(){
	var obj = $(this);
	if($("input[type='file']").length > max)
	{
		alert('fail');
		obj.val("");
		return false;
	}
	$(obj).css({'position':'absolute','left':'-9999px','display':'none'}).parent().prepend('<input type="file" name="'+obj.attr('name')+'"/>')
	$('#upload_list').append('<div>'+obj.val()+'<input type="button" value="cancel"/><div>');
	$("input[type='file']").change(replaceMe);
	$("input[type='button']").click(function(){
		$(this).parent().remove();
		$(obj).remove();
		return false; //safari fixes
	});
}
$("input[type='file']").change(replaceMe);
});

Where max is the counter that restricts the number of uploads. Since jQuery takes care of most of the complicated things. The code eventually became smaller. For some reason bind doesn't works for the event change. Thus, i will have to attach the event again. From pure JavaScript of 156 line into a few line of codes, jQuery definitely makes developers life much more easier (doesn't means the code produce by jQuery is much more efficient though.).

The Demo

The demo can be found here.

Conclusion

The above jQuery code has simplify the version found on stickman site. Although the display doesn't looks as attractive but my objective has been achieved. Sometimes, JavaScript can be harder to read compared to a jQuery one. However, JavaScript version still has a better efficiency as compared to a jQuery one.

Multiple WordPress installation sharing one user table

Recently my project required me to setup multiple WordPress installation and each WordPress installation does different thing. The only problem that i faced was to have a synchronize login between these three WordPress installation. Short to say, once a user has logged into the system, they will not have to do it again if they decide to proceed to the next WordPress installation.  There are a few great explanation on how this can be done even on WordPress forum. However, it seems like everyone faced a little problem of having to touch the core of WordPress in order for their user to access another WordPress installation without getting the message 'You do not have sufficient permissions to access this page.'.

Requirement

Before i began, you will need to know certain things in order for WordPress to share user table across multiple WordPress installation.

  • All your WordPress installation must be within the same database
  • Each WordPress installation must have different prefix ( or else you are performing overwriting)
  • URL must be within the domain. Multiple domain will have problem authenticating an already logged in user although sharing user table is still possible.
  • WordPress installation version must be similar across the media you wished to share.

Enough of my crap. Let's start with something simple.

Sharing WordPress user table

Our objective here is to share WordPress table across sub domain. Although different domain will also work but your user will have to log in manually on each different domain WordPress installation. (i will explain later) The main thing you will need for each sub domain to use a single user table is to point all your WordPress installation towards one user table. In order to do that, you will have to edit your wp-config.php file on all your sub domain WordPress installation and place the following code into it.

define('CUSTOM_USER_TABLE', 'wp_users');
define('CUSTOM_USER_META_TABLE', 'wp_usermeta');

where wp_ is your WordPress installation prefix, change it to your own prefix. The above constant variable (or code) is telling your WordPress installation that we won't be needing any users and usermeta table. We will be using the one defined above. Hence, any user who login from different installation will all see the same table. (sharing user has accomplished)

Login once only

Pointing everyone to one table is pretty simple. Next we want our user only login once and every time when they visit a new WordPress installation blog. In this case, we will have to further add in some code into ALL wp-config.php file. (although you don't need all but let's play safe)

Dump the following code into all of your wp-config.php file (including the main and sub installation).

$baseurl = 'http://www.domain.com'; // replace with the actual domain name for your root site
$cookiehash = md5($baseurl);
define('COOKIEHASH', $cookiehash);
define ('AUTH_COOKIE', 'wordpress_'.COOKIEHASH);
define ('SECURE_AUTH_COOKIE', 'wordpress_sec_'.COOKIEHASH);
define ('LOGGED_IN_COOKIE','wordpress_logged_in_'.COOKIEHASH);
define ('TEST_COOKIE', 'wordpress_test_cookie');

define('COOKIE_DOMAIN', '.domain.com'); // change domain with your root site
define('COOKIEPATH', '/');

 define('AUTH_KEY',        'to be generated at https://api.wordpress.org/secret-key/1.1/');
 define('SECURE_AUTH_KEY','to be generated at https://api.wordpress.org/secret-key/1.1/');
 define('LOGGED_IN_KEY',  'to be generated at https://api.wordpress.org/secret-key/1.1/');
 define('NONCE_KEY',       'to be generated at https://api.wordpress.org/secret-key/1.1/');

 define('AUTH_SALT',   'can use one of the key on https://api.wordpress.org/secret-key/1.1/');
 define('LOGGED_IN_SALT',       'can use one of the key on  https://api.wordpress.org/secret-key/1.1/');

The above will be adviced to be placed on all of your WordPress installation that you wished to have an one time login feature. Please take note if there is a constant variable or code similar on your wp-config.php file, please remove them. The objective of having the above code similar across each WordPress installation is so that each installation can authenticate your user login detail correctly across different installation.

Now, take note of the cookie constant variable. WordPress uses that to authenticate your user login and cookie authentication cannot do cross domain validation. And this is the reason why different domain couldn't achieve one time login feature.

Explanation on what's going on

I tried very hard to explain in least technical aspect as possible but i still thinks it is quite technical so allow me to summarize a bit here.

  • Firstly we point all WordPress installation to the main WordPress installation user table where all user login details are located.
  • Secondly, we copy the required code for one time login to all WordPress installation including the main WordPress installation in order for all WordPress installation to proceed the same way to identify a user (it's like everyone holding the same key to open one lock where the lock refer to the user)
  • Thirdly, 'You do not have sufficient permissions to access this page.' message bugs you. (it's a good thing)

Alright, this should be it.

Solve the problem of You do not have sufficient permissions to access this page

Soon, you figure when you try to login from other WordPress installation using other WordPress installation user detail, you receive the message  'You do not have sufficient permissions to access this page.'. If you manage to get this message after you have logged in to the main blog and directly access your sub ones. This means that one time login has successfully implemented. The reason why this is happening is because within WordPress core code, it is checking your user capability with the prefix that you have install on that WordPress installation and not the one used on our user table.

Example, main blog A uses prefix wp_ and sub blog B uses prefix wpp_. You tried to login from blog B and you get the error message because on blog B installation, it is checking wpp_ instead of wp_ .

This logic resist within the core code on wp-include/capabilities.php and you will have to dig into the core in order to fix this problem or manually add the record yourself every time a new user register.

If you select the first option to manually insert the record on other sub domain, you will go to your usermeta table and insert the following record.

wp-multiple-installation-one-user

Or you may want to change the logic in your core implementation so that all your sub domain knows the correct usermeta table to look into. If you do, change all sub domain WordPress installation capabilities.php file located at wp-include/capabilities.php line 514 where you will see this.

	function _init_caps() {
		global $wpdb;
		$this->cap_key = $wpdb->prefix . 'capabilities';
		$this->caps = &$this->{$this->cap_key};
		if ( ! is_array( $this->caps ) )
			$this->caps = array();
		$this->get_role_caps();
	}

On the comment above you will notice the trouble maker. Now, hardcode the prefix to the main domain to this instead for all your sub domain. (because everyone is sharing the same user table, we won't have the detail of their unique prefix + user_roles table.)

//$this->cap_key = $wpdb->prefix . 'capabilities'; //this is the trouble maker
$this->cap_key = 'wp_capabilities'; //this is the trouble maker

this will do the trick but you will have to change this every time wordpress update 🙁

I have another personal solution which doesn't need to do both or required to set a default permission type for all user who do not have access across sub domain. However, there is a problem with it that is why i can't release it at the moment (although it's really a small problem) but let me fix it and release for you guys in the near future 🙂

Security

Having the same user to access across different WordPress installation is great but doing this might degrade the strength of each WordPress installation. The security concern lays on the one time login implementation and not how each WordPress installation is using the same user table. Since one time login required every security measurement to be same across different WordPress installation, this means that only one installation is required to be broken in order to access all other sub domain. Short to say, instead of multiple wall of defends for each implementation. Now, we have just one.

**** UPDATE *****

Unfortunately, the above method doesn't work anymore for latest WordPress version (hence, stop your research and keep reading the same appraoch). In order to get auto login in multiple wordpress sub domain sites with multiple installation, another approach has to be done. It is still possible to get all users to login automatically across every website and sharing user login without using CUSTOM_USER_TABLE. It is more reliable and you do not have to combine databases together. However, this approach require a bit of bypassing and modification certain behavior which i will try to write one out if i get the time. Do dig deep into the core code to understand what you guys might think as an 'easy' approach might not be that easy anymore.

Sorting post according to post tag in WordPress

It's been quite a while since i write new post on Hungred Dot Com as i got stuck with my Malay language examination (sigh..) and other project that i'm working on. And recently i was trying to sort one of my WordPress theme according to its post tag and found that there is no easy way to sort your post tag in prioritize or customization ways. Although i manage to make it work on the WordPress theme. It still required additional query to the database although i tried to minimized it. Well, i guess you will have to pay the price for things that WordPress doesn't support i guess. Nonetheless, this is the optimized version of how i get it done on my WordPress theme.

Getting Started

Before i start, let me explain what i am sorting here. Basically, i have only two post tag which are 'sold out' and 'available' and i'm sorting it according to this sequence.

  1. Sticky post
  2. Available tag
  3. Sold out tag
  4. Others

The objective is pretty simple but working on the code require a bit more work than i though. Let's see how do i simplify the explanation so that all of us can understand. I'll just split them in 4 section as shown on my sorting sequence. Let's pray that we all get it. (hahaha..)

Sorting sticky post

The starting sort which is also the one who will initialize all variables (so that we don't enquire the database twice) is located on the first sorting sequence.

<?php
global $wp_query;
$tmp = $wp_query->query;	// backup the original query
$paged = (get_query_var('paged')) ? get_query_var('paged') : 1;	//determine the current page number
$sticky = get_option('sticky_posts');	//get all sticky post from db
$num2display = get_option('posts_per_page');	//get post per page from db
$numHadDisplay = 0;		//display post counter
$maxnumofpost = 0;		//maximum post counter
$args = array(
	'post__in'  => $sticky, // we only want to see sticky post
	'orderby'=>'date',
	'order'=>'DESC',
	'paged' => $paged,
	'showposts' => ($num2display),
	'offset' => (($paged-1)*$num2display)	//we need to tell our query the offset so that it will display correctly on subsequence page
);
 query_posts(array_merge($args,$wp_query->query));	//merge my query with the original one (don't have to do this, its up to you)
 $numHadDisplay += $wp_query->post_count;	//increment the post that has displayed
 $maxnumofpost += $wp_query->found_posts;	//increment the maximum post on this section

I have commented above so that you guys can figure out what the heck am i doing. I'm basically initializing all the things i need on the top to avoid redundant call to the database. Once you have finish this, the next bit of code is to print them out on the screen. This bits of code are the same for all print out after the logic has been applied.

	<?php if (have_posts()) : ?>
		<?php while (have_posts()) : the_post(); ?>
			<div class="post" id="post-<?php the_ID(); ?>">
				<div class="commentP">
					<div class="commentC">
					<?php comments_popup_link('0', '1', '%'); ?>
					</div>
				</div>

				<h2 class="home_title"><a href="<?php the_permalink() ?>" rel="bookmark" title="Permanent Link to <?php the_title_attribute(); ?>"><?php the_title(); ?></a></h2>
				<p class="byline"><small>
					by <?php the_author() ?>, 				<?php the_time('F jS') ?> • 				<?php the_category(' •  ') ?> • 				<?php edit_post_link('Edit', '', '  •  '); ?>
				</small></p>
				<div class="entry">
					<?php the_content('<div id="moretag">MORE »</div>');?>
				</div>
				<p class="tags"><?php the_tags('Item Status: ', ', ', '<br />'); ?></p>
			</div>
		<?php endwhile; ?>
	<?php endif; ?>

It's just a loop to print out everything. It should be different from your theme. (looping should be the same though)

Sorting available post tag

NOW, i'm hoping that the first sorting part doesn't confuse you. If it does, then you should stop reading and start thinking of your own logic. But it should be alright for most of you. The next page should get a bit more challenging.

<?php
if($numHadDisplay < $num2display){	//check whether the previous tag post has enough to print (too little, we will need to help to fill up the gap
if($maxnumofpost%$num2display == 0)	//we will need to have decimal to determine the correct page
$maxnumofpost++;
$maxpage = ceil($maxnumofpost/$num2display);	//find the maximum page the previous tag post sections can get
$offset = 0;
if(($paged-$maxpage) == 0)			//although we are at n page where n can be any number, this section might just run the first time. Hence, first page.
$offset = 0;
else if($paged >= 1)		//subsequence page we will calculate the number of post we have printed to fill up the grap on the previous page PLUS the number that had already display to get the starting index
$offset = ($num2display-($maxnumofpost)%$num2display) + ((($paged-$maxpage) -1 )*$num2display);

$args = array(
	'tag'=> "available",
	'post__not_in'  => $sticky,
	'orderby'=>'date',
	'order'=>'DESC',
	'paged' => ($paged-$maxpage),	// this is the starting page (we do not follow the current page because each section start page is different)
	'showposts' => ($num2display - $numHadDisplay),	//show this amount of post
	'caller_get_posts'=>1,	//do not show any sticky post
	'offset' => $offset		//get the record after this offset
);
 query_posts($args);
 $numHadDisplay += $wp_query->post_count;
 $maxnumofpost += $wp_query->found_posts;
?>

The above is pretty confusing but this is all the logic we need for all subsequence section. In here, you must understand that we are trying to prioritize each tag to display first. Hence, each level in your priority list will initialize at different time. Therefore, we need to calculate what is the starting offset of that part, number of post to display and the correct page to display at that time.

After the above logic has been applied, you will perform another print out on the screen as shown previously.

Sorting sold out post tag

Like i mention on the previous section. That is all the logic we need. Hence, the code for this section differ only at the query level.

<?php
if($numHadDisplay < $num2display){
if($maxnumofpost%$num2display == 0)
$maxnumofpost++;
$maxpage = ceil($maxnumofpost/$num2display);
global $wp_query;
$offset = 0;
if(($paged-$maxpage) == 0)
$offset = 0;
else if($paged >= 1)
$offset = ($num2display-($maxnumofpost)%$num2display) + ((($paged-$maxpage) -1 )*$num2display);

$args = array(
	'tag'=> "sold-out", // it's sold out instead
	'post__not_in'  => $sticky,
	'orderby'=>'date',
	'order'=>'DESC',
	'paged' => ($paged-$maxpage),
	'showposts' => ($num2display - $numHadDisplay),
	'caller_get_posts'=>1,
	'offset' => $offset
);
 query_posts($args);
 $numHadDisplay += $wp_query->post_count;
 $maxnumofpost += $wp_query->found_posts;
?>

Then, we will just output all the things out as usual as shown on the sticky section.

Sorting other post

Once, we display all the tag we want, other not so important post will be thrown at the back.

<?php
if($numHadDisplay < $num2display){
if($maxnumofpost%$num2display == 0)
$maxnumofpost++;
global $wp_query;
$maxpage = ceil($maxnumofpost/$num2display);
$soldout = get_term_by('slug','sold-out','post_tag');
$available = get_term_by('slug','available','post_tag');
$offset = 0;
if(($paged-$maxpage) == 0)
$offset = 0;
else if($paged >= 1)
$offset = ($num2display-($maxnumofpost)%$num2display) + ((($paged-$maxpage) -1 )*$num2display);

// echo "max page = ". $maxpage . "<br/>";
// echo "previous printed = " . ($num2display-($maxnumofpost)%$num2display) . "<br/>";
// echo "added to printed = ". ((($paged-$maxpage) -1 )*$num2display) . "<br/>";
// echo "max num of post = ".$maxnumofpost . "<br/>";
// echo "page = ". ($paged-$maxpage) . "<br/>";
// echo "offset = ". ($offset) . "<br/>";
// echo "showposts = ".  ($num2display - $numHadDisplay) . "<br/>";

$args = array(
	'post__not_in'  => $sticky, // do not display sticky post
	'tag__not_in'=> array($available->term_id, $soldout->term_id), // do not display sold out and available tag post
	'orderby'=>'date',
	'order'=>'DESC',
	'paged' => ($paged-$maxpage),
	'showposts' => ($num2display - $numHadDisplay),
	'caller_get_posts'=>1,
	'offset' => $offset
);
 query_posts(array_merge($args));
 $numHadDisplay += $wp_query->post_count;
 $maxnumofpost += $wp_query->found_posts;
?>

As usual, print them out once the above has initialize.

Fix pagination

Once you have done all that, you will notice that your WordPress theme page is wrong as it takes the last query total page. Hence, you will need to call the backup query that we did previous to display the proper maximum page our site have.

<?php  query_posts($tmp); ?>

That's it!

Full Sorting code

In case you can't follow without the full code. Here is the one i have 🙂

<?php get_header(); ?>
<!-- start #content -->
<div id="page">
	<div id="content">
<?php
global $wp_query;
$tmp = $wp_query->query;	// backup the original query
$paged = (get_query_var('paged')) ? get_query_var('paged') : 1;	//determine the current page number
$sticky = get_option('sticky_posts');	//get all sticky post from db
$num2display = get_option('posts_per_page');	//get post per page from db
$numHadDisplay = 0;		//display post counter
$maxnumofpost = 0;		//maximum post counter
$args = array(
	'post__in'  => $sticky,
	'orderby'=>'date',
	'order'=>'DESC',
	'paged' => $paged,
	'showposts' => ($num2display),
	'offset' => (($paged-1)*$num2display)	//start query
);
 query_posts(array_merge($args,$wp_query->query));	//merge my query with the original one (don't have to do this, its up to you)
 $numHadDisplay += $wp_query->post_count;	//increment the post that has displayed
 $maxnumofpost += $wp_query->found_posts;	//increment the maximum post on this section
?>
	<?php if (have_posts()) : ?>
		<?php while (have_posts()) : the_post(); ?>
			<div class="post" id="post-<?php the_ID(); ?>">
				<div class="commentP">
					<div class="commentC">
					<?php comments_popup_link('0', '1', '%'); ?>
					</div>
				</div>

				<h2 class="home_title"><a href="<?php the_permalink() ?>" rel="bookmark" title="Permanent Link to <?php the_title_attribute(); ?>"><?php the_title(); ?></a></h2>
				<p class="byline"><small>
					by <?php the_author() ?>, 				<?php the_time('F jS') ?> • 				<?php the_category(' •  ') ?> • 				<?php edit_post_link('Edit', '', '  •  '); ?>
				</small></p>
				<div class="entry">
					<?php the_content('<div id="moretag">MORE »</div>');?>
				</div>
				<p class="tags"><?php the_tags('Item Status: ', ', ', '<br />'); ?></p>
			</div>
		<?php endwhile; ?>
	<?php endif; ?>
<?php
if($numHadDisplay < $num2display){	//check whether the previous tag post has enough to print (too little, we will need to help to fill up the gap
if($maxnumofpost%$num2display == 0)	//we will need to have decimal to determine the correct page
$maxnumofpost++;
$maxpage = ceil($maxnumofpost/$num2display);	//find the maximum page the previous tag post sections can get
$offset = 0;
if(($paged-$maxpage) == 0)			//although we are at n page where n can be any number, this section might just run the first time. Hence, first page.
$offset = 0;
else if($paged >= 1)		//subsequence page we will calculate the number of post we have printed to fill up the grap on the previous page PLUS the number that had already display to get the starting index
$offset = ($num2display-($maxnumofpost)%$num2display) + ((($paged-$maxpage) -1 )*$num2display);

$args = array(
	'tag'=> "available",
	'post__not_in'  => $sticky,
	'orderby'=>'date',
	'order'=>'DESC',
	'paged' => ($paged-$maxpage),	// this is the starting page (we do not follow the current page because each section start page is different)
	'showposts' => ($num2display - $numHadDisplay),	//show this amount of post
	'caller_get_posts'=>1,	//do not show any sticky post
	'offset' => $offset		//get the record after this offset
);
 query_posts($args);
 $numHadDisplay += $wp_query->post_count;
 $maxnumofpost += $wp_query->found_posts;
?>
	<?php if (have_posts()) : ?>
		<?php while (have_posts()) : the_post(); ?>
			<div class="post" id="post-<?php the_ID(); ?>">
				<div class="commentP">
					<div class="commentC">
					<?php comments_popup_link('0', '1', '%'); ?>
					</div>
				</div>

				<h2 class="home_title"><a href="<?php the_permalink() ?>" rel="bookmark" title="Permanent Link to <?php the_title_attribute(); ?>"><?php the_title(); ?></a></h2>
				<p class="byline"><small>
					by <?php the_author() ?>, 				<?php the_time('F jS') ?> • 				<?php the_category(' •  ') ?> • 				<?php edit_post_link('Edit', '', '  •  '); ?>
				</small></p>
				<div class="entry">
					<?php the_content('<div id="moretag">MORE »</div>');?>
				</div>
				<p class="tags"><?php the_tags('Item Status: ', ', ', '<br />'); ?></p>
			</div>
		<?php endwhile; ?>
	<?php endif; ?>
<?php
}
if($numHadDisplay < $num2display){
if($maxnumofpost%$num2display == 0)
$maxnumofpost++;
$maxpage = ceil($maxnumofpost/$num2display);
global $wp_query;
$offset = 0;
if(($paged-$maxpage) == 0)
$offset = 0;
else if($paged >= 1)
$offset = ($num2display-($maxnumofpost)%$num2display) + ((($paged-$maxpage) -1 )*$num2display);

$args = array(
	'tag'=> "sold-out",
	'post__not_in'  => $sticky,
	'orderby'=>'date',
	'order'=>'DESC',
	'paged' => ($paged-$maxpage),
	'showposts' => ($num2display - $numHadDisplay),
	'caller_get_posts'=>1,
	'offset' => $offset
);
 query_posts($args);
 $numHadDisplay += $wp_query->post_count;
 $maxnumofpost += $wp_query->found_posts;
?>
	<?php if (have_posts()) : ?>
		<?php while (have_posts()) : the_post(); ?>
			<div class="post" id="post-<?php the_ID(); ?>">
				<div class="commentP">
					<div class="commentC">
					<?php comments_popup_link('0', '1', '%'); ?>
					</div>
				</div>

				<h2 class="home_title"><a href="<?php the_permalink() ?>" rel="bookmark" title="Permanent Link to <?php the_title_attribute(); ?>"><?php the_title(); ?></a></h2>
				<p class="byline"><small>
					by <?php the_author() ?>, 				<?php the_time('F jS') ?> • 				<?php the_category(' •  ') ?> • 				<?php edit_post_link('Edit', '', '  •  '); ?>
				</small></p>
				<div class="entry">
					<?php the_content('<div id="moretag">MORE »</div>');?>
				</div>
				<p class="tags"><?php the_tags('Item Status: ', ', ', '<br />'); ?></p>
			</div>
		<?php endwhile; ?>
	<?php endif; ?>
<?php
}
if($numHadDisplay < $num2display){
if($maxnumofpost%$num2display == 0)
$maxnumofpost++;
global $wp_query;
$maxpage = ceil($maxnumofpost/$num2display);
$soldout = get_term_by('slug','sold-out','post_tag');
$available = get_term_by('slug','available','post_tag');
$offset = 0;
if(($paged-$maxpage) == 0)
$offset = 0;
else if($paged >= 1)
$offset = ($num2display-($maxnumofpost)%$num2display) + ((($paged-$maxpage) -1 )*$num2display);

// echo "max page = ". $maxpage . "<br/>";
// echo "previous printed = " . ($num2display-($maxnumofpost)%$num2display) . "<br/>";
// echo "added to printed = ". ((($paged-$maxpage) -1 )*$num2display) . "<br/>";
// echo "max num of post = ".$maxnumofpost . "<br/>";
// echo "page = ". ($paged-$maxpage) . "<br/>";
// echo "offset = ". ($offset) . "<br/>";
// echo "showposts = ".  ($num2display - $numHadDisplay) . "<br/>";

$args = array(
	'post__not_in'  => $sticky,
	'tag__not_in'=> array($available->term_id, $soldout->term_id),
	'orderby'=>'date',
	'order'=>'DESC',
	'paged' => ($paged-$maxpage),
	'showposts' => ($num2display - $numHadDisplay),
	'caller_get_posts'=>1,
	'offset' => $offset
);
 query_posts($args);
 $numHadDisplay += $wp_query->post_count;
 $maxnumofpost += $wp_query->found_posts;
?>
	<?php if (have_posts()) : ?>
		<?php while (have_posts()) : the_post(); ?>
			<div class="post" id="post-<?php the_ID(); ?>">
				<div class="commentP">
					<div class="commentC">
					<?php comments_popup_link('0', '1', '%'); ?>
					</div>
				</div>

				<h2 class="home_title"><a href="<?php the_permalink() ?>" rel="bookmark" title="Permanent Link to <?php the_title_attribute(); ?>"><?php the_title(); ?></a></h2>
				<p class="byline"><small>
					by <?php the_author() ?>, 				<?php the_time('F jS') ?> • 				<?php the_category(' •  ') ?> • 				<?php edit_post_link('Edit', '', '  •  '); ?>
				</small></p>
				<div class="entry">
					<?php the_content('<div id="moretag">MORE »</div>');?>
				</div>
				<p class="tags"><?php the_tags('Item Status: ', ', ', '<br />'); ?></p>
			</div>
		<?php endwhile; ?>

	<?php else : ?>
		<div class="post">
			<h2 class="center">Not Found</h2>
			<p class="center">Sorry, we can't find the thing you are looking for. Please try to search for it.</p>
		</div>
		<?php include (TEMPLATEPATH . "/searchform.php"); ?>
	<?php endif; ?>
<?php } ?>
<?php  query_posts($tmp); ?>
		<div class="navigation post">
			<div class="alignleft"><?php if(function_exists('wp_pagenavi')) { wp_pagenavi(); } ?>  </div>
		</div>
	</div>
<?php get_sidebar(); ?>
</div>
<!-- end #content -->
<?php get_menu(); ?>
<?php get_footer(); ?>

And if you still not convince that it work, you can have a look at this store.looliwun.com.

Summary

The challenging part on sorting post tag is that you will have to take care of each sequence logic. In this logic that i have, i have only added around 5 WordPress query to get what i want. (let's don't calculate what WordPress did inside, hahaha..) But it depends on what you want, you might try other ways to achieve the same result. Enjoy 🙂

P.S: you can further optimize this by blocking access to each section after there is no post to be displayed for some of the top logic. Example, sticky has 1 post, available have 2 post, sold out have 100 post. In this case, after 2nd page, sticky and available post should have ended but we still enquire the database to check. This can be optimized. 🙂