<?php
/*
Plugin Name: Fuzzy Recent Posts
Plugin URI: http://www.semiologic.com/software/recent-posts/
Description: <a href="http://www.semiologic.com/legal/license/">Terms of use</a> &bull; <a href="http://www.semiologic.com/software/recent-posts/">Doc/FAQ</a> &bull; <a href="http://wordpress.org/tags/semiologic">Support forum</a> &#8212; Lists a fuzzy number of recently posted entries. To use, call the_recent_posts(); where you want the tile to appear. Alternatively, do nothing and the tile will display when wp_meta(); is called.
Author: Denis de Bernardy
Version: 4.2
Author URI: http://www.semiologic.com
*/

/*
Terms of use
------------

This software is copyright Mesoconcepts Ltd, and is distributed under the terms of the Mesoconcepts license. In a nutshell, you may freely use it for any purpose, but may not redistribute it without written permission.

http://www.semiologic.com/legal/license/
**/


load_plugin_textdomain('sem-recent-posts');

if ( !defined('sem_cache_path') )
{
	define('sem_cache_path', ABSPATH . 'wp-content/cache/'); # same as wp-cache
}
if ( !defined('sem_cache_timeout') )
{
	define('sem_cache_timeout', 3600); # one hour
}


class sem_recent_posts
{
	#
	# Variables
	#
	
	var $params = array(
			'min_num' => false,			# integer, set to false to disable
			'max_num' => false,			# integer, set to false to disable
			'min_days' => false,		# integer, set to false to disable
			'max_days' => false,		# integer, set to false to disable
			'num_days' => 3,			# integer, set to false to disable
			'inc_posts' => true,		# boolean
			'inc_pages' => false,		# boolean
			'show_date' => false		# boolean
			);
	
	var $captions = array(
			'recently_posted' => 'Other recent news'
			);
	
	
	#
	# Constructor
	#
	
	function sem_recent_posts()
	{
		$this->cache_file = sem_cache_path . 'sem-recent-posts';
		
		$params = get_settings('sem_recent_posts_params');
		
		if ( $params )
		{
			foreach ( $params as $key => $value )
			{
				$this->params[$key] = $value;
			}
		}
		else
		{
			update_option('sem_recent_posts_params', $this->params);
		}
		
		if ( isset($_GET['action'])
			&& in_array($_GET['action'], array('flush', 'flush_cache'))
			)
		{
			$this->flush_cache();
		}
		
		add_action('wp_meta', array(&$this, 'auto_display'), 6);
		add_action('admin_menu', array(&$this, 'add2admin_menu'));
		
		add_action('publish_post', array(&$this, 'flush_cache'), 0);
		add_action('save_post', array(&$this, 'flush_cache'), 0);
		add_action('edit_post', array(&$this, 'flush_cache'), 0);
		add_action('delete_post', array(&$this, 'flush_cache'), 0);
		add_action('publish_phone', array(&$this, 'flush_cache'), 0);
		#add_action('generate_rewrite_rules', array(&$this, 'flush_cache'), 0);
		add_action('init', array(&$this, 'init'));
	} # end sem_recent_posts()
	
	
	#
	# init()
	#
	
	function init()
	{
		global $sem_theme_captions;
		
		if ( isset($sem_theme_captions) )
		{
			$this->captions = $sem_theme_captions->register($this->captions);
		}
		else
		{

			array_walk($this->captions, '__');
		}
	} # end init()
	
	
	#
	# flush_cache()
	#
	
	function flush_cache()
	{
		if ( is_writable(sem_cache_path) )
		{
			$cache_files = glob(sem_cache_path . "*");
			
			if ( $cache_files )
			{
				foreach ( $cache_files as $cache_file )
				{
					if ( is_file($cache_file) && is_writable($cache_file) )
					{
						unlink( $cache_file );
					}
				}
			}
		}
	} # end flush_cache()
	
	
	#
	# add2admin_menu()
	#
	
	function add2admin_menu()
	{
		add_options_page(
				__('Recent&nbsp;Posts', 'sem-recent-posts'),
				__('Recent&nbsp;Posts', 'sem-recent-posts'),
				8,
				str_replace("\\", "/", __FILE__),
				array(&$this, 'display_admin_page')
				);
	} # end add2admin_menu()
	
	
	#
	# update()
	#
	
	function update()
	{
		$this->params = array();
		
		#echo '<pre>';
		#var_dump($_POST);
		#echo '</pre>';
		
		switch ( $_POST['config_type'] )
		{
		case 'fuzzy':
			$this->params['min_num'] = intval($_POST['min_num']);
			$this->params['max_num'] = intval($_POST['max_num']);
			$this->params['min_days'] = intval($_POST['min_days']);
			$this->params['max_days'] = intval($_POST['max_days']);
			$this->params['num_days'] = false;
			break;
		case 'fixed_entries':
			if ( !intval($_POST['num_entries']) )
			{
				$_POST['num_entries'] = 10;
			}
			$this->params['min_num'] = intval($_POST['num_entries']);
			$this->params['max_num'] = intval($_POST['num_entries']);
			$this->params['min_days'] = false;
			$this->params['max_days'] = false;
			$this->params['num_days'] = false;
			break;
		case 'fixed_days':
			if ( !intval($_POST['num_days']) )
			{
				$_POST['num_days'] = 3;
			}
			$this->params['min_num'] = false;
			$this->params['max_num'] = false;
			$this->params['min_days'] = false;
			$this->params['max_days'] = false;
			$this->params['num_days'] = intval($_POST['num_days']);
			break;
		case 'fixed_days_ago':
			if ( !intval($_POST['num_days_ago']) )
			{
				$_POST['num_days_ago'] = 7;
			}
			$this->params['min_num'] = false;
			$this->params['max_num'] = false;
			$this->params['min_days'] = intval($_POST['num_days_ago']);
			$this->params['max_days'] = intval($_POST['num_days_ago']);
			$this->params['num_days'] = false;
			break;
		}
		
		switch ( $_POST['recent_type'] )
		{
		case 'inc_posts':
			$this->params['inc_posts'] = true;
			$this->params['inc_pages'] = false;
			break;
		case 'inc_pages':
			$this->params['inc_posts'] = false;
			$this->params['inc_pages'] = true;
			break;
		case 'inc_entries':
			$this->params['inc_posts'] = true;
			$this->params['inc_pages'] = true;
			break;
		}
		
		if ( isset($_POST['show_date']) )
		{
			$this->params['show_date'] = true;
		}
		else
		{
			$this->params['show_date'] = false;
		}
		
		foreach ( $this->params as $key => $val )
		{
			switch ( $key )
			{
			case 'inc_posts':
			case 'inc_pages':
				break;
			default:
				if ( $val == 0 )
				{
					$this->params[$key] = false;
				}
				break;
			}
		}
		
		if ( !$this->params['min_num'] && !$this->params['max_num']
			&& !$this->params['min_days'] && !$this->params['max_days']
			&& !$this->params['num_days']
			)
		{
			$this->params['num_days'] = 3;
		}
		
		if ( !$this->params['inc_posts'] && !$this->params['inc_pages'] )
		{
			$this->params['inc_posts'] = true;
		}
		
		update_option('sem_recent_posts_params', $this->params);
		
		$this->flush_cache();
	} # end update()
	
	
	#
	# display_admin_page()
	#
	
	function display_admin_page()
	{
		# Process updates, if any
		
		if ( isset($_POST['action'])
			&& ( $_POST['action'] == 'update_sem_recent_posts' )
			)
		{
			$this->update();
			
			echo "<div class=\"updated\">\n"
				. "<p>"
					. "<strong>"
					. __('Options saved.', 'sem-recent-posts')
					. "</strong>"
				. "</p>\n"
				. "</div>\n";
		}
		
		# Display admin page
		
		echo "<div class=\"wrap\">\n"
			. "<h2>" . __('Recent Posts Options', 'sem-recent-posts') . "</h2>\n"
			. "<form method=\"post\" action=\"\">\n"
			. "<input type=\"hidden\" name=\"action\" value=\"update_sem_recent_posts\" />\n";
		
		echo "<fieldset class=\"options\">\n"
			. "<legend>" . __('Display Options', 'sem-recent-posts') . "</legend>\n";
		
		echo "<p style=\"padding-bottom: 6px;\">"
				. "<label for=\"show_date\">"
				. "<input type=\"checkbox\" id=\"show_date\""
				. " name=\"show_date\""
				. ( $this->params['show_date']
					? " checked=\"checked\""
					: ""
					)
				. " /> " . __('Show date', 'sem-recent-posts')
				. "</label>"
				. "</p>\n";
		
		echo "<p style=\"padding-bottom: 6px;\">" . __('Show recently posted', 'sem-recent-posts')
				. ":"
				. " <label for=\"show_recent_posts\">"
				. "<input type=\"radio\" id=\"show_recent_posts\""
					. " name=\"recent_type\" value=\"inc_posts\""
					. ( ( $this->params['inc_posts'] && !$this->params['inc_pages'] )
						? " checked=\"checked\""
						: ""
						)
					. " /> " . __('Posts', 'sem-recent-posts')
				. "</label>"
				. " <label for=\"show_recent_pages\">"
				. "<input type=\"radio\" id=\"show_recent_pages\""
					. " name=\"recent_type\" value=\"inc_pages\""
					. ( ( !$this->params['inc_posts'] && $this->params['inc_pages'] )
						? " checked=\"checked\""
						: ""
						)
					. " /> " . __('Pages', 'sem-recent-posts')
				. "</label>"
				. " <label for=\"show_recent_entries\">"
				. "<input type=\"radio\" id=\"show_recent_entries\""
					. " name=\"recent_type\" value=\"inc_entries\""
					. ( ( $this->params['inc_posts'] && $this->params['inc_pages'] )
						? " checked=\"checked\""
						: ""
						)
					. " /> " . __('Both', 'sem-recent-posts')
				. "</label>"
			. "</p>\n";
		
		echo "</fieldset>\n";
		
		echo "<fieldset class=\"options\">\n"
			. "<legend>" . __('Fuzziness Options', 'sem-recent-posts') . "</legend>\n";
		
		echo "<table>\n"
			. "<tr valign=\"top\">\n"
			. "<td style=\"width: 40%;\">"
				. "<p><label for=\"config_type_fixed_days\">"
					. "<input type=\"radio\" id=\"config_type_fixed_days\""
					. " name=\"config_type\" value=\"fixed_days\""
					. ( ( $this->params['num_days'] )
						? " checked=\"checked\""
						: ""
						)
					. " /> " . __('Fixed number of days', 'sem-recent-posts') . "</label></p>\n"
			. "</td>\n"
			. "<td>"
				. "<p><label for=\"mum_days\">"
					. "<input type=\"text\" size=\"2\""
					. " id=\"num_days\" name=\"num_days\""
					. " value=\""
						. ( $this->params['num_days']
							? $this->params['num_days']
							: 3
							)
						. "\""
						. " onchange=\"if ( this.value ) this.form.config_type[0].checked = true;\""
						. "> " . __('Days', 'sem-recent-posts') . "</label></p>\n"
			. "</td>\n"
			. "</tr>\n"
			. "<tr valign=\"top\">\n"
			. "<td style=\"width: 40%;\">"
				. "<p><label for=\"config_type_fixed_days_ago\">"
					. "<input type=\"radio\" id=\"config_type_fixed_days_ago\""
					. " name=\"config_type\" value=\"fixed_days_ago\""
					. ( ( $this->params['min_days']
							&& ( $this->params['min_days'] == $this->params['max_days'] )
							)
						? " checked=\"checked\""
						: ""
						)
					. " /> " . __('Fixed number of days ago', 'sem-recent-posts') . "</label></p>\n"
			. "</td>\n"
			. "<td>"
				. "<p><label for=\"num_days_ago\">"
					. "<input type=\"text\" size=\"2\""
					. " id=\"num_days_ago\" name=\"num_days_ago\""
					. " value=\""
						. ( ( $this->params['min_days']
								&& ( $this->params['min_days'] == $this->params['max_days'] )
								)
							? $this->params['min_days']
							: 7
							)
						. "\""
						. " onchange=\"if ( this.value ) this.form.config_type[1].checked = true;\""
						. "> " . __('Days ago', 'sem-recent-posts') . "</label></p>\n"
			. "</td>\n"
			. "</tr>\n"
			. "<tr valign=\"top\">\n"
			. "<td style=\"width: 40%;\">"
				. "<p><label for=\"config_type_fixed_entries\">"
					. "<input type=\"radio\" id=\"config_type_fixed_entries\""
					. " name=\"config_type\" value=\"fixed_entries\""
					. ( ( $this->params['min_num']
							&& ( $this->params['min_num'] == $this->params['max_num'] )
							)
						? " checked=\"checked\""
						: ""
						)
					. " /> " . __('Fixed number of entries', 'sem-recent-posts') . "</label>"
					. "</p>\n"
			. "</td>\n"
			. "<td>"
				. "<p><label for=\"num_entries\">"
					. "<input type=\"text\" size=\"2\""
					. " id=\"num_entries\" name=\"num_entries\""
					. " value=\""
						. ( ( $this->params['min_num']
								&& ( $this->params['min_num'] == $this->params['max_num'] )
								)
							? $this->params['min_num']
							: 10
							)
						. "\""
						. " onchange=\"if ( this.value ) this.form.config_type[2].checked = true;\""
						. "> ". __('Entries', 'sem-recent-posts') . "</label>"
						. "</p>\n"
			. "</td>\n"
			. "</tr>\n"
			. "</table>\n";
		
		echo "</fieldset>\n";
		
		
		echo "<p class=\"submit\">"
			. "<input type=\"submit\""
				. " value=\"" . __('Update Options', 'sem-recent-posts') . "\""
				. " />"
			. "</p>\n";
		
		echo "</form>"
			. "</div>\n";
	} # end display_admin_page()
	
	
	#
	# display()
	#
	
	function display($args = null)
	{
		global $wpdb;
		
		
		# disable auto_display
		if ( !defined('sem_recent_posts_displayed') )
		{
			define('sem_recent_posts_displayed', true);
		}
		
		# fetch params
		
		if ( isset($args) )
		{
			parse_str($args, $params);
		}
		else
		{
			$params = array();
		}
		
		# default params
		
		foreach ( $this->params as $key => $val )
		{
			if ( !isset($params[$key]) )
			{
				$params[$key] = $this->params[$key];
			}
		}
		
		if ( !isset($params['title']) )
		{
			$params['title'] = $this->captions['recently_posted'];
		}
		
		# process booleans params
		
		foreach ( $params as $key => $val )
		{
			switch ( $key )
			{
			case 'inc_posts':
			case 'inc_pages':
			case 'show_date':
				if ( $val == 'false' )
				{
					$val = false;
				}
				if ( !$params[$key] )
				{
					$params[$key] = false;
				}
				else
				{
					$params[$key] = intval($params[$key]);
				}
			}
		}
		
		# autocorrect params
		
		if ( !($params['inc_posts'] || $params['inc_pages']) )
		{
			$params['inc_posts'] = true;
		}
		
		if ( $params['max_num'] !== false
			&& $params['max_num'] < $params['min_num']
			)
		{
			$params['max_num'] = false;
		}
		
		if ( $params['max_days'] !== false
			&& $params['max_days'] < $params['min_days']
			)
		{
			$params['max_days'] = false;
		}
		
		# additional params
		
		$params['date_format'] = get_settings('date_format');
		$params['encoding'] = get_settings('blog_charset');
		
		if ( defined('sem_home_page_id') && sem_home_page_id )
		{
			$params['home_page_id'] = intval(sem_home_page_id);
		}
		else
		{
			$params['home_page_id'] = false;
		}
		
		if ( defined('sem_main_cat_id') && sem_main_cat_id )
		{
			$params['main_cat_id'] = intval(sem_main_cat_id);
		}
		else
		{
			$params['main_cat_id'] = false;
		}
		
		if ( defined('sem_sidebar_tile_id') && sem_sidebar_tile_id )
		{
			$params['sidebar_tile_id'] = intval(sem_sidebar_tile_id);
		}
		else
		{
			$params['sidebar_tile_id'] = false;
		}
		
		# return cache if relevant
		
		if ( is_writable(sem_cache_path) )
		{
			$cache_file = $this->cache_file . "-" . md5(serialize($params));
			if ( file_exists($cache_file) )
			{
				if ( ( filemtime($cache_file) + sem_cache_timeout ) >= time() )
				{
					return file_get_contents($cache_file);
				}
				else
				{
					$this->flush_cache();
				}
			}
		}
		
		
		# else display normally
		
		$o = "";
		
		$now = gmdate('Y-m-d H:i:00', strtotime("+1 minute"));
		
		if ( $params['min_days'] !== false )
		{
			$days_ago = gmdate('Y-m-d H:i:00', strtotime("-" . $params['min_days'] . " days"));
		}
		else
		{
			$days_ago = false;
		}
		
		if ( $params['max_days'] !== false )
		{
			$max_days_ago = gmdate('Y-m-d H:i:00', strtotime("-" . $params['max_days'] . "days"));
		}
		else
		{
			$max_days_ago = false;
		}
		
		if ( $params['num_days'] )
		{
			$max_date = $wpdb->get_col(
			#var_dump(
				"SELECT
					DATE_FORMAT( posts.post_date, '%Y-%m-%d 00:00:00' ) AS max_date
				FROM
					$wpdb->posts as posts"
					. ( ( $params['main_cat_id'] && !$params['inc_pages'] )
						? "
				INNER JOIN $wpdb->post2cat as post2cat ON post2cat.post_id = posts.ID"
						: ""
						)
					. "
				WHERE
					posts.post_date_gmt <= '" . $now . "'
					AND posts.post_password = ''"
					. ( $params['home_page_id']
						? ( "
					AND posts.ID <> " . $params['home_page_id'] )
						: ""
						)
					. ( $params['sidebar_tile_id']
						? ( "
					AND posts.ID <> " . $params['sidebar_tile_id'] )
						: ""
						)
					. "
					AND
					("
						. ( $params['inc_posts']
							? "
						(
							posts.post_status = 'publish'"
							. ( ( $params['main_cat_id'] && !$params['inc_pages'] )
								? ( "
							AND post2cat.category_id = " . $params['main_cat_id'] )
								: ""
								)
							. "
						)"
							: ""
							)
						. ( ( $params['inc_posts'] && $params['inc_pages'] )
							? ( "
						OR" )
							: ""
							)
						. ( $params['inc_pages']
							? "
						posts.post_status = 'static'"
							: ""
							)
						. "
					)
				GROUP BY
					max_date DESC
				LIMIT " . $params['num_days']
				);
			
			if ( isset($max_date) && $max_date )
			{
				$max_date = end($max_date);
			}
		}
		else
		{
			$max_date = false;
		}
		
		if ( !( $params['min_num']
				&& ( $params['min_num'] == $params['max_num'] )
				)
			)
		{
			$posts = $wpdb->get_results(
			#var_dump(
				"SELECT
					posts.*
				FROM
					$wpdb->posts as posts"
					. ( ( $params['main_cat_id'] && !$params['inc_pages'] )
						? "
				INNER JOIN $wpdb->post2cat as post2cat ON post2cat.post_id = posts.ID"
						: ""
						)
					. "
				WHERE
					posts.post_date_gmt <= '" . $now . "'
					AND posts.post_password = ''"
					. ( $params['home_page_id']
						? ( "
					AND posts.ID <> " . $params['home_page_id'] )
						: ""
						)
					. ( $params['sidebar_tile_id']
						? ( "
					AND posts.ID <> " . $params['sidebar_tile_id'] )
						: ""
						)
					. "
					AND
					("
						. ( $params['inc_posts']
							? "
						(
							posts.post_status = 'publish'"
							. ( ( $params['main_cat_id'] && !$params['inc_pages'] )
								? ( "
							AND post2cat.category_id = " . $params['main_cat_id'] )
								: ""
								)
							. "
						)"
							: ""
							)
						. ( ( $params['inc_posts'] && $params['inc_pages'] )
							? ( "
						OR" )
							: ""
							)
						. ( $params['inc_pages']
							? "
						posts.post_status = 'static'"
							: ""
							)
						. "
					)"
					. ( $days_ago
						? ( "
					AND posts.post_date_gmt >= '" . $days_ago . "'" )
						: ""
						)
					. ( $max_date
						? ( "
					AND posts.post_date >= '" . $max_date . "'" )
						: ""
						)
					. "
				ORDER BY
					posts.post_date DESC"
				. ( ( ( $params['max_num'] !== false )
						&& ( $params['max_num'] > $params['min_num'] )
						)
					? "
				LIMIT " . intval($params['max_num'])
					: ""
					)
				);
		}
		
		if ( $params['min_num']
			&& ( !isset($posts)
				|| ( sizeof($posts) < $params['min_num'] )
				)
			)
		{
			$posts = $wpdb->get_results(
			#var_dump(
				"SELECT
					posts.*
				FROM
					$wpdb->posts as posts"
					. ( ( $params['main_cat_id'] && !$params['inc_pages'] )
						? "
				INNER JOIN $wpdb->post2cat as post2cat ON post2cat.post_id = posts.ID"
						: ""
						)
					. "
				WHERE
					posts.post_date_gmt <= '" . $now . "'
					AND posts.post_password = ''"
					. ( $params['home_page_id']
						? ( "
					AND posts.ID <> " . $params['home_page_id'] )
						: ""
						)
					. ( $params['sidebar_tile_id']
						? ( "
					AND posts.ID <> " . $params['sidebar_tile_id'] )
						: ""
						)
					. "
					AND
					("
						. ( $params['inc_posts']
							? "
						(
							posts.post_status = 'publish'"
							. ( ( $params['main_cat_id'] && !$params['inc_pages'] )
								? ( "
							AND post2cat.category_id = " . $params['main_cat_id'] )
								: ""
								)
							. "
						)"
							: ""
							)
						. ( ( $params['inc_posts'] && $params['inc_pages'] )
							? ( "
						OR" )
							: ""
							)
						. ( $params['inc_pages']
							? "
						posts.post_status = 'static'"
							: ""
							)
						. "
					)"
					. ( $max_days_ago
						? ( "
					AND posts.post_date_gmt >= '" . $max_days_ago . "'" )
						: ""
						)
					. "
				ORDER BY
					posts.post_date DESC
				LIMIT " . $params['min_num']
				);
		}
		
		if ( isset($posts) && $posts )
		{
			if ( function_exists('update_post_cache') )
			{
				update_post_cache($posts);
			}
			if ( function_exists('update_page_cache') )
			{
				update_page_cache($posts);
			}
			
			$o .= "<div class=\"sem_recent\">\n";

			$o .= ( $params['title']
				? ( "<h2>" . $params['title'] . "</h2>\n" )
				: ""
				);
			
			if ( !$params['show_date'] )
			{
				$o .= "<ul>\n";
			}
			
			foreach ( $posts as $key => $post )
			{
				$post_title = $post->post_title;
				$post_permalink = apply_filters('the_permalink', get_permalink($post->ID));
				$post_date = date($params['date_format'], strtotime($post->post_date));
				
				if ( $params['show_date']
					&& ( !isset($posts[$key-1])
						|| ( $post_date != date($params['date_format'], strtotime($posts[$key-1]->post_date)) )
						)
					)
				{
					$o .= "<h3>" . $post_date . "</h3>\n"
						. "<ul>\n";
				}
				
				$o .= "<li>"
					. "<a href=\"" . $post_permalink . "\">"
						. $post_title
						. "</a>"
					. "</li>\n";
				
				if ( $params['show_date']
					&& ( !isset($posts[$key+1])
						|| $post_date != date($params['date_format'], strtotime($posts[$key+1]->post_date))
						)
					)
				{
					$o .= "</ul>\n";
				}
			}
			
			if ( !$params['show_date'] )
			{
				$o .= "</ul>\n";
			}

			$o .= "</div>\n";
		}
		
		
		# cache the result
		
		if ( is_writable(sem_cache_path) && is_writable($this->cache_file) )
		{
			$fp = fopen($cache_file, "w+");
			fwrite($fp, $o);
			fclose($fp);
		}
		
		
		# return output
		
		return $o;
	} # end display()
	
	
	#
	# auto_display()
	#
	
	function auto_display()
	{
		if ( !defined('sem_recent_posts_displayed') )
		{
			echo "</ul>\n";
			echo $this->display();
			echo "<ul>\n";
		}
	} # end auto_display()
} # end sem_recent_posts

$my_recent_posts =& new sem_recent_posts();


#
# Template tags
#

function the_recent_posts($args = null)
{
	global $my_recent_posts;
	
	echo $my_recent_posts->display($args);
} # end the_recent_posts()


########################
#
# Backward compatibility
#

function sem_recent_posts($args = null)
{
	the_recent_posts($args);
} # end sem_recent_posts()
?>
