children now separated) SimplePie updated to 1.1.1 Fixed broken cron command Fixed broken export on some systems Fixed broken redirect when resetting a campaign Everything now stored in GMT to avoid time issues. Gotten rid of NOW() functions in favor of WP time functions Fixed bug with validation upon deletion of feeds in existing campaigns Fixed bug with allow comments setting. Fixed bug with logs dates Fixed bug with double quote escaping (fixes campaign templates / rewrite html bugs) Username in options tab changed to a more handy select box. Interface now looks better on IE (d'oh) Added many help files Fixed annoying duplicates bug Fixed small bug in import with labels Fixed bug with categories in edit mode Fixed Tools post changes. Fixed issue with empty rewrite replacements Non-regex rewrite replacements now case insensitive. Fixed bugs with 'use feed date' option. Fixed footer copyright Fixed bad dates in 'view all' logs Log message field made text Fields changed to datetime format Clean logs function fixed r6: str_ireplace now works with arrays queries adjusted to work on all server configurations */ # WP-o-Matic paths. With trailing slash. define('WPODIR', dirname(__FILE__) . '/'); define('WPOINC', WPODIR . 'inc/'); define('WPOTPL', WPOINC . 'admin/'); # Dependencies require_once( WPOINC . 'tools.class.php' ); class WPOMatic { var $version = '1.0RC3-7'; # Editable options # true by default until upgrading is implemented var $delete_tables = true; # Internal var $sections = array('home', 'setup', 'list', 'add', 'edit', 'options', 'import', 'export', 'reset', 'delete', 'logs', 'testfeed', 'forcefetch'); var $campaign_structure = array('main' => array(), 'rewrites' => array(), 'categories' => array(), 'feeds' => array()); # __construct() function WPOMatic() { global $wpdb, $wp_version; # Table names init $this->db = array( 'campaign' => $wpdb->prefix . 'wpo_campaign', 'campaign_category' => $wpdb->prefix . 'wpo_campaign_category', 'campaign_feed' => $wpdb->prefix . 'wpo_campaign_feed', 'campaign_word' => $wpdb->prefix . 'wpo_campaign_word', 'campaign_post' => $wpdb->prefix . 'wpo_campaign_post', 'log' => $wpdb->prefix . 'wpo_log' ); # Are we running the new admin panel (2.5+) ? $this->newadmin = version_compare($wp_version, '2.5.0', '>='); # Is installed ? $this->installed = get_option('wpo_version'); // upgrade feature due for 1.0 // $this->needs_upgrade = $this->installed ? version_compare(get_option('wpo_version'), $this->version, '<') : false; $this->needs_upgrade = false; $this->setup = get_option('wpo_setup'); # Actions add_action('activate_wp-o-matic/wpomatic.php', array(&$this, 'install')); # Plugin installed add_action('deactivate_wp-o-matic/wpomatic.php', array(&$this, 'uninstall')); # Plugin unintalled add_action('init', array(&$this, 'init')); # Wordpress init add_action('admin_head', array(&$this, 'adminHead')); # Admin head add_action('admin_footer', array(&$this, 'adminWarning')); # Admin footer add_action('admin_menu', array(&$this, 'adminMenu')); # Admin menu creation # Ajax actions add_action('wp_ajax_delete-campaign', array(&$this, 'adminDelete')); add_action('wp_ajax_test-feed', array(&$this, 'adminTestfeed')); # Filters add_action('the_permalink', array(&$this, 'filterPermalink')); # WP-o-Matic URIs. Without trailing slash $this->optionsurl = get_option('siteurl') . '/wp-admin/options-general.php'; $this->adminurl = $this->optionsurl . '?page=wpomatic.php'; $this->pluginpath = get_option('siteurl') . '/wp-content/plugins/wp-o-matic'; $this->helpurl = $this->pluginpath . '/help.php?item='; $this->tplpath = $this->pluginpath . '/inc/admin'; $this->cachepath = WPODIR . get_option('wpo_cachepath'); # Cron command / url $this->cron_command = attribute_escape('*/20 * * * * '. $this->getCommand() . ' ' . dirname(__FILE__) . '/cron.php?code=' . get_option('wpo_croncode')); $this->cron_url = $this->pluginpath . '/cron.php?code=' . get_option('wpo_croncode'); } /** * Called when plugin is activated * * */ function install() { global $wpdb; if(file_exists(ABSPATH . '/wp-admin/upgrade-functions.php')) require_once(ABSPATH . '/wp-admin/upgrade-functions.php'); else require_once(ABSPATH . '/wp-admin/includes/upgrade.php'); if(! $this->installed) { # wpo_campaign dbDelta( "CREATE TABLE `{$this->db['campaign']}` ( `id` int(11) unsigned NOT NULL auto_increment, `title` varchar(255) NOT NULL, `active` tinyint(1) default '1', `slug` varchar(250), `template` MEDIUMTEXT, `frequency` int(5) default '180', `feeddate` tinyint(1) default '0', `cacheimages` tinyint(1) default '1', `posttype` enum('publish','draft','private') NOT NULL default 'publish', `authorid` int(11), `comment_status` enum('open','closed','registered_only') NOT NULL default 'open', `allowpings` tinyint(1) default '1', `dopingbacks` tinyint(1) default '1', `max` smallint(3) default '10', `linktosource` tinyint(1) default '0', `count` int(11), `lastactive` datetime NOT NULL default '0000-00-00 00:00:00', `created_on` datetime NOT NULL default '0000-00-00 00:00:00', PRIMARY KEY (`id`) );" ); # wpo_campaign_category dbDelta( "CREATE TABLE `{$this->db['campaign_category']}` ( `id` int(11) unsigned NOT NULL auto_increment, `category_id` int(11) NOT NULL, `campaign_id` int(11) NOT NULL, PRIMARY KEY (`id`) );" ); # wpo_campaign_feed dbDelta( "CREATE TABLE `{$this->db['campaign_feed']}` ( `id` int(11) unsigned NOT NULL auto_increment, `campaign_id` int(11) NOT NULL, `url` varchar(255) NOT NULL, `type` varchar(255) NOT NULL, `title` varchar(255) NOT NULL, `description` varchar(255) NOT NULL, `logo` varchar(255), `count` int(11), `hash` varchar(255), `lastactive` datetime, PRIMARY KEY (`id`) );" ); # wpo_campaign_post dbDelta( "CREATE TABLE `{$this->db['campaign_post']}` ( `id` int(11) unsigned NOT NULL auto_increment, `campaign_id` int(11) NOT NULL, `feed_id` int(11) NOT NULL, `post_id` int(11) NOT NULL, `hash` varchar(255), PRIMARY KEY (`id`) );" ); # wpo_campaign_word dbDelta( "CREATE TABLE `{$this->db['campaign_word']}` ( `id` int(11) unsigned NOT NULL auto_increment, `campaign_id` int(11) NOT NULL, `word` varchar(255) NOT NULL, `regex` tinyint(1) default '0', `rewrite` tinyint(1) default '1', `rewrite_to` varchar(255), `relink` varchar(255), PRIMARY KEY (`id`) );" ); # wpo_log dbDelta( "CREATE TABLE `{$this->db['log']}` ( `id` int(11) unsigned NOT NULL auto_increment, `message` mediumtext NOT NULL, `created_on` datetime NOT NULL default '0000-00-00 00:00:00', PRIMARY KEY (`id`) );" ); # Options WPOTools::addOptions(array( 'wpo_log' => array(1, 'Log WP-o-Matic actions'), 'wpo_log_stdout' => array(0, 'Output logs to browser while a campaign is being processed'), 'wpo_unixcron' => array(WPOTools::isUnix(), 'Use unix-style cron'), 'wpo_croncode' => array(substr(md5(time()), 0, 8), 'Cron job password.'), 'wpo_cacheimages' => array(0, 'Cache all images. Overrides campaign options'), 'wpo_cachepath' => array('cache', 'Cache path relative to wpomatic directory'), 'wpo_version' => array($this->version, 'Installed version log') )); $this->installed = true; } else if($this->needs_upgrade) { $this->setup = false; if($this->tablesExist()) $this->display_upgrade = true; else { $this->installed = false; $this->needs_upgrade = false; $this->install(); } } } /** * Called when plugin is deactivated * * */ function uninstall() { global $wpdb; // Delete tables if($this->delete_tables) { foreach($this->db as $table) $wpdb->query("DROP TABLE `{$table}` "); } // Delete options // We remove wpo_version until upgrading functionality is implemented WPOTools::deleteOptions(array('wpo_version', 'wpo_setup', 'wpo_log', 'wpo_log_stdout', 'wpo_unixcron', 'wpo_croncode', 'wpo_cacheimages', 'wpo_cachepath')); } /** * Checks that WP-o-Matic tables exist * * */ function tablesExist() { global $wpdb; foreach($this->db as $table) { if(! $wpdb->query("SELECT * FROM `{$table}`")) return false; } return true; } /** * Called when blog is initialized * * */ function init() { global $wpdb; if($this->installed) { if(! get_option('wpo_unixcron')) $this->processAll(); if(isset($_REQUEST['page'])) { if(isset($_REQUEST['campaign_add']) || isset($_REQUEST['campaign_edit'])) $this->adminCampaignRequest(); $this->adminExportProcess(); $this->adminInit(); } } } /** * Saves a log message to database * * * @param string $message Message to save */ function log($message) { global $wpdb; if(get_option('wpo_log_stdout')) echo $message; if(get_option('wpo_log')) { $message = $wpdb->escape($message); $time = current_time('mysql', true); $wpdb->query("INSERT INTO {$this->db['log']} (message, created_on) VALUES ('{$message}', '{$time}') "); } } /** * Called by cron.php to update the site * * */ function runCron($log = true) { $this->log('Running cron job'); $this->processAll(); } /** * Finds a suitable command to run cron * * @return string command **/ function getCommand() { $commands = array( WPOTools::getBinaryPath('curl'), WPOTools::getBinaryPath('wget'), WPOTools::getBinaryPath('lynx', ' -dump'), WPOTools::getBinaryPath('ftp') ); return WPOTools::pick($commands[0], $commands[1], $commands[2], $commands[3]); } /** * Determines what the title has to link to * * @return string new text **/ function filterPermalink($url) { // if from admin panel if($this->admin) return $url; if(get_the_ID()) { $campaignid = (int) get_post_meta(get_the_ID(), 'wpo_campaignid', true); if($campaignid) { $campaign = $this->getCampaignById($campaignid); if($campaign->linktosource) return get_post_meta(get_the_ID(), 'wpo_sourcepermalink', true); } return $url; } } /** * Processes all campaigns * */ function processAll() { @set_time_limit(0); $campaigns = $this->getCampaigns('unparsed=1'); foreach($campaigns as $campaign) { $this->processCampaign($campaign); } } /** * Processes a campaign * * @param object $campaign Campaign database object * @return integer Number of processed items */ function processCampaign(&$campaign) { global $wpdb; @set_time_limit(0); ob_implicit_flush(); // Get campaign $campaign = is_numeric($campaign) ? $this->getCampaignById($campaign) : $campaign; // Log $this->log('Processing campaign ' . $campaign->title . ' (ID: ' . $campaign->id . ')'); // Get feeds $count = 0; $feeds = $this->getCampaignFeeds($campaign->id); foreach($feeds as $feed) $count += $this->processFeed($campaign, $feed); $wpdb->query(WPOTools::updateQuery($this->db['campaign'], array( 'count' => $campaign->count + $count, 'lastactive' => current_time('mysql', true) ), "id = {$campaign->id}")); return $count; } /** * Processes a feed * * @param $campaign object Campaign database object * @param $feed object Feed database object * @return The number of items added to database */ function processFeed(&$campaign, &$feed) { global $wpdb; @set_time_limit(0); // Log $this->log('Processing feed ' . $feed->title . ' (ID: ' . $feed->id . ')'); // Access the feed $simplepie = $this->fetchFeed($feed->url, false, $campaign->max); // Get posts (last is first) $items = array(); $count = 0; foreach($simplepie->get_items() as $item) { if($feed->hash == $this->getItemHash($item)) { if($count == 0) $this->log('No new posts'); break; } if($this->isDuplicate($campaign, $feed, $item)) { $this->log('Filtering duplicate post'); break; } $count++; array_unshift($items, $item); if($count == $campaign->max) { $this->log('Campaign fetch limit reached at ' . $campaign->max); break; } } // Processes post stack foreach($items as $item) { $this->processItem($campaign, $feed, $item); $lasthash = $this->getItemHash($item); } // If we have added items, let's update the hash if($count) { $wpdb->query(WPOTools::updateQuery($this->db['campaign_feed'], array( 'count' => $count, 'lastactive' => current_time('mysql', true), 'hash' => $lasthash ), "id = {$feed->id}")); $this->log( $count . ' posts added' ); } return $count; } /** * Processes an item * * @param $item object SimplePie_Item object */ function getItemHash($item) { return sha1($item->get_title() . $item->get_permalink()); } /** * Processes an item * * @param $campaign object Campaign database object * @param $feed object Feed database object * @param $item object SimplePie_Item object */ function processItem(&$campaign, &$feed, &$item) { global $wpdb; $this->log('Processing item'); // Item content $content = $this->parseItemContent($campaign, $feed, $item); // Item date if($campaign->feeddate && ($item->get_date('U') > (current_time('timestamp', 1) - $campaign->frequency) && $item->get_date('U') < current_time('timestamp', 1))) $date = $item->get_date('U'); else $date = null; // Categories $categories = $this->getCampaignData($campaign->id, 'categories'); // Meta $meta = array( 'wpo_campaignid' => $campaign->id, 'wpo_feedid' => $feed->id, 'wpo_sourcepermalink' => $item->get_permalink() ); // Create post $postid = $this->insertPost($wpdb->escape($item->get_title()), $wpdb->escape($content), $date, $categories, $campaign->posttype, $campaign->authorid, $campaign->allowpings, $campaign->comment_status, $meta); // If pingback/trackbacks if($campaign->dopingbacks) { $this->log('Processing item pingbacks'); require_once(ABSPATH . WPINC . '/comment.php'); pingback($content, $postid); } // Save post to log database $wpdb->query(WPOTools::insertQuery($this->db['campaign_post'], array( 'campaign_id' => $campaign->id, 'feed_id' => $feed->id, 'post_id' => $postid, 'hash' => $this->getItemHash($item) ))); } /** * Processes an item * * @param $campaign object Campaign database object * @param $feed object Feed database object * @param $item object SimplePie_Item object */ function isDuplicate(&$campaign, &$feed, &$item) { global $wpdb; $hash = $this->getItemHash($item); $row = $wpdb->get_row("SELECT * FROM `{$this->db['campaign_post']}` WHERE campaign_id = {$campaign->id} AND feed_id = {$feed->id} AND hash = '$hash' "); return !! $row; } /** * Writes a post to blog * * * @param string $title Post title * @param string $content Post content * @param integer $timestamp Post timestamp * @param array $category Array of categories * @param string $status 'draft', 'published' or 'private' * @param integer $authorid ID of author. * @param boolean $allowpings Allow pings * @param boolean $comment_status 'open', 'closed', 'registered_only' * @param array $meta Meta key / values * @return integer Created post id */ function insertPost($title, $content, $timestamp = null, $category = null, $status = 'draft', $authorid = null, $allowpings = true, $comment_status = 'open', $meta = array()) { $date = ($timestamp) ? gmdate('Y-m-d H:i:s', $timestamp + (get_option('gmt_offset') * 3600)) : null; $postid = wp_insert_post(array( 'post_title' => $title, 'post_content' => $content, 'post_content_filtered' => $content, 'post_category' => $category, 'post_status' => $status, 'post_author' => $authorid, 'post_date' => $date, 'comment_status' => $comment_status, 'ping_status' => $allowpings )); foreach($meta as $key => $value) $this->insertPostMeta($postid, $key, $value); return $postid; } /** * insertPostMeta * * */ function insertPostMeta($postid, $key, $value) { global $wpdb; $result = $wpdb->query( "INSERT INTO $wpdb->postmeta (post_id,meta_key,meta_value ) " . " VALUES ('$postid','$key','$value') "); return $wpdb->insert_id; } /** * Parses an item content * * @param $campaign object Campaign database object * @param $feed object Feed database object * @param $item object SimplePie_Item object */ function parseItemContent(&$campaign, &$feed, &$item) { $content = $item->get_content(); // Caching if(get_option('wpo_cacheimages') || $campaign->cacheimages) { $images = WPOTools::parseImages($content); $urls = $images[2]; if(sizeof($urls)) { $this->log('Caching images'); foreach($urls as $url) { $newurl = $this->cacheRemoteImage($url); if($newurl) $content = str_replace($url, $newurl, $content); } } } // Template parse $vars = array( '{content}', '{title}', '{permalink}', '{feedurl}', '{feedtitle}', '{feedlogo}', '{campaigntitle}', '{campaignid}', '{campaignslug}' ); $replace = array( $content, $item->get_title(), $item->get_link(), $feed->url, $feed->title, $feed->logo, $campaign->title, $campaign->id, $campaign->slug ); $content = str_ireplace($vars, $replace, ($campaign->template) ? $campaign->template : '{content}'); // Rewrite $rewrites = $this->getCampaignData($campaign->id, 'rewrites'); foreach($rewrites as $rewrite) { $origin = $rewrite['origin']['search']; if(isset($rewrite['rewrite'])) { $reword = isset($rewrite['relink']) ? '' . $rewrite['rewrite'] . '' : $rewrite['rewrite']; if($rewrite['origin']['regex']) { $content = preg_replace($origin, $reword, $content); } else $content = str_ireplace($origin, $reword, $content); } else if(isset($rewrite['relink'])) $content = str_ireplace($origin, '' . $origin . '', $content); } return $content; } /** * Cache remote image * * @return string New url */ function cacheRemoteImage($url) { $contents = @file_get_contents($url); $filename = substr(md5(time()), 0, 5) . '_' . basename($url); $cachepath = $this->cachepath; if(is_writable($cachepath) && $contents) { file_put_contents($cachepath . '/' . $filename, $contents); return $this->pluginpath . '/' . get_option('wpo_cachepath') . '/' . $filename; } return false; } /** * Parses a feed with SimplePie * * @param boolean $stupidly_fast Set fast mode. Best for checks * @param integer $max Limit of items to fetch * @return SimplePie_Item Feed object **/ function fetchFeed($url, $stupidly_fast = false, $max = 0) { # SimplePie if(! class_exists('SimplePie')) require_once( WPOINC . 'simplepie/simplepie.class.php' ); $feed = new SimplePie(); $feed->set_feed_url($url); $feed->set_item_limit($max); $feed->set_stupidly_fast($stupidly_fast); $feed->enable_cache(false); $feed->init(); $feed->handle_content_type(); return $feed; } /** * Returns all blog usernames (in form [user_login => display_name (user_login)] ) * * @return array $usernames **/ function getBlogUsernames() { $return = array(); $users = get_users_of_blog(); foreach($users as $user) { if($user->display_name == $user->user_login) $return[$user->user_login] = "{$user->display_name}"; else $return[$user->user_login] = "{$user->display_name} ({$user->user_login})"; } return $return; } /** * Returns all data for a campaign * * */ function getCampaignData($id, $section = null) { global $wpdb; $campaign = (array) $this->getCampaignById($id); if($campaign) { $campaign_data = $this->campaign_structure; // Main if(!$section || $section == 'main') { $campaign_data['main'] = array_merge($campaign_data['main'], $campaign); $userdata = get_userdata($campaign_data['main']['authorid']); $campaign_data['main']['author'] = $userdata->user_login; } // Categories if(!$section || $section == 'categories') { $categories = $wpdb->get_results("SELECT * FROM `{$this->db['campaign_category']}` WHERE campaign_id = $id"); foreach($categories as $category) $campaign_data['categories'][] = $category->category_id; } // Feeds if(!$section || $section == 'feeds') { $campaign_data['feeds']['edit'] = array(); $feeds = $this->getCampaignFeeds($id); foreach($feeds as $feed) $campaign_data['feeds']['edit'][$feed->id] = $feed->url; } // Rewrites if(!$section || $section == 'rewrites') { $rewrites = $wpdb->get_results("SELECT * FROM `{$this->db['campaign_word']}` WHERE campaign_id = $id"); foreach($rewrites as $rewrite) { $word = array('origin' => array('search' => $rewrite->word, 'regex' => $rewrite->regex), 'rewrite' => $rewrite->rewrite_to, 'relink' => $rewrite->relink); if(! $rewrite->rewrite) unset($word['rewrite']); if(empty($rewrite->relink)) unset($word['relink']); $campaign_data['rewrites'][] = $word; } } if($section) return $campaign_data[$section]; return $campaign_data; } return false; } /** * Retrieves logs from database * * */ function getLogs($args = '') { global $wpdb; extract(WPOTools::getQueryArgs($args, array('orderby' => 'created_on', 'ordertype' => 'DESC', 'limit' => null, 'page' => null, 'perpage' => null))); if(!is_null($page)) { if($page == 0) $page = 1; $page--; $start = $page * $perpage; $end = $start + $perpage; $limit = "LIMIT {$start}, {$end}"; } return $wpdb->get_results("SELECT * FROM `{$this->db['log']}` ORDER BY $orderby $ordertype $limit"); } /** * Retrieves a campaign by its id * * */ function getCampaignById($id) { global $wpdb; $id = intval($id); return $wpdb->get_row("SELECT * FROM `{$this->db['campaign']}` WHERE id = $id"); } /** * Retrieves a feed by its id * * */ function getFeedById($id) { global $wpdb; $id = intval($id); return $wpdb->get_row("SELECT * FROM `{$this->db['campaign_feed']}` WHERE id = $id"); } /** * Retrieves campaigns from database * * */ function getCampaigns($args = '') { global $wpdb; extract(WPOTools::getQueryArgs($args, array('fields' => '*', 'search' => '', 'orderby' => 'created_on', 'ordertype' => 'DESC', 'where' => '', 'unparsed' => false, 'limit' => null))); if(! empty($search)) $where .= " AND title LIKE '%{$search}%' "; if($unparsed) $where .= " AND active = 1 AND (frequency + UNIX_TIMESTAMP(lastactive)) < ". (current_time('timestamp',1) - ($gmt ? 0 : (get_option('gmt_offset') * 3600))) . " "; $sql = "SELECT $fields FROM `{$this->db['campaign']}` WHERE 1 = 1 $where " . "ORDER BY $orderby $ordertype $limit"; return $wpdb->get_results($sql); } /** * Retrieves feeds for a certain campaign * * @param integer $id Campaign id */ function getCampaignFeeds($id) { global $wpdb; return $wpdb->get_results("SELECT * FROM `{$this->db['campaign_feed']}` WHERE campaign_id = $id"); } /** * Retrieves all WP posts for a certain campaign * * @param integer $id Campaign id */ function getCampaignPosts($id) { global $wpdb; return $wpdb->get_results("SELECT post_id FROM `{$this->db['campaign_post']}` WHERE campaign_id = $id "); } /** * Adds a feed by url and campaign id * * */ function addCampaignFeed($id, $feed) { global $wpdb; $simplepie = $this->fetchFeed($feed, true); $url = $wpdb->escape($simplepie->subscribe_url()); // If it already exists, ignore it if(! $wpdb->get_var("SELECT id FROM `{$this->db['campaign_feed']}` WHERE campaign_id = $id AND url = '$url' ")) { $wpdb->query(WPOTools::insertQuery($this->db['campaign_feed'], array('url' => $url, 'title' => $wpdb->escape($simplepie->get_title()), 'description' => $wpdb->escape($simplepie->get_description()), 'logo' => $wpdb->escape($simplepie->get_image_url()), 'campaign_id' => $id) )); return $wpdb->insert_id; } return false; } /** * Retrieves feeds from database * * @param mixed $args */ function getFeeds($args = '') { global $wpdb; extract(WPOTools::getQueryArgs($args, array('fields' => '*', 'campid' => '', 'join' => false, 'orderby' => 'created_on', 'ordertype' => 'DESC', 'limit' => null))); $sql = "SELECT $fields FROM `{$this->db['campaign_feed']}` cf "; if(!empty($join)) $sql .= "INNER JOIN `{$this->db['campaign']}` camp ON camp.id = cf.campaign_id "; if(!empty($campid)) $sql .= "WHERE cf.campaign_id = $campid"; return $wpdb->get_results($sql); } /** * Returns how many seconds left till reprocessing * * @return seconds **/ function getCampaignRemaining(&$campaign, $gmt = 0) { return mysql2date('U', $campaign->lastactive) + $campaign->frequency - current_time('timestamp', true) + ($gmt ? 0 : (get_option('gmt_offset') * 3600)); } /** * Called when WP-o-Matic admin pages initialize. * * */ function adminInit() { auth_redirect(); // force display of a certain section // if user needs to upgrade (TODO: for 1.0) //if($this->needs_upgrade) // $this->section = 'upgrade'; //elseif(! $this->setup) // $this->section = 'setup'; //else // $this->section = $_REQUEST['s'] ? $_REQUEST['s'] : $this->sections[0]; $this->section = ($this->setup) ? ($_REQUEST['s'] ? $_REQUEST['s'] : $this->sections[0]) : 'setup'; if (isset($_SERVER['HTTP_USER_AGENT']) && (strpos($_SERVER['HTTP_USER_AGENT'], 'MSIE') !== false)) die('Please switch to Firefox / Safari'); wp_enqueue_script('prototype'); wp_enqueue_script('wpoadmin', $this->tplpath . '/admin.js', array('prototype'), $this->version); if($this->section == 'list') wp_enqueue_script('listman'); if(WPOTools::isAjax()) { $this->admin(); exit; } } /** * Called by admin-header.php * * @return void **/ function adminHead() { $this->admin = true; } /** * Shows a warning fading box * * -idea from Akismet plugin */ function adminWarning() { if(! $this->setup && $this->section != 'setup') { echo "

" . sprintf(__('Please click here to setup and configure WP-o-Matic.', 'wpomatic'), $this->adminurl . '&s=setup') . "

"; } } /** * Executes the current section method. * * */ function admin() { if(in_array($this->section, $this->sections)) { $method = 'admin' . ucfirst($this->section); $this->$method(); } } /** * Adds the WP-o-Matic item to menu * * */ function adminMenu() { add_submenu_page('options-general.php', 'WP-o-Matic', 'WP-o-Matic', 10, basename(__FILE__), array(&$this, 'admin')); } /** * Outputs the admin header in a template * * */ function adminHeader() { $current = array(); foreach($this->sections as $s) $current[$s] = ($s == $this->section) ? 'class="current"' : ''; include(WPOTPL . 'header.php'); } /** * Outputs the admin footer in a template * * */ function adminFooter() { include(WPOTPL . 'footer.php'); } /** * Home section * * */ function adminHome() { $logging = get_option('wpo_log'); $logs = $this->getLogs('limit=7'); $nextcampaigns = $this->getCampaigns( 'fields=id,title,lastactive,frequency&limit=5'. '&where=active=1&orderby=UNIX_TIMESTAMP(lastactive)%2Bfrequency&ordertype=ASC'); $lastcampaigns = $this->getCampaigns('fields=id,title,lastactive,frequency&limit=5&where=UNIX_TIMESTAMP(lastactive)>0&orderby=lastactive'); $campaigns = $this->getCampaigns('fields=id,title,count&limit=5&orderby=count'); include(WPOTPL . 'home.php'); } /** * Setup admin * * */ function adminSetup() { if(isset($_POST['dosetup'])) { update_option('wpo_unixcron', isset($_REQUEST['option_unixcron'])); update_option('wpo_setup', 1); $this->adminHome(); exit; } # Commands $prefix = $this->getCommand(); $nocommand = ! file_exists($prefix); $safe_mode = ini_get('safe_mode'); $command = $this->cron_command; $url = $this->cron_url; include(WPOTPL . 'setup.php'); } /** * List campaigns section * * */ function adminList() { global $wpdb; if(isset($_REQUEST['q'])) { $q = $_REQUEST['q']; $campaigns = $this->getCampaigns('search=' . $q); } else $campaigns = $this->getCampaigns('orderby=CREATED_ON'); include(WPOTPL . 'list.php'); } /** * Add campaign section * * */ function adminAdd() { $data = $this->campaign_structure; if(isset($_REQUEST['campaign_add'])) { check_admin_referer('wpomatic-edit-campaign'); if($this->errno) $data = $this->campaign_data; else $addedid = $this->adminProcessAdd(); } $author_usernames = $this->getBlogUsernames(); $campaign_add = true; include(WPOTPL . 'edit.php'); } /** * Edit campaign section * * */ function adminEdit() { $id = intval($_REQUEST['id']); if(!$id) die("Can't be called directly"); if(isset($_REQUEST['campaign_edit'])) { check_admin_referer('wpomatic-edit-campaign'); $data = $this->campaign_data; $submitted = true; if(! $this->errno) { $this->adminProcessEdit($id); $edited = true; $data = $this->getCampaignData($id); } } else $data = $this->getCampaignData($id); $author_usernames = $this->getBlogUsernames(); $campaign_edit = true; include(WPOTPL . 'edit.php'); } function adminEditCategories(&$data, $parent = 0, $level = 0, $categories = 0) { if ( !$categories ) $categories = get_categories(array('hide_empty' => 0)); if(function_exists('_get_category_hierarchy')) $children = _get_category_hierarchy(); elseif(function_exists('_get_term_hierarchy')) $children = _get_term_hierarchy('category'); else $children = array(); if ( $categories ) { ob_start(); foreach ( $categories as $category ) { if ( $category->parent == $parent) { echo "\t" . _wpo_edit_cat_row($category, $level, $data); if ( isset($children[$category->term_id]) ) $this->adminEditCategories($data, $category->term_id, $level + 1, $categories ); } } $output = ob_get_contents(); ob_end_clean(); echo $output; } else { return false; } } /** * Resets a campaign (sets post count to 0, forgets last parsed post) * * * @todo Make it ajax-compatible here and add javascript code */ function adminReset() { global $wpdb; $id = intval($_REQUEST['id']); if(! defined('DOING_AJAX')) check_admin_referer('reset-campaign_'.$id); // Reset count and lasactive $wpdb->query(WPOTools::updateQuery($this->db['campaign'], array( 'count' => 0, 'lastactive' => 0 ), "id = $id")); // Reset feeds hashes, count, and lasactive foreach($this->getCampaignFeeds($id) as $feed) { $wpdb->query(WPOTools::updateQuery($this->db['campaign_feed'], array( 'count' => 0, 'lastactive' => 0, 'hash' => '' ), "id = {$feed->id}")); } if(defined('DOING_AJAX')) die('1'); else $this->adminList(); } /** * Deletes a campaign * * */ function adminDelete() { global $wpdb; $id = intval($_REQUEST['id']); // If not called through admin-ajax.php if(! defined('DOING_AJAX')) check_admin_referer('delete-campaign_'.$id); $wpdb->query("DELETE FROM `{$this->db['campaign']}` WHERE id = $id"); $wpdb->query("DELETE FROM `{$this->db['campaign_feed']}` WHERE campaign_id = $id"); $wpdb->query("DELETE FROM `{$this->db['campaign_word']}` WHERE campaign_id = $id"); $wpdb->query("DELETE FROM `{$this->db['campaign_category']}` WHERE campaign_id = $id"); if(defined('DOING_AJAX')) die('1'); else $this->adminList(); } /** * Options section * * */ function adminOptions() { if(isset($_REQUEST['update'])) { update_option('wpo_unixcron', isset($_REQUEST['option_unixcron'])); update_option('wpo_log', isset($_REQUEST['option_logging'])); update_option('wpo_log_stdout', isset($_REQUEST['option_logging_stdout'])); update_option('wpo_cacheimages', isset($_REQUEST['option_caching'])); update_option('wpo_cachepath', rtrim($_REQUEST['option_cachepath'], '/')); $updated = 1; } if(!is_writable($this->cachepath)) $not_writable = true; include(WPOTPL . 'options.php'); } /** * Import section * * */ function adminImport() { global $wpdb; @session_start(); if(!$_POST) unset($_SESSION['opmlimport']); if(isset($_FILES['importfile']) || $_POST) check_admin_referer('import-campaign'); if(!isset($_SESSION['opmlimport'])) { if(isset($_FILES['importfile'])) { if(is_uploaded_file($_FILES['importfile']['tmp_name'])) $file = $_FILES['importfile']['tmp_name']; else $file = false; } else if(isset($_REQUEST['importurl'])) { $fromurl = true; $file = $_REQUEST['importurl']; } } if(isset($file) || ($_POST && isset($_SESSION['opmlimport'])) ) { require_once( WPOINC . 'xmlparser.class.php' ); $contents = (isset($file) ? @file_get_contents($file) : $_SESSION['opmlimport']); $_SESSION['opmlimport'] = $contents; # Get OPML data $opml = new XMLParser($contents); $opml = $opml->getTree(); # Check that it is indeed opml if(is_array($opml) && isset($opml['OPML'])) { $opml = $opml['OPML'][0]; $title = isset($opml['HEAD'][0]['TITLE'][0]['VALUE']) ? $opml['HEAD'][0]['TITLE'][0]['VALUE'] : null; $opml = $opml['BODY'][0]; $success = 1; # Campaigns dropdown $campaigns = array(); foreach($this->getCampaigns() as $campaign) $campaigns[$campaign->id] = $campaign->title; } else $import_error = 1; } $this->adminImportProcess(); include(WPOTPL . 'import.php'); } /** * Import process * * */ function adminImportProcess() { global $wpdb; if(isset($_REQUEST['add'])) { if(!isset($_REQUEST['feed'])) $add_error = __('You must select at least one feed', 'wpomatic'); else { switch($_REQUEST['import_mode']) { // Several campaigns case '1': $created_campaigns = array(); foreach($_REQUEST['feed'] as $campaignid => $feeds) { if(!in_array($campaignid, $created_campaigns)) { // Create campaign $title = $_REQUEST['campaign'][$campaignid]; if(!$title) continue; $slug = WPOTools::stripText($title); $wpdb->query("INSERT INTO {$this->db['campaign']} (title, active, slug, lastactive, count) VALUES ('$title', 0, '$slug', 0, 0) "); $created_campaigns[] = $wpdb->insert_id; // Add feeds foreach($feeds as $feedurl => $yes){ $this->addCampaignFeed($campaignid, urldecode($feedurl)); } } } $this->add_success = __('Campaigns added successfully. Feel free to edit them', 'wpomatic'); break; // All feeds into an existing campaign case '2': $campaignid = $_REQUEST['import_custom_campaign']; foreach($_REQUEST['feed'] as $cid => $feeds) { // Add feeds foreach($feeds as $feedurl => $yes) $this->addCampaignFeed($campaignid, urldecode($feedurl)); } $this->add_success = sprintf(__('Feeds added successfully. Edit campaign', 'wpomatic'), $this->adminurl . '&s=edit&id=' . $campaignid); break; // All feeds into new campaign case '3': $title = $_REQUEST['import_new_campaign']; $slug = WPOTools::stripText($title); $wpdb->query("INSERT INTO {$this->db['campaign']} (title, active, slug, lastactive, count) VALUES ('$title', 0, '$slug', 0, 0) "); $campaignid = $wpdb->insert_id; // Add feeds foreach($_REQUEST['feed'] as $cid => $feeds) { // Add feeds foreach($feeds as $feedurl => $yes) $this->addCampaignFeed($campaignid, urldecode($feedurl)); } $this->add_success = sprintf(__('Feeds added successfully. Edit campaign', 'wpomatic'), $this->adminurl . '&s=edit&id=' . $campaignid); break; } } } } /** * Export * * */ function adminExport() { if(isset($this->export_error)) $error = $this->export_error; $campaigns = $this->getCampaigns(); include(WPOTPL . 'export.php'); } /** * Export process * * */ function adminExportProcess() { if($_POST) { if(!isset($_REQUEST['export_campaign'])) { $this->export_error = __('Please select at least one campaign', 'wpomatic'); } else { $campaigns = array(); foreach($_REQUEST['export_campaign'] as $cid) { $campaign = $this->getCampaignById($cid); $campaign->feeds = (array) $this->getCampaignFeeds($cid); $campaigns[] = $campaign; } header("Content-type: text/x-opml"); header('Content-Disposition: attachment; filename="wpomatic.opml"'); include(WPOTPL . 'export.opml.php'); exit; } } } /** * Tests a feed * * */ function adminTestfeed() { if(!isset($_REQUEST['url'])) return false; $url = $_REQUEST['url']; $feed = $this->fetchFeed($url, true); $works = ! $feed->error(); // if no error returned if(defined('DOING_AJAX')){ echo intval($works); die(); } else include(WPOTPL . 'testfeed.php'); } /** * Forcedfully processes a campaign * * */ function adminForcefetch() { $cid = intval($_REQUEST['id']); if(! defined('DOING_AJAX')) check_admin_referer('forcefetch-campaign_'.$cid); $this->forcefetched = $this->processCampaign($cid); if(defined('DOING_AJAX')) die('1'); else $this->adminList(); } /** * Checks submitted campaign edit form for errors * * * @return array errors */ function adminCampaignRequest() { global $wpdb; # Main data $this->campaign_data = $this->campaign_structure; $this->campaign_data['main'] = array( 'title' => $_REQUEST['campaign_title'], 'active' => isset($_REQUEST['campaign_active']), 'slug' => $_REQUEST['campaign_slug'], 'template' => (isset($_REQUEST['campaign_templatechk'])) ? $_REQUEST['campaign_template'] : null, 'frequency' => intval($_REQUEST['campaign_frequency_d']) * 86400 + intval($_REQUEST['campaign_frequency_h']) * 3600 + intval($_REQUEST['campaign_frequency_m']) * 60, 'cacheimages' => (int) isset($_REQUEST['campaign_cacheimages']), 'feeddate' => (int) isset($_REQUEST['campaign_feeddate']), 'posttype' => $_REQUEST['campaign_posttype'], 'author' => sanitize_user($_REQUEST['campaign_author']), 'comment_status' => $_REQUEST['campaign_commentstatus'], 'allowpings' => (int) isset($_REQUEST['campaign_allowpings']), 'dopingbacks' => (int) isset($_REQUEST['campaign_dopingbacks']), 'max' => intval($_REQUEST['campaign_max']), 'linktosource' => (int) isset($_REQUEST['campaign_linktosource']) ); // New feeds foreach($_REQUEST['campaign_feed']['new'] as $i => $feed) { $feed = trim($feed); if(!empty($feed)) { if(!isset($this->campaign_data['feeds']['new'])) $this->campaign_data['feeds']['new'] = array(); $this->campaign_data['feeds']['new'][$i] = $feed; } } // Existing feeds to delete if(isset($_REQUEST['campaign_feed']['delete'])) { $this->campaign_data['feeds']['delete'] = array(); foreach($_REQUEST['campaign_feed']['delete'] as $feedid => $yes) $this->campaign_data['feeds']['delete'][] = intval($feedid); } // Existing feeds. if(isset($_REQUEST['id'])) { $this->campaign_data['feeds']['edit'] = array(); foreach($this->getCampaignFeeds(intval($_REQUEST['id'])) as $feed) $this->campaign_data['feeds']['edit'][$feed->id] = $feed->url; } // Categories if(isset($_REQUEST['campaign_categories'])) { foreach($_REQUEST['campaign_categories'] as $category) { $id = intval($category); $this->campaign_data['categories'][] = $category; } } # New categories if(isset($_REQUEST['campaign_newcat'])) { foreach($_REQUEST['campaign_newcat'] as $k => $on) { $catname = $_REQUEST['campaign_newcatname'][$k]; if(!empty($catname)) { if(!isset($this->campaign_data['categories']['new'])) $this->campaign_data['categories']['new'] = array(); $this->campaign_data['categories']['new'][] = $catname; } } } // Rewrites if(isset($_REQUEST['campaign_word_origin'])) { foreach($_REQUEST['campaign_word_origin'] as $id => $origin_data) { $rewrite = isset($_REQUEST['campaign_word_option_rewrite']) && isset($_REQUEST['campaign_word_option_rewrite'][$id]); $relink = isset($_REQUEST['campaign_word_option_relink']) && isset($_REQUEST['campaign_word_option_relink'][$id]); if($rewrite || $relink) { $rewrite_data = trim($_REQUEST['campaign_word_rewrite'][$id]); $relink_data = trim($_REQUEST['campaign_word_relink'][$id]); // Relink data field can't be empty if(($relink && !empty($relink_data)) || !$relink) { $regex = isset($_REQUEST['campaign_word_option_regex']) && isset($_REQUEST['campaign_word_option_regex'][$id]); $data = array(); $data['origin'] = array('search' => $origin_data, 'regex' => $regex); if($rewrite) $data['rewrite'] = $rewrite_data; if($relink) $data['relink'] = $relink_data; $this->campaign_data['rewrites'][] = $data; } } } } $errors = array('basic' => array(), 'feeds' => array(), 'categories' => array(), 'rewrite' => array(), 'options' => array()); # Main if(empty($this->campaign_data['main']['title'])) { $errors['basic'][] = __('You have to enter a campaign title', 'wpomatic'); $this->errno++; } # Feeds $feedscount = 0; if(isset($this->campaign_data['feeds']['new'])) $feedscount += count($this->campaign_data['feeds']['new']); if(isset($this->campaign_data['feeds']['edit'])) $feedscount += count($this->campaign_data['feeds']['edit']); if(isset($this->campaign_data['feeds']['delete'])) $feedscount -= count($this->campaign_data['feeds']['delete']); if(!$feedscount) { $errors['feeds'][] = __('You have to enter at least one feed', 'wpomatic'); } else { if(isset($this->campaign_data['feeds']['new'])) { foreach($this->campaign_data['feeds']['new'] as $feed) { $simplepie = $this->fetchFeed($feed, true); if($simplepie->error()) { $errors['feeds'][] = sprintf(__('Feed %s could not be parsed (SimplePie said: %s)', 'wpomatic'), $feed, $xmlerror); $this->errno++; } } } } # Categories if(! sizeof($this->campaign_data['categories'])) { $errors['categories'][] = __('Select at least one category', 'wpomatic'); $this->errno++; } # Rewrite if(sizeof($this->campaign_data['rewrites'])) { foreach($this->campaign_data['rewrites'] as $rewrite) { if($rewrite['origin']['regex']) { if(false === @preg_match($rewrite['origin']['search'], '')) { $errors['rewrites'][] = __('There\'s an error with the supplied RegEx expression', 'wpomatic'); $this->errno++; } } } } # Options if(! get_userdatabylogin($this->campaign_data['main']['author'])) { $errors['options'][] = __('Author username not found', 'wpomatic'); $this->errno++; } if(! $this->campaign_data['main']['frequency']) { $errors['options'][] = __('Selected frequency is not valid', 'wpomatic'); $this->errno++; } if(! ($this->campaign_data['main']['max'] > 0) ) { $errors['options'][] = __('Max items should be a valid number (greater than zero)', 'wpomatic'); $this->errno++; } if($this->campaign_data['main']['cacheimages'] && !is_writable($this->cachepath)) { $errors['options'][] = sprintf(__('Cache path (in Options) must be writable before enabling image caching.', 'wpomatic'), $this->adminurl . '&s=options' ); $this->errno++; } $this->errors = $errors; } /** * Creates a campaign, and runs processEdit. If processEdit fails, campaign is removed * * @return campaign id if created successfully, errors if not */ function adminProcessAdd() { global $wpdb; // Insert a campaign with dumb data $wpdb->query(WPOTools::insertQuery($this->db['campaign'], array('lastactive' => 0, 'count' => 0))); $cid = $wpdb->insert_id; // Process the edit $this->campaign_data['main']['lastactive'] = 0; $this->adminProcessEdit($cid); return $cid; } /** * Cleans everything for the given id, then redoes everything * * @param integer $id The id to edit */ function adminProcessEdit($id) { global $wpdb; // If we need to execute a tool action we stop here if($this->adminProcessTools()) return; // Delete all to recreate $wpdb->query("DELETE FROM `{$this->db['campaign_word']}` WHERE campaign_id = $id"); $wpdb->query("DELETE FROM `{$this->db['campaign_category']}` WHERE campaign_id = $id"); // Process categories # New if(isset($this->campaign_data['categories']['new'])) { foreach($this->campaign_data['categories']['new'] as $category) $this->campaign_data['categories'][] = wp_insert_category(array('cat_name' => $category)); unset($this->campaign_data['categories']['new']); } # All foreach($this->campaign_data['categories'] as $category) { // Insert $wpdb->query(WPOTools::insertQuery($this->db['campaign_category'], array('category_id' => $category, 'campaign_id' => $id) )); } // Process feeds # New if(isset($this->campaign_data['feeds']['new'])) { foreach($this->campaign_data['feeds']['new'] as $feed) $this->addCampaignFeed($id, $feed); } # Delete if(isset($this->campaign_data['feeds']['delete'])) { foreach($this->campaign_data['feeds']['delete'] as $feed) $wpdb->query("DELETE FROM `{$this->db['campaign_feed']}` WHERE id = $feed "); } // Process words foreach($this->campaign_data['rewrites'] as $rewrite) { $wpdb->query(WPOTools::insertQuery($this->db['campaign_word'], array('word' => $rewrite['origin']['search'], 'regex' => $rewrite['origin']['regex'], 'rewrite' => isset($rewrite['rewrite']), 'rewrite_to' => isset($rewrite['rewrite']) ? $rewrite['rewrite'] : '', 'relink' => isset($rewrite['relink']) ? $rewrite['relink'] : null, 'campaign_id' => $id) )); } // Main $main = $this->campaign_data['main']; // Fetch author id $author = get_userdatabylogin($this->campaign_data['main']['author']); $main['authorid'] = $author->ID; unset($main['author']); // Query $query = WPOTools::updateQuery($this->db['campaign'], $main, 'id = ' . intval($id)); $wpdb->query($query); } /** * Processes edit campaign tools actions * * */ function adminProcessTools() { global $wpdb; $id = intval($_REQUEST['id']); if(isset($_REQUEST['tool_removeall'])) { $posts = $this->getCampaignPosts($id); foreach($posts as $post) { $wpdb->query("DELETE FROM {$wpdb->posts} WHERE ID = {$post->post_id} "); } // Delete log $wpdb->query("DELETE FROM {$this->db['campaign_post']} WHERE campaign_id = {$id} "); // Update feed and campaign posts count $wpdb->query(WPOTools::updateQuery($this->db['campaign'], array('count' => 0), "id = {$id}")); $wpdb->query(WPOTools::updateQuery($this->db['campaign_feed'], array('hash' => 0, 'count' => 0), "campaign_id = {$id}")); $this->tool_success = __('All posts removed', 'wpomatic'); return true; } if(isset($_REQUEST['tool_changetype'])) { $this->adminUpdateCampaignPosts($id, array( 'post_status' => $wpdb->escape($_REQUEST['campaign_tool_changetype']) )); $this->tool_success = __('Posts status updated', 'wpomatic'); return true; } if(isset($_REQUEST['tool_changeauthor'])) { $author = get_userdatabylogin($_REQUEST['campaign_tool_changeauthor']); if($author) { $authorid = $author->ID; $this->adminUpdateCampaignPosts($id, array('post_author' => $authorid)); } else { $this->errno = 1; $this->errors = array('tools' => array(sprintf(__('Author %s not found', 'wpomatic'), attribute_escape($_REQUEST['campaign_tool_changeauthor'])))); } $this->tool_success = __('Posts status updated', 'wpomatic'); return true; } return false; } function adminUpdateCampaignPosts($id, $properties) { global $wpdb; $posts = $this->getCampaignPosts($id); foreach($posts as $post) $wpdb->query(WPOTools::updateQuery($wpdb->posts, $properties, "ID = {$post->id}")); } /** * Show logs * * */ function adminLogs() { global $wpdb; // Clean logs? if(isset($_REQUEST['clean_logs'])) { check_admin_referer('clean-logs'); $wpdb->query("DELETE FROM `{$this->db['log']}` WHERE 1=1 "); } // Logs to show per page $logs_per_page = 20; $page = isset($_REQUEST['p']) ? intval($_REQUEST['p']) : 0; $total = $wpdb->get_var("SELECT COUNT(*) as cnt FROM `{$this->db['log']}` "); $logs = $this->getLogs("page={$page}&perpage={$logs_per_page}"); $paging = paginate_links(array( 'base' => $this->adminurl . '&s=logs&%_%', 'format' => 'p=%#%', 'total' => ceil($total / $logs_per_page), 'current' => $page, 'end_size' => 3 )); include(WPOTPL . 'logs.php'); } } $wpomatic = & new WPOMatic();