Posted Post

Posted People, Posted Life.

WordPress mu Sitemap Files: sitemap-core.php

Check WordPress Mu Google XML Sitemap Solution for what this file is about.

File name sitemap-core.php, a modified version of Google Sitemaps Generator (from verison 3.1.0.1)

<?php
/*

 $Id: sitemap-core.php 48034 2008-05-27 14:36:18Z arnee $

*/

//Enable for dev! Good code doesn't generate any notices...
//error_reporting(E_ALL);
//ini_set("display_errors",1);

#region PHP5 compat functions
if (!function_exists('file_get_contents')) {
    /**
     * Replace file_get_contents()
     *
     * @category    PHP
     * @package     PHP_Compat
     * @link        http://php.net/function.file_get_contents
     * @author      Aidan Lister <aidan - php - net>
     * @version     $Revision: 1.21 $
     * @internal    resource_context is not supported
     * @since       PHP 5
     */
    function file_get_contents($filename, $incpath = false, $resource_context = null) {
        if (false === $fh = fopen($filename, 'rb', $incpath)) {
            user_error('file_get_contents() failed to open stream: No such file or directory', E_USER_WARNING);
            return false;
        }

        clearstatcache();
        if ($fsize = @filesize($filename)) {
            $data = fread($fh, $fsize);
        } else {
            $data = '';
            while (!feof($fh)) {
                $data .= fread($fh, 8192);
            }
        }

        fclose($fh);
        return $data;
    }
}

if(!function_exists('file_put_contents')) {

    if (!defined('FILE_USE_INCLUDE_PATH')) {
        define('FILE_USE_INCLUDE_PATH', 1);
    }

    if (!defined('LOCK_EX')) {
        define('LOCK_EX', 2);
    }

    if (!defined('FILE_APPEND')) {
        define('FILE_APPEND', 8);
    }

    /**
     * Replace file_put_contents()
     *
     * @category    PHP
     * @package     PHP_Compat
     * @link        http://php.net/function.file_put_contents
     * @author      Aidan Lister <aidan - php - net>
     * @version     $Revision: 1.25 $
     * @internal    resource_context is not supported
     * @since       PHP 5
     * @require     PHP 4.0.0 (user_error)
     */
    function file_put_contents($filename, $content, $flags = null, $resource_context = null) {
        // If $content is an array, convert it to a string
        if (is_array($content)) {
            $content = implode('', $content);
        }

        // If we don't have a string, throw an error
        if (!is_scalar($content)) {
            user_error('file_put_contents() The 2nd parameter should be either a string or an array',E_USER_WARNING);
            return false;
        }

        // Get the length of data to write
        $length = strlen($content);

        // Check what mode we are using
        $mode = ($flags & FILE_APPEND)?'a':'wb';

        // Check if we're using the include path
        $use_inc_path = ($flags & FILE_USE_INCLUDE_PATH)?true:false;

        // Open the file for writing
        if (($fh = @fopen($filename, $mode, $use_inc_path)) === false) {
            user_error('file_put_contents() failed to open stream: Permission denied',E_USER_WARNING);
            return false;
        }

        // Attempt to get an exclusive lock
        $use_lock = ($flags & LOCK_EX) ? true : false ;
        if ($use_lock === true) {
            if (!flock($fh, LOCK_EX)) {
                return false;
            }
        }

        // Write to the file
        $bytes = 0;
        if (($bytes = @fwrite($fh, $content)) === false) {
            $errormsg = sprintf('file_put_contents() Failed to write %d bytes to %s',$length,$filename);
            user_error($errormsg, E_USER_WARNING);
            return false;
        }

        // Close the handle
        @fclose($fh);

        // Check all the data was written
        if ($bytes != $length) {
            $errormsg = sprintf('file_put_contents() Only %d of %d bytes written, possibly out of free disk space.',$bytes,$length);
            user_error($errormsg, E_USER_WARNING);
            return false;
        }

        // Return length
        return $bytes;
    }

}
#endregion

/**
 * Represents the status (success and failures) of a building process
 * @author Arne Brachhold
 * @package sitemap
 * @since 3.0b5
 */
class GoogleSitemapGeneratorStatus {

    function GoogleSitemapGeneratorStatus() {
        $this->_startTime = $this->GetMicrotimeFloat();

        $exists = get_option("sm_status");

        if($exists === false) add_option("sm_status","","Status","no");

        $this->Save();
    }

    function Save() {
        update_option("sm_status",$this);
    }

    function Load() {
        $status = @get_option("sm_status");
        if(is_a($status,"GoogleSitemapGeneratorStatus")) return $status;
        else return null;
    }

    /**
     * @var float $_startTime The start time of the building process
     * @access private
     */
    var $_startTime = 0;

    /**
     * @var float $_endTime The end time of the building process
     * @access private
     */
    var $_endTime = 0;

    /**
     * @var bool $$_hasChanged Indicates if the sitemap content has changed
     * @access private
     */
    var $_hasChanged = true;

    /**
     * @var int $_memoryUsage The amount of memory used in bytes
     * @access private
     */
    var $_memoryUsage = 0;    

    /**
     * @var int $_lastPost The number of posts processed. This value is updated every 50 posts.
     * @access private
     */
    var $_lastPost = 0;

    /**
     * @var int $_lastTime The time when the last step-update occured. This value is updated every 50 posts.
     * @access private
     */
    var $_lastTime = 0;

    function End($hasChanged = true) {
        $this->_endTime = $this->GetMicrotimeFloat();

        $this->SetMemoryUsage();

        $this->_hasChanged = $hasChanged;

        $this->Save();
    }

    function SetMemoryUsage() {
        if(function_exists("memory_get_peak_usage")) {
            $this->_memoryUsage = memory_get_peak_usage(true);
        } else if(function_exists("memory_get_usage")) {
            $this->_memoryUsage =  memory_get_usage(true);
        }
    }

    function GetMemoryUsage() {
        return round($this->_memoryUsage / 1024 / 1024,2);
    }

    function SaveStep($postCount) {
        $this->SetMemoryUsage();
        $this->_lastPost = $postCount;
        $this->_lastTime = $this->GetMicrotimeFloat();

        $this->Save();
    }

    function GetTime() {
        return round($this->_endTime - $this->_startTime,2);
    }

    function GetStartTime() {
        return round($this->_startTime, 2);
    }

    function GetLastTime() {
        return round($this->_lastTime - $this->_startTime,2);
    }

    function GetLastPost() {
        return $this->_lastPost;
    }

    var $_usedXml = false;
    var $_xmlSuccess = false;
    var $_xmlPath = '';
    var $_xmlUrl = '';

    function StartXml($path,$url) {
        $this->_usedXml = true;
        $this->_xmlPath = $path;
        $this->_xmlUrl = $url;

        $this->Save();
    }

    function EndXml($success) {
        $this->_xmlSuccess = $success;    

        $this->Save();
    }

    var $_usedZip = false;
    var $_zipSuccess = false;
    var $_zipPath = '';
    var $_zipUrl = '';

    function StartZip($path,$url) {
        $this->_usedZip = true;
        $this->_zipPath = $path;
        $this->_zipUrl = $url;    

        $this->Save();
    }

    function EndZip($success) {
        $this->_zipSuccess = $success;

        $this->Save();
    }

    var $_usedGoogle = false;
    var $_googleUrl = '';
    var $_gooogleSuccess = false;
    var $_googleStartTime = 0;
    var $_googleEndTime = 0;

    function StartGooglePing($url) {
        $this->_googleUrl = true;
        $this->_usedGoogle = true;
        $this->_googleStartTime = $this->GetMicrotimeFloat();    

        $this->Save();
    }

    function EndGooglePing($success) {
        $this->_googleEndTime = $this->GetMicrotimeFloat();
        $this->_gooogleSuccess = $success;    

        $this->Save();
    }

    function GetGoogleTime() {
        return round($this->_googleEndTime - $this->_googleStartTime,2);
    }

    var $_usedYahoo = false;
    var $_yahooUrl = '';
    var $_yahooSuccess = false;
    var $_yahooStartTime = 0;
    var $_yahooEndTime = 0;

    function StartYahooPing($url) {
        $this->_yahooUrl = $url;
        $this->_usedYahoo = true;
        $this->_yahooStartTime = $this->GetMicrotimeFloat();

        $this->Save();
    }

    function EndYahooPing($success) {
        $this->_yahooEndTime = $this->GetMicrotimeFloat();
        $this->_yahooSuccess = $success;    

        $this->Save();
    }

    function GetYahooTime() {
        return round($this->_yahooEndTime - $this->_yahooStartTime,2);
    }

    var $_usedAsk = false;
    var $_askUrl = '';
    var $_askSuccess = false;
    var $_askStartTime = 0;
    var $_askEndTime = 0;

    function StartAskPing($url) {
        $this->_usedAsk = true;
        $this->_askUrl = $url;
        $this->_askStartTime = $this->GetMicrotimeFloat();

        $this->Save();
    }

    function EndAskPing($success) {
        $this->_askEndTime = $this->GetMicrotimeFloat();
        $this->_askSuccess = $success;    

        $this->Save();
    }

    function GetAskTime() {
        return round($this->_askEndTime - $this->_askStartTime,2);
    }

    var $_usedMsn = false;
    var $_msnUrl = '';
    var $_msnSuccess = false;
    var $_msnStartTime = 0;
    var $_msnEndTime = 0;

    function StartMsnPing($url) {
        $this->_usedMsn = true;
        $this->_msnUrl = $url;
        $this->_msnStartTime = $this->GetMicrotimeFloat();

        $this->Save();
    }

    function EndMsnPing($success) {
        $this->_msnEndTime = $this->GetMicrotimeFloat();
        $this->_msnSuccess = $success;    

        $this->Save();
    }

    function GetMsnTime() {
        return round($this->_msnEndTime - $this->_msnStartTime,2);
    }

    function GetMicrotimeFloat() {
        list($usec, $sec) = explode(" ", microtime());
        return ((float)$usec + (float)$sec);
    }
}

/**
 * Represents an item in the page list
 * @author Arne Brachhold
 * @package sitemap
 * @since 3.0
 */
class GoogleSitemapGeneratorPage {

    /**
     * @var string $_url Sets the URL or the relative path to the blog dir of the page
     * @access private
     */
    var $_url;

    /**
     * @var float $_priority Sets the priority of this page
     * @access private
     */
    var $_priority;

    /**
     * @var string $_changeFreq Sets the chanfe frequency of the page. I want Enums!
     * @access private
     */
    var $_changeFreq;

    /**
     * @var int $_lastMod Sets the lastMod date as a UNIX timestamp.
     * @access private
     */
    var $_lastMod;    

    /**
     * Initialize a new page object
     *
     * @since 3.0
     * @access public
     * @author Arne Brachhold
     * @param bool $enabled Should this page be included in thesitemap
     * @param string $url The URL or path of the file
     * @param float $priority The Priority of the page 0.0 to 1.0
     * @param string $changeFreq The change frequency like daily, hourly, weekly
     * @param int $lastMod The last mod date as a unix timestamp
     */
    function GoogleSitemapGeneratorPage($url="",$priority=0.0,$changeFreq="never",$lastMod=0) {
        $this->SetUrl($url);
        $this->SetProprity($priority);
        $this->SetChangeFreq($changeFreq);
        $this->SetLastMod($lastMod);
    }

    /**
     * Returns the URL of the page
     *
     * @return string The URL
     */
    function GetUrl() {
        return $this->_url;
    }

    /**
     * Sets the URL of the page
     *
     * @param string $url The new URL
     */
    function SetUrl($url) {
        $this->_url=(string) $url;
    }

    /**
     * Returns the priority of this page
     *
     * @return float the priority, from 0.0 to 1.0
     */
    function GetPriority() {
        return $this->_priority;
    }

    /**
     * Sets the priority of the page
     *
     * @param float $priority The new priority from 0.1 to 1.0
     */
    function SetProprity($priority) {
        $this->_priority=floatval($priority);
    }

    /**
     * Returns the change frequency of the page
     *
     * @return string The change frequncy like hourly, weekly, monthly etc.
     */
    function GetChangeFreq() {
        return $this->_changeFreq;
    }

    /**
     * Sets the change frequency of the page
     *
     * @param string $changeFreq The new change frequency
     */
    function SetChangeFreq($changeFreq) {
        $this->_changeFreq=(string) $changeFreq;
    }

    /**
     * Returns the last mod of the page
     *
     * @return int The lastmod value in seconds
     */
    function GetLastMod() {
        return $this->_lastMod;
    }

    /**
     * Sets the last mod of the page
     *
     * @param int $lastMod The lastmod of the page
     */
    function SetLastMod($lastMod) {
        $this->_lastMod=intval($lastMod);
    }    

    function Render() {

        if($this->_url == "/" || empty($this->_url)) return '';

        $r="";
        $r.= "\t<url>\n";
        $r.= "\t\t<loc>" . $this->EscapeXML($this->_url) . "</loc>\n";
        if($this->_lastMod>0) $r.= "\t\t<lastmod>" . date('Y-m-d\TH:i:s+00:00',$this->_lastMod) . "</lastmod>\n";
        if(!empty($this->_changeFreq)) $r.= "\t\t<changefreq>" . $this->_changeFreq . "</changefreq>\n";
        if($this->_priority!==false && $this->_priority!=="") $r.= "\t\t<priority>" . number_format($this->_priority,1) . "</priority>\n";
        $r.= "\t</url>\n";
        return $r;
    }    

    function EscapeXML($string) {
        return str_replace ( array ( '&', '"', "'", '<', '>'), array ( '&amp;' , '&quot;', '&apos;' , '&lt;' , '&gt;'), $string);
    }
}

class GoogleSitemapGeneratorXmlEntry {

    var $_xml;

    function GoogleSitemapGeneratorXmlEntry($xml) {
        $this->_xml = $xml;
    }

    function Render() {
        return $this->_xml;
    }
}

class GoogleSitemapGeneratorDebugEntry extends GoogleSitemapGeneratorXmlEntry {

    function Render() {
        return "<!-- " . $this->_xml . " -->";
    }
}

/**
 * Base class for all priority providers
 * @author Arne Brachhold
 * @package sitemap
 * @since 3.0
 */
class GoogleSitemapGeneratorPrioProviderBase {

    /**
     * @var int $_totalComments The total number of comments of all posts
     * @access protected
     */
    var $_totalComments=0;

    /**
     * @var int $_totalComments The total number of posts
     * @access protected
     */
    var $_totalPosts=0;

    /**
     * Returns the (translated) name of this priority provider
     *
     * @since 3.0
     * @access public
     * @author Arne Brachhold
     * @return string The translated name
    */
    function GetName() {
        return "";
    }

    /**
     * Returns the (translated) description of this priority provider
     *
     * @since 3.0
     * @access public
     * @author Arne Brachhold
     * @return string The translated description
    */
    function GetDescription() {
        return "";
    }

    /**
     * Initializes a new priority provider
     *
     * @param $totalComments int The total number of comments of all posts
     * @param $totalPosts int The total number of posts
     * @since 3.0
     * @access public
     * @author Arne Brachhold
    */
    function GoogleSitemapGeneratorPrioProviderBase($totalComments,$totalPosts) {
        $this->_totalComments=$totalComments;
        $this->_totalPosts=$totalPosts;

    }    

    /**
     * Returns the priority for a specified post
     *
     * @param $postID int The ID of the post
     * @param $commentCount int The number of comments for this post
     * @since 3.0
     * @access public
     * @author Arne Brachhold
     * @return int The calculated priority
    */
    function GetPostPriority($postID,$commentCount) {
        return 0;
    }
}

/**
 * Priority Provider which calculates the priority based on the number of comments
 * @author Arne Brachhold
 * @package sitemap
 * @since 3.0
 */
class GoogleSitemapGeneratorPrioByCountProvider extends GoogleSitemapGeneratorPrioProviderBase {

    /**
     * Returns the (translated) name of this priority provider
     *
     * @since 3.0
     * @access public
     * @author Arne Brachhold
     * @return string The translated name
    */
    function GetName() {
        return __("Comment Count",'sitemap');
    }

    /**
     * Returns the (translated) description of this priority provider
     *
     * @since 3.0
     * @access public
     * @author Arne Brachhold
     * @return string The translated description
    */
    function GetDescription() {
        return __("Uses the number of comments of the post to calculate the priority",'sitemap');
    }

    /**
     * Initializes a new priority provider which calculates the post priority based on the number of comments
     *
     * @param $totalComments int The total number of comments of all posts
     * @param $totalPosts int The total number of posts
     * @since 3.0
     * @access public
     * @author Arne Brachhold
    */
    function GoogleSitemapGeneratorPrioByCountProvider($totalComments,$totalPosts) {
        parent::GoogleSitemapGeneratorPrioProviderBase($totalComments,$totalPosts);
    }

    /**
     * Returns the priority for a specified post
     *
     * @param $postID int The ID of the post
     * @param $commentCount int The number of comments for this post
     * @since 3.0
     * @access public
     * @author Arne Brachhold
     * @return int The calculated priority
    */
    function GetPostPriority($postID,$commentCount) {
        $prio=0;
        if($this->_totalComments>0 && $commentCount>0) {
            $prio = round(($commentCount*100/$this->_totalComments)/100,1);
        } else {
            $prio = 0;
        }
        return $prio;
    }
}

/**
 * Priority Provider which calculates the priority based on the average number of comments
 * @author Arne Brachhold
 * @package sitemap
 * @since 3.0
 */
class GoogleSitemapGeneratorPrioByAverageProvider extends GoogleSitemapGeneratorPrioProviderBase {

    /**
     * @var int $_average The average number of comments per post
     * @access protected
     */
    var $_average=0.0;

    /**
     * Returns the (translated) name of this priority provider
     *
     * @since 3.0
     * @access public
     * @author Arne Brachhold
     * @return string The translated name
    */
    function GetName() {
        return __("Comment Average",'sitemap');
    }

    /**
     * Returns the (translated) description of this priority provider
     *
     * @since 3.0
     * @access public
     * @author Arne Brachhold
     * @return string The translated description
    */
    function GetDescription() {
        return __("Uses the average comment count to calculate the priority",'sitemap');
    }

    /**
     * Initializes a new priority provider which calculates the post priority based on the average number of comments
     *
     * @param $totalComments int The total number of comments of all posts
     * @param $totalPosts int The total number of posts
     * @since 3.0
     * @access public
     * @author Arne Brachhold
    */
    function GoogleSitemapGeneratorPrioByAverageProvider($totalComments,$totalPosts) {
        parent::GoogleSitemapGeneratorPrioProviderBase($totalComments,$totalPosts);

        if($this->_totalComments>0 && $this->_totalPosts>0) {
            $this->_average= (double) $this->_totalComments / $this->_totalPosts;
        }
    }

    /**
     * Returns the priority for a specified post
     *
     * @param $postID int The ID of the post
     * @param $commentCount int The number of comments for this post
     * @since 3.0
     * @access public
     * @author Arne Brachhold
     * @return int The calculated priority
    */
    function GetPostPriority($postID,$commentCount) {
        $prio = 0;
        //Do not divide by zero!
        if($this->_average==0) {
            if($commentCount>0)    $prio = 1;
            else $prio = 0;
        } else {
            $prio = $commentCount/$this->_average;
            if($prio>1) $prio = 1;
            else if($prio<0) $prio = 0;
        }

        return round($prio,1);
    }
} 

/**
 * Priority Provider which calculates the priority based on the popularity by the PopularityContest Plugin
 * @author Arne Brachhold
 * @package sitemap
 * @since 3.0
 */
class GoogleSitemapGeneratorPrioByPopularityContestProvider extends GoogleSitemapGeneratorPrioProviderBase {

    /**
     * Returns the (translated) name of this priority provider
     *
     * @since 3.0
     * @access public
     * @author Arne Brachhold
     * @return string The translated name
    */
    function GetName() {
        return __("Popularity Contest",'sitemap');
    }

    /**
     * Returns the (translated) description of this priority provider
     *
     * @since 3.0
     * @access public
     * @author Arne Brachhold
     * @return string The translated description
    */
    function GetDescription() {
        return str_replace("%4","index.php?page=popularity-contest.php",str_replace("%3","options-general.php?page=popularity-contest.php",str_replace("%2","http://www.alexking.org/",str_replace("%1","http://www.alexking.org/index.php?content=software/wordpress/content.php",__("Uses the activated <a href=\"%1\">Popularity Contest Plugin</a> from <a href=\"%2\">Alex King</a>. See <a href=\"%3\">Settings</a> and <a href=\"%4\">Most Popular Posts</a>",'sitemap')))));
    }

    /**
     * Initializes a new priority provider which calculates the post priority based on the popularity by the PopularityContest Plugin
     *
     * @param $totalComments int The total number of comments of all posts
     * @param $totalPosts int The total number of posts
     * @since 3.0
     * @access public
     * @author Arne Brachhold
    */
    function GoogleSitemapGeneratorPrioByPopularityContestProvider($totalComments,$totalPosts) {
        parent::GoogleSitemapGeneratorPrioProviderBase($totalComments,$totalPosts);
    }

    /**
     * Returns the priority for a specified post
     *
     * @param $postID int The ID of the post
     * @param $commentCount int The number of comments for this post
     * @since 3.0
     * @access public
     * @author Arne Brachhold
     * @return int The calculated priority
    */
    function GetPostPriority($postID,$commentCount) {
        //$akpc is the global instance of the Popularity Contest Plugin
        global $akpc,$posts;

        $res=0;
        //Better check if its there
        if(!empty($akpc) && is_object($akpc)) {
            //Is the method we rely on available?
        if(method_exists($akpc,"get_post_rank")) {
            if(!is_array($posts) || !$posts) $posts = array();
                if(!isset($posts[$postID])) $posts[$postID] = get_post($postID);
                //popresult comes as a percent value
                $popresult=$akpc->get_post_rank($postID);
                if(!empty($popresult) && strpos($popresult,"%")!==false) {
                    //We need to parse it to get the priority as an int (percent)
                    $matches=null;
                    preg_match("/([0-9]{1,3})\%/si",$popresult,$matches);
                    if(!empty($matches) && is_array($matches) && count($matches)==2) {
                        //Divide it so 100% = 1, 10% = 0.1
                        $res=round(intval($matches[1])/100,1);
                    }
                }
            }
        }
        return $res;
    }
}

/**
 * Class to generate a sitemaps.org Sitemaps compliant sitemap of a WordPress blog.
 *
 * @package sitemap
 * @author Arne Brachhold
 * @since 3.0
*/
class GoogleSitemapGenerator {
    /**
     * @var Version of the generator in SVN
    */
    var $_svnVersion = '$Id: sitemap-core.php 48034 2008-05-27 14:36:18Z arnee $';

    /**
     * @var array The unserialized array with the stored options
     */
    var $_options = array();

    /**
     * @var array The saved additional pages
     */
    var $_pages = array();

    /**
     * @var array The values and names of the change frequencies
     */
    var $_freqNames = array();

    /**
     * @var array A list of class names which my be called for priority calculation
     */
    var $_prioProviders = array();

    /**
     * @var bool True if init complete (options loaded etc)
     */
    var $_initiated = false;

    /**
     * @var string Holds the last error if one occurs when writing the files
     */
    var $_lastError=null;

    /**
     * @var int The last handled post ID
     */
    var $_lastPostID = 0;

    /**
     * @var bool Defines if the sitemap building process is active at the moment
     */
    var $_isActive = false;

    /**
     * @var bool Defines if the sitemap building process has been scheduled via Wp cron
     */
    var $_isScheduled = false;

    /**
     * @var object The file handle which is used to write the sitemap file
     */
    var $_fileHandle = null;

    /**
     * @var object The file handle which is used to write the zipped sitemap file
     */
    var $_fileZipHandle = null;

    /**
     * Returns the path to the blog directory
     *
     * @since 3.0
     * @access private
     * @author Arne Brachhold
     * @return string The full path to the blog directory
    */
    function GetHomePath() {

        $res="";
        //Check if we are in the admin area -> get_home_path() is avaiable
        if(function_exists("get_home_path")) {
            $res = get_home_path();
        } else {
            //get_home_path() is not available, but we can't include the admin
            //libraries because many plugins check for the "check_admin_referer"
            //function to detect if you are on an admin page. So we have to copy
            //the get_home_path function in our own...
            $home = get_option( 'home' );
            if ( $home != '' && $home != get_option( 'siteurl' ) ) {
                $home_path = parse_url( $home );
                $home_path = $home_path['path'];
                $root = str_replace( $_SERVER["PHP_SELF"], '', $_SERVER["SCRIPT_FILENAME"] );
                $home_path = trailingslashit( $root.$home_path );
            } else {
                $home_path = ABSPATH;
            }

            $res = $home_path;
        }
        global $blog_id;
        if ($blog_id != 1) $res .= 'wp-content/blogs.dir/' . $blog_id . '/files/';
        return $res;
    }

    /**
     * Returns the path to the directory where the plugin file is located
     * @since 3.0b5
     * @access private
     * @author Arne Brachhold
     * @return string The path to the plugin directory
     */
    function GetPluginPath() {
        $path = dirname(__FILE__);
        return trailingslashit(str_replace("\\","/",$path));
    }

    /**
     * Returns the URL to the directory where the plugin file is located
     * @since 3.0b5
     * @access private
     * @author Arne Brachhold
     * @return string The URL to the plugin directory
     */
    function GetPluginUrl() {
        $path = dirname(__FILE__);
        $path = str_replace("\\","/",$path);
        $path = trailingslashit(get_bloginfo('wpurl')) . trailingslashit(substr($path,strpos($path,"wp-content/")));
        return $path;
    }    

    /**
     * Returns the URL to default XSLT style if it exists
     * @since 3.0b5
     * @access private
     * @author Arne Brachhold
     * @return string The URL to the default stylesheet, empry string if not available.
     */
    function GetDefaultStyle() {
        $p = $this->GetPluginPath();
        if(file_exists($p . "sitemap.xsl")) {
            return $this->GetPluginUrl() . 'sitemap.xsl';
        }
        return '';
    }

    /**
     * Returns the path to the robots.txt file in the blog root
     *
     * @since 3.0b8
     * @access private
     * @author Arne Brachhold
     * @return The full path to the robots.txt file
     */
    function GetRobotsFilePath() {
        return trailingslashit($this->GetHomePath()) . 'robots.txt';
    }

    /**
     * Returns the URL to the robots.txt file in the blog root
     *
     * @since 3.0b8
     * @access private
     * @author Arne Brachhold
     * @return The full URL to the robots.txt file
     */
    function GetRobotsFileUrl() {
        return trailingslashit(get_bloginfo('siteurl')) . 'robots.txt';
    }

    /**
     * Sets up the default configuration
     *
     * @since 3.0
     * @access private
     * @author Arne Brachhold
    */
    function InitOptions() {

        $this->_options=array();
        $this->_options["sm_b_prio_provider"]="GoogleSitemapGeneratorPrioByAverageProvider";            //Provider for automatic priority calculation
        $this->_options["sm_b_filename"]="sitemap.xml";        //Name of the Sitemap file
        $this->_options["sm_b_debug"]=true;                    //Write debug messages in the xml file
        $this->_options["sm_b_xml"]=true;                    //Create a .xml file
        $this->_options["sm_b_gzip"]=true;                    //Create a gzipped .xml file(.gz) file
        $this->_options["sm_b_ping"]=true;                    //Auto ping Google
        $this->_options["sm_b_pingyahoo"]=true;                //Auto ping YAHOO
        $this->_options["sm_b_yahookey"]='Q89D23fV34GSeqHjRl8o5i55ojmxrmwJPyMa_195mBnBaK6Vp_NB8y5WjnIoamjikw--';                //YAHOO Application Key, default for all
        $this->_options["sm_b_pingask"]=true;                //Auto ping Ask.com
        $this->_options["sm_b_pingmsn"]=true;                //Auto ping MSN
        $this->_options["sm_b_manual_enabled"]=false;        //Allow manual creation of the sitemap via GET request
        $this->_options["sm_b_auto_enabled"]=true;            //Rebuild sitemap when content is changed
        $this->_options["sm_b_auto_delay"]=true;            //Use WP Cron to execute the building process in the background
        $this->_options["sm_b_manual_key"]=md5(microtime());//The secret key to build the sitemap via GET request
        $this->_options["sm_b_memory"] = '';                //Set Memory Limit (e.g. 16M)
        $this->_options["sm_b_time"] = -1;                    //Set time limit in seconds, 0 for unlimited, -1 for disabled
        $this->_options["sm_b_max_posts"] = -1;                //Maximum number of posts, <= 0 for all
        $this->_options["sm_b_safemode"] = false;            //Enable MySQL Safe Mode (doesn't use unbuffered results)
        $this->_options["sm_b_style"] = $this->GetDefaultStyle(); //Include a stylesheet in the XML
        $this->_options["sm_b_robots"] = false;                //Modify or create robots.txt file in blog root which contains the sitemap location
        $this->_options["sm_b_exclude"] = array();            //List of post / page IDs to exclude
        $this->_options["sm_b_location_mode"]="auto";        //Mode of location, auto or manual
        $this->_options["sm_b_filename_manual"]="";            //Manuel filename
        $this->_options["sm_b_fileurl_manual"]="";            //Manuel fileurl

        $this->_options["sm_in_home"]=true;                    //Include homepage
        $this->_options["sm_in_posts"]=true;                //Include posts
        $this->_options["sm_in_posts_sub"]=false;            //Include post pages (<!--nextpage--> tag)
        $this->_options["sm_in_pages"]=true;                //Include static pages
        $this->_options["sm_in_cats"]=false;                //Include categories
        $this->_options["sm_in_arch"]=false;                //Include archives
        $this->_options["sm_in_auth"]=false;                //Include author pages
        $this->_options["sm_in_tags"]=false;                //Include tag pages

        $this->_options["sm_cf_home"]="daily";                //Change frequency of the homepage
        $this->_options["sm_cf_posts"]="monthly";            //Change frequency of posts
        $this->_options["sm_cf_pages"]="weekly";            //Change frequency of static pages
        $this->_options["sm_cf_cats"]="weekly";                //Change frequency of categories
        $this->_options["sm_cf_auth"]="weekly";                //Change frequency of author pages
        $this->_options["sm_cf_arch_curr"]="daily";            //Change frequency of the current archive (this month)
        $this->_options["sm_cf_arch_old"]="yearly";            //Change frequency of older archives
        $this->_options["sm_cf_tags"]="weekly";                //Change frequency of tags

        $this->_options["sm_pr_home"]=1.0;                    //Priority of the homepage
        $this->_options["sm_pr_posts"]=0.6;                    //Priority of posts (if auto prio is disabled)
        $this->_options["sm_pr_posts_min"]=0.2;                //Minimum Priority of posts, even if autocalc is enabled
        $this->_options["sm_pr_pages"]=0.6;                    //Priority of static pages
        $this->_options["sm_pr_cats"]=0.3;                    //Priority of categories
        $this->_options["sm_pr_arch"]=0.3;                    //Priority of archives
        $this->_options["sm_pr_auth"]=0.3;                    //Priority of author pages
        $this->_options["sm_pr_tags"]=0.3;                    //Priority of tags    

        $this->_options["sm_i_donated"]=false;                //Did you donate? Thank you! :)
        $this->_options["sm_i_hide_donated"]=false;            //And hide the thank you..
        $this->_options["sm_i_install_date"]=time();        //The installation date
        $this->_options["sm_i_hide_note"]=false;            //Hide the note which appears after 30 days
        $this->_options["sm_i_hide_donors"]=false;            //Hide the list of donations
    }

    /**
     * Loads the configuration from the database
     *
     * @since 3.0
     * @access private
     * @author Arne Brachhold
    */
    function LoadOptions() {

        $this->InitOptions();

        //First init default values, then overwrite it with stored values so we can add default
        //values with an update which get stored by the next edit.
        $storedoptions=get_option("sm_options");
        if($storedoptions && is_array($storedoptions)) {
            foreach($storedoptions AS $k=>$v) {
                $this->_options[$k]=$v;
            }
        } else update_option("sm_options",$this->_options); //First time use, store default values
    }

    /**
     * Initializes a new Google Sitemap Generator
     *
     * @since 3.0
     * @access private
     * @author Arne Brachhold
    */
    function GoogleSitemapGenerator() {

    }

    /**
     * Returns the version of the generator
     *
     * @since 3.0
     * @access public
     * @author Arne Brachhold
     * @return int The version
    */
    function GetVersion() {
        return GoogleSitemapGeneratorLoader::GetVersion();
    }

    /**
     * Returns all parent classes of a class
     *
     * @param $className string The name of the class
     *
     * @since 3.0
     * @access private
     * @author Arne Brachhold
     * @return array An array which contains the names of the parent classes
    */
    function GetParentClasses($classname) {
        $parent = get_parent_class($classname);
        $parents = array();
        if (!empty($parent)) {
            $parents = $this->GetParentClasses($parent);
            $parents[] = strtolower($parent);
        }
        return $parents;
    }

    /**
     * Returns if a class is a subclass of another class
     *
     * @param $className string The name of the class
     * @param $$parentName string The name of the parent class
     *
     * @since 3.0
     * @access private
     * @author Arne Brachhold
     * @return bool true if the given class is a subclass of the other one
    */
    function IsSubclassOf($className, $parentName) {

        $className = strtolower($className);
        $parentName = strtolower($parentName);

        if(empty($className) || empty($parentName) || !class_exists($className) || !class_exists($parentName)) return false;

        $parents=$this->GetParentClasses($className);

        return in_array($parentName,$parents);
    }

    /**
     * Loads up the configuration and validates the prioity providers
     *
     * This method is only called if the sitemaps needs to be build or the admin page is displayed.
     *
     * @since 3.0
     * @access private
     * @author Arne Brachhold
    */
    function Initate() {
        if(!$this->_initiated) {

            //Loading language file...
            //load_plugin_textdomain('sitemap');
            //Hmm, doesn't work if the plugin file has its own directory.
            //Let's make it our way... load_plugin_textdomain() searches only in the wp-content/plugins dir.
            $currentLocale = get_locale();
            if(!empty($currentLocale)) {
                $moFile = dirname(__FILE__) . "/lang/sitemap-" . $currentLocale . ".mo";
                if(@file_exists($moFile) && is_readable($moFile)) load_textdomain('sitemap', $moFile);
            }

            $this->_freqNames = array(
                "always"=>__("Always","sitemap"),
                "hourly"=>__("Hourly","sitemap"),
                "daily"=>__("Daily","sitemap"),
                "weekly"=>__("Weekly","sitemap"),
                "monthly"=>__("Monthly","sitemap"),
                "yearly"=>__("Yearly","sitemap"),
                "never"=>__("Never","sitemap")
            );

            $this->LoadOptions();
            $this->LoadPages();

            //Register our own priority providers
            add_filter("sm_add_prio_provider",array(&$this, 'AddDefaultPrioProviders'));

            //Let other plugins register their providers
            $r = apply_filters("sm_add_prio_provider",$this->_prioProviders);

            //Check if no plugin return null
            if($r != null) $this->_prioProviders = $r;        

            $this->ValidatePrioProviders();

            $this->_initiated = true;
        }
    }

    /**
     * Returns the instance of the Sitemap Generator
     *
     * @since 3.0
     * @access public
     * @return GoogleSitemapGenerator The instance or null if not available.
     * @author Arne Brachhold
    */
    function &GetInstance() {
        if(isset($GLOBALS["sm_instance"])) {
            return $GLOBALS["sm_instance"];
        } else return null;
    }

    /**
     * Returns if the sitemap building process is currently active
     *
     * @since 3.0
     * @access public
     * @return bool true if active
     * @author Arne Brachhold
    */
    function IsActive() {
        $inst = &GoogleSitemapGenerator::GetInstance();
        return ($inst != null && $inst->_isActive);
    }

    /**
     * Returns if the compressed sitemap was activated
     *
     * @since 3.0b8
     * @access private
     * @author Arne Brachhold
     * @return true if compressed
     */
    function IsGzipEnabled() {
        return ($this->GetOption("b_gzip")===true && function_exists("gzwrite"));
    }

    /**
     * Returns if this version of WordPress supports the new taxonomy system
     *
     * @since 3.0b8
     * @access private
     * @author Arne Brachhold
     * @return true if supported
     */
    function IsTaxonomySupported() {
        return (function_exists("get_taxonomy") && function_exists("get_terms"));
    }

    /**
     * Enables the Google Sitemap Generator and registers the WordPress hooks
     *
     * @since 3.0
     * @access public
     * @author Arne Brachhold
    */
    function Enable() {
        if(!isset($GLOBALS["sm_instance"])) {
            $GLOBALS["sm_instance"]=new GoogleSitemapGenerator();
        }
    }

    /**
     * Checks if sitemap building after content changed is enabled and rebuild the sitemap
     *
     * @param int $postID The ID of the post to handle. Used to avoid double rebuilding if more than one hook was fired.
     * @since 3.0
     * @access public
     * @author Arne Brachhold
    */
    function CheckForAutoBuild($postID) {
        $this->Initate();
        //Build one time per post and if not importing.
        if($this->GetOption("b_auto_enabled")===true && $this->_lastPostID != $postID && (!defined('WP_IMPORTING') || WP_IMPORTING != true)) {
            $this->_lastPostID = $postID;

            //Build the sitemap directly or schedule it with WP cron
            if($this->GetOption("b_auto_delay")==true) {
                if(!$this->_isScheduled) {
                    //Schedule in 15 seconds, this should be enough to catch all changes.
                    //Clear all other existing hooks, so the sitemap is only built once.
                    wp_clear_scheduled_hook('sm_build_cron');
                    wp_schedule_single_event(time()+15,'sm_build_cron');
                    $this->_isScheduled = true;
                }
            } else {
                $this->BuildSitemap();
            }
        }
    }

    /**
     * Checks if the rebuild request was send and starts to rebuilt the sitemap
     *
     * @since 3.0
     * @access public
     * @author Arne Brachhold
    */
    function CheckForManualBuild() {
        if(!empty($_GET["sm_command"]) && !empty($_GET["sm_key"])) {
            $this->Initate();
            if($this->GetOption("b_manual_enabled")===true && $_GET["sm_command"]=="build" && $_GET["sm_key"]==$this->GetOption("b_manual_key")) {
                $this->BuildSitemap();
                echo "DONE";
                exit;
            }
        }
    }

    /**
     * Validates all given Priority Providers by checking them for required methods and existence
     *
     * @since 3.0
     * @access private
     * @author Arne Brachhold
    */
    function ValidatePrioProviders() {
        $validProviders=array();

        for($i=0; $i<count($this->_prioProviders); $i++) {
            if(class_exists($this->_prioProviders[$i])) {
                if($this->IsSubclassOf($this->_prioProviders[$i],"GoogleSitemapGeneratorPrioProviderBase")) {
                    array_push($validProviders,$this->_prioProviders[$i]);
                }
            }
        }
        $this->_prioProviders=$validProviders;

        if(!$this->GetOption("b_prio_provider")) {
            if(!in_array($this->GetOption("b_prio_provider"),$this->_prioProviders,true)) {
                $this->SetOption("b_prio_provider","");
            }
        }
    }

    /**
     * Adds the default Priority Providers to the provider list
     *
     * @since 3.0
     * @access private
     * @author Arne Brachhold
    */
    function AddDefaultPrioProviders($providers) {
        array_push($providers,"GoogleSitemapGeneratorPrioByCountProvider");
        array_push($providers,"GoogleSitemapGeneratorPrioByAverageProvider");
        if(class_exists("ak_popularity_contest")) {
            array_push($providers,"GoogleSitemapGeneratorPrioByPopularityContestProvider");
        }
        return $providers;
    }

    /**
     * Loads the stored pages from the database
     *
     * @since 3.0
     * @access private
     * @author Arne Brachhold
    */
    function LoadPages() {
        global $wpdb;

        $needsUpdate=false;

        $pagesString=$wpdb->get_var("SELECT option_value FROM $wpdb->options WHERE option_name = 'sm_cpages'");

        //Class sm_page was renamed with 3.0 -> rename it in serialized value for compatibility
        if(!empty($pagesString) && strpos($pagesString,"sm_page")!==false) {
            $pagesString = str_replace("O:7:\"sm_page\"","O:26:\"GoogleSitemapGeneratorPage\"",$pagesString);
            $needsUpdate=true;
        }

        if(!empty($pagesString)) {
            $storedpages=unserialize($pagesString);
            $this->_pages=$storedpages;
        } else {
            $this->_pages=array();
        }    

        if($needsUpdate) $this->SavePages();
    }

    /**
     * Saved the additional pages back to the database
     *
     * @since 3.0
     * @access private
     * @author Arne Brachhold
     * @return true on success
    */
    function SavePages() {
        $oldvalue = get_option("sm_cpages");
        if($oldvalue == $this->_pages) {
            return true;
        } else {
            delete_option("sm_cpages");
            //Add the option, Note the autoload=false because when the autoload happens, our class GoogleSitemapGeneratorPage doesn't exist
            add_option("sm_cpages",$this->_pages,"Storage for custom pages of the sitemap plugin","no");
            return true;
        }
    }

    /**
     * Returns the URL for the sitemap file
     *
     * @since 3.0
     * @access private
     * @author Arne Brachhold
     * @param bool $forceAuto Force the return value to the autodetected value.
     * @return The URL to the Sitemap file
    */
    function GetXmlUrl($forceAuto=false) {

        if(!$forceAuto && $this->GetOption("b_location_mode")=="manual") {
            return $this->GetOption("b_fileurl_manual");
        } else {
            return trailingslashit(get_bloginfo('siteurl')). $this->GetOption("b_filename");
        }
    }

    /**
     * Returns the URL for the gzipped sitemap file
     *
     * @since 3.0
     * @access private
     * @author Arne Brachhold
     * @param bool $forceAuto Force the return value to the autodetected value.
     * @return The URL to the gzipped Sitemap file
    */
    function GetZipUrl($forceAuto=false) {
        return $this->GetXmlUrl($forceAuto) . ".gz";
    }

    /**
     * Returns the file system path to the sitemap file
     *
     * @since 3.0
     * @access private
     * @author Arne Brachhold
     * @param bool $forceAuto Force the return value to the autodetected value.
     * @return The file system path;
    */
    function GetXmlPath($forceAuto=false) {
        if(!$forceAuto && $this->GetOption("b_location_mode")=="manual") {
            return $this->GetOption("b_filename_manual");
        } else {
            return $this->GetHomePath()  . $this->GetOption("b_filename");
        }
    }

    /**
     * Returns the file system path to the gzipped sitemap file
     *
     * @since 3.0
     * @access private
     * @author Arne Brachhold
     * @param bool $forceAuto Force the return value to the autodetected value.
     * @return The file system path;
    */
    function GetZipPath($forceAuto=false) {
        return $this->GetXmlPath($forceAuto) . ".gz";
    }

    /**
     * Returns the option value for the given key
     * Alias for getOption
     *
     * @since 3.0
     * @access private
     * @author Arne Brachhold
     * @param $key string The Configuration Key
     * @return mixed The value
    */
    function Go($key) {
        return $this->getOption($key);
    }

    /**
     * Returns the option value for the given key
     *
     * @since 3.0
     * @access private
     * @author Arne Brachhold
     * @param $key string The Configuration Key
     * @return mixed The value
     */
    function GetOption($key) {
        if(strpos($key,"sm_")!==0) $key="sm_" . $key;
        if(array_key_exists($key,$this->_options)) {
            return $this->_options[$key];
        } else return null;
    }

    /**
     * Sets an option to a new value
     *
     * @since 3.0
     * @access private
     * @author Arne Brachhold
     * @param $key string The configuration key
     * @param $value mixed The new object
     */
    function SetOption($key,$value) {
        if(strstr($key,"sm_")!==0) $key="sm_" . $key;

        $this->_options[$key]=$value;
    }

    /**
     * Saves the options back to the database
     *
     * @since 3.0
     * @access private
     * @author Arne Brachhold
     * @return bool true on success
     */
    function SaveOptions() {
        $oldvalue = get_option("sm_options");
        if($oldvalue == $this->_options) {
            return true;
        } else return update_option("sm_options",$this->_options);
    }

    /**
     * Retrieves the number of comments of a post in a asso. array
     * The key is the postID, the value the number of comments
     *
     * @since 3.0
     * @access private
     * @author Arne Brachhold
     * @return array An array with postIDs and their comment count
     */
    function GetComments() {
        global $wpdb;
        $comments=array();

        //Query comments and add them into the array
        $commentRes=$wpdb->get_results("SELECT `comment_post_ID` as `post_id`, COUNT(comment_ID) as `comment_count` FROM `" . $wpdb->comments . "` WHERE `comment_approved`='1' GROUP BY `comment_post_ID`");
        if($commentRes) {
            foreach($commentRes as $comment) {
                $comments[$comment->post_id]=$comment->comment_count;
            }
        }
        return $comments;
    }

    /**
     * Calculates the full number of comments from an sm_getComments() generated array
     *
     * @since 3.0
     * @access private
     * @author Arne Brachhold
     * @param $comments array The Array with posts and c0mment count
     * @see sm_getComments
     * @return The full number of comments
     */
    function GetCommentCount($comments) {
        $commentCount=0;
        foreach($comments AS $k=>$v) {
            $commentCount+=$v;
        }
        return $commentCount;
    }

    /**
     * Adds a url to the sitemap. You can use this method or call AddElement directly.
     *
     * @since 3.0
     * @access public
     * @author Arne Brachhold
     * @param $loc string The location (url) of the page
     * @param $lastMod int The last Modification time as a UNIX timestamp
     * @param $changeFreq string The change frequenty of the page, Valid values are "always", "hourly", "daily", "weekly", "monthly", "yearly" and "never".
     * @param $priorty float The priority of the page, between 0.0 and 1.0
     * @see AddElement
     * @return string The URL node
     */
    function AddUrl($loc,$lastMod=0,$changeFreq="monthly",$priority=0.5) {
        $page = new GoogleSitemapGeneratorPage($loc,$priority,$changeFreq,$lastMod);

        $this->AddElement($page);
    }

    /**
     * Adds an element to the sitemap
     *
     * @since 3.0
     * @access private
     * @author Arne Brachhold
     * @param $page The element
     */
    function AddElement(&$page) {
        if(empty($page)) return;

        $s = $page->Render();

        if($this->_fileZipHandle && $this->IsGzipEnabled()) {
            gzwrite($this->_fileZipHandle,$s);
        }

        if($this->_fileHandle && $this->GetOption("b_xml")) {
            fwrite($this->_fileHandle,$s);
        }
    }

    /**
     * Checks if a file is writable and tries to make it if not.
     *
     * @since 3.05b
     * @access private
     * @author  VJTD3 <http://www.VJTD3.com>
     * @return bool true if writable
     */
    function IsFileWritable($filename) {
        //can we write?
        if(!is_writable($filename)) {
            //no we can't.
            if(!@chmod($filename, 0666)) {
                $pathtofilename = dirname($filename);
                //Lets check if parent directory is writable.
                if(!is_writable($pathtofilename)) {
                    //it's not writeable too.
                    if(!@chmod($pathtoffilename, 0666)) {
                        //darn couldn't fix up parrent directory this hosting is foobar.
                        //Lets error because of the permissions problems.
                        return false;
                    }
                }
            }
        }
        //we can write, return 1/true/happy dance.
        return true;
    }

    /**
     * Creates or opens the robots.txt in blog root and inserts the sitemap location
     *
     * @since 3.0b8
     * @access private
     * @author Arne Brachhold
     * @return true on success
     */
    function WriteRobotsFile() {
        $file = $this->GetRobotsFilePath();

        $marker = 'XML-SITEMAP-PLUGIN';

        $current = extract_from_markers($file,$marker);
        if(is_array($current)) $current = $current[0];

        $smUrl = $this->GetXmlUrl();
        if($this->IsGzipEnabled()) {
            $smUrl = $this->GetZipUrl();
        }

        $new = "Sitemap: " . $smUrl;

        if($current != $new) {
            if($this->IsFileWritable($file)) return insert_with_markers($file,$marker,array($new));
            else return false;
        }
        return true;
    }

    /**
     * Builds the sitemap and writes it into a xml file.
     *
     * @since 3.0
     * @access public
     * @author Arne Brachhold <himself [at] arnebrachhold [dot] de>
     * @return array An array with messages such as failed writes etc.
     */
    function BuildSitemap() {
        global $wpdb, $posts, $wp_version;
        $this->Initate();

        if($this->GetOption("b_memory")!='') {
            @ini_set("memory_limit",$this->GetOption("b_memory"));
        }

        if($this->GetOption("sm_b_time")!=-1) {
            @set_time_limit($this->GetOption("sm_b_time"));
        }        

        //This object saves the status information of the script directly to the database
        $status = new GoogleSitemapGeneratorStatus();

        //Other plugins can detect if the building process is active
        $this->_isActive = true;

        //$this->AddElement(new GoogleSitemapGeneratorXmlEntry());

        //Debug mode?
        $debug=$this->GetOption("b_debug");

        if($this->GetOption("b_xml")) {
            $fileName = $this->GetXmlPath();
            $status->StartXml($this->GetXmlPath(),$this->GetXmlUrl());

            if($this->IsFileWritable($fileName)) {

                $this->_fileHandle = fopen($fileName,"w");
                if(!$this->_fileHandle) $status->EndXml(false,"Not openable");

            } else $status->EndXml(false,"not writable");
        }

        //Write gzipped sitemap file
        if($this->IsGzipEnabled()) {
            $fileName = $this->GetZipPath();
            $status->StartZip($this->GetZipPath(),$this->GetZipUrl());

            if($this->IsFileWritable($fileName)) {

                $this->_fileZipHandle = gzopen($fileName,"w1");
                if(!$this->_fileZipHandle) $status->EndZip(false,"Not openable");

            } else $status->EndZip(false,"not writable");
        }    

        if(!$this->_fileHandle && !$this->_fileZipHandle) {
            $status->End();
            return;
        }    

        //Content of the XML file
        $this->AddElement(new GoogleSitemapGeneratorXmlEntry('<?xml version="1.0" encoding="UTF-8"' . '?' . '>'));

        if($this->GetOption("b_style")!='') {
            $this->AddElement(new GoogleSitemapGeneratorXmlEntry('<' . '?xml-stylesheet type="text/xsl" href="' . $this->GetOption("b_style") . '"?' . '>'));
        }

        $this->AddElement(new GoogleSitemapGeneratorDebugEntry("generator=\"wordpress/" . get_bloginfo('version') . "\""));
        $this->AddElement(new GoogleSitemapGeneratorDebugEntry("sitemap-generator-url=\"http://www.arnebrachhold.de\" sitemap-generator-version=\"" . $this->GetVersion() . "\""));
        $this->AddElement(new GoogleSitemapGeneratorDebugEntry("generated-on=\"" . date(get_option("date_format") . " " . get_option("time_format")) . "\""));

        //All comments as an asso. Array (postID=>commentCount)
        $comments=($this->GetOption("b_prio_provider")!=""?$this->GetComments():array());

        //Full number of comments
        $commentCount=(count($comments)>0?$this->GetCommentCount($comments):0);

        if($debug && $this->GetOption("b_prio_provider")!="") {
            $this->AddElement(new GoogleSitemapGeneratorDebugEntry("Debug: Total comment count: " . $commentCount));
        }

        //Go XML!
        $this->AddElement(new GoogleSitemapGeneratorXmlEntry('<urlset xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.sitemaps.org/schemas/sitemap/0.9 http://www.sitemaps.org/schemas/sitemap/0.9/sitemap.xsd" xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">'));

        $home = get_bloginfo('url');

        //Add the home page (WITH a slash!)
        if($this->GetOption("in_home")) {
            $this->AddUrl(trailingslashit($home),$this->GetTimestampFromMySql(get_lastpostmodified('GMT')),$this->GetOption("cf_home"),$this->GetOption("pr_home"));
        }

        //Add the posts
        if($this->GetOption("in_posts") || $this->GetOption("in_pages")) {

            if($debug) $this->AddElement(new GoogleSitemapGeneratorDebugEntry("Debug: Start Postings"));

            //Pre 2.1 compatibility. 2.1 introduced 'future' as post_status so we don't need to check post_date
            $wpCompat = (floatval($wp_version) < 2.1);

            $postPageStmt = '';

            $inSubPages = ($this->GetOption('sm_in_posts_sub')===true);

            if($inSubPages && $this->GetOption('in_posts')===true) {
                $pageDivider='<!--nextpage-->';
                $postPageStmt = ", (character_length(`post_content`)  - character_length(REPLACE(`post_content`, '$pageDivider', ''))) / " . strlen($pageDivider) . " as postPages";
            }

            $sql="SELECT `ID`, `post_author`, `post_date`, `post_date_gmt`, `post_status`, `post_name`, `post_modified`, `post_modified_gmt`, `post_parent`, `post_type` $postPageStmt FROM `" . $wpdb->posts . "` WHERE ";

            $where = '(';

            if($this->GetOption('in_posts')) {
                //WP < 2.1: posts are post_status = publish
                //WP >= 2.1: post_type must be 'post', no date check required because future posts are post_status='future'
                if($wpCompat) $where.="(post_status = 'publish' AND post_date_gmt <= '" . gmdate('Y-m-d H:i:59') . "')";
                else $where.=" (post_status = 'publish' AND (post_type = 'post' OR post_type = '')) ";
            }

            if($this->GetOption('in_pages')) {
                if($this->GetOption('in_posts')) {
                    $where.=" OR ";
                }
                if($wpCompat) {
                    //WP < 2.1: posts have post_status = published, pages have post_status = static
                    $where.=" post_status='static' ";
                } else {
                    //WP >= 2.1: posts have post_type = 'post' and pages have post_type = 'page'. Both must be published.
                    $where.=" (post_status = 'publish' AND post_type = 'page') ";
                }
            }

            $where.=") ";

            $excludes = $this->GetOption('b_exclude');

            if(is_array($excludes) && count($excludes)>0) {
                $where.=" AND ID NOT IN ('" . implode("','",$excludes) . "')";
            }

            $where.=" AND post_password='' ORDER BY post_modified DESC";

            $sql .= $where;

            if($this->GetOption("sm_b_max_posts")>0) {
                $sql.=" LIMIT 0," . $this->GetOption("sm_b_max_posts");
            }    

            $postCount = intval($wpdb->get_var("SELECT COUNT(*) AS cnt FROM `" . $wpdb->posts . "` WHERE ". $where,0,0));

            //Create a new connection because we are using mysql_unbuffered_query and don't want to disturb the WP connection
            //Safe Mode for other plugins which use mysql_query() without a connection handler and will destroy our resultset :(
            $con = $postRes = null;
            if($this->GetOption("b_safemode")===true) {
                $postRes = mysql_query($sql,$wpdb->dbh);
                if(!$postRes) {
                    trigger_error("MySQL query failed: " . mysql_error(),E_USER_NOTICE); //E_NOTE will be displayed on our debug mode
                    return;
                }
            } else {
                $con = mysql_connect(DB_HOST,DB_USER,DB_PASSWORD,true);
                if(!$con) {
                    trigger_error("MySQL Connection failed: " . mysql_error(),E_USER_NOTICE);
                    return;
                }
                if(!mysql_select_db(DB_NAME,$con)) {
                    trigger_error("MySQL DB Select failed: " . mysql_error(),E_USER_NOTICE);
                    return;
                }
                $postRes = mysql_unbuffered_query($sql,$con);

                if(!$postRes) {
                    trigger_error("MySQL unbuffered query failed: " . mysql_error(),E_USER_NOTICE);
                    return;
                }
            }

            if($postRes) {

                //#type $prioProvider GoogleSitemapGeneratorPrioProviderBase
                $prioProvider=NULL;

                if($this->GetOption("b_prio_provider") != '') {
                    $providerClass=$this->GetOption('b_prio_provider');
                    $prioProvider = new $providerClass($commentCount,$postCount);
                }

                //$posts is used by Alex King's Popularity Contest plugin
                //if($posts == null || !is_array($posts)) {
                //    $posts = &$postRes;
                //}

                $z = 1;
                $zz = 1;

                //Default priorities
                $default_prio_posts = $this->GetOption('pr_posts');
                $default_prio_pages = $this->GetOption('pr_pages');

                //Change frequencies
                $cf_pages = $this->GetOption('sm_cf_pages');
                $cf_posts = $this->GetOption('sm_cf_posts');

                $minPrio=$this->GetOption('pr_posts_min');

                $useQTransLate = false; //function_exists('qtrans_convertURL'); Not ready yet

                //Cycle through all posts and add them
                while($post = mysql_fetch_object($postRes)) {

                    //Fill the cache with our DB result. Since it's incomplete (no text-content for example), we will clean it later.
                    $cache = array(&$post);
                    update_post_cache($cache);

                    $permalink = get_permalink($post->ID);
                    if($permalink != $home) {

                        $isPage = false;
                        if($wpCompat) {
                            $isPage = ($post->post_status == 'static');
                        } else {
                            $isPage = ($post->post_type == 'page');
                        }

                        //Set the current working post
                        $GLOBALS['post'] = &$post;

                        //Default Priority if auto calc is disabled
                        $prio = 0;

                        if($isPage) {
                            //Priority for static pages
                            $prio = $default_prio_pages;
                        } else {
                            //Priority for normal posts
                            $prio = $default_prio_posts;
                        }

                        //If priority calc. is enabled, calculate (but only for posts, not pages)!
                        if($prioProvider !== null && !$isPage) {

                            //Comment count for this post
                            $cmtcnt = (isset($comments[$post->ID])?$comments[$post->ID]:0);
                            $prio = $prioProvider->GetPostPriority($post->ID,$cmtcnt);

                            if($debug) $this->AddElement(new GoogleSitemapGeneratorDebugEntry('Debug: Priority report of postID ' . $post->ID . ': Comments: ' . $cmtcnt . ' of ' . $commentCount . ' = ' . $prio . ' points'));
                        }    

                        if(!$isPage && $minPrio>0 && $prio<$minPrio) {
                            $prio = $minPrio;
                        }

                        //Add it
                        $this->AddUrl($permalink,$this->GetTimestampFromMySql(($post->post_modified_gmt && $post->post_modified_gmt!='0000-00-00 00:00:00'?$post->post_modified_gmt:$post->post_date_gmt)),($isPage?$cf_pages:$cf_posts),$prio);

                        if($inSubPages) {
                            $subPage = '';
                            for($p =1; $p<=$post->postPages; $p++) {
                                if(get_option('permalink_structure') == '') {
                                    $subPage = $permalink . '&amp;page=' . $p;
                                } else {
                                    $subPage = trailingslashit($permalink) . user_trailingslashit($p, 'single_paged');
                                }    

                                $this->AddUrl($subPage,$this->GetTimestampFromMySql(($post->post_modified_gmt && $post->post_modified_gmt!='0000-00-00 00:00:00'?$post->post_modified_gmt:$post->post_date_gmt)),($isPage?$cf_pages:$cf_posts),$prio);
                            }
                        }

                       // Multilingual Support with qTranslate, thanks to Qian Qin
                       if($useQTransLate) {
                           global $q_config;
                           foreach($q_config['enabled_languages'] as $language) {
                               if($language != $q_config['default_language']) {
                                   $this->AddUrl(qtrans_convertURL($permalink,$language),$this->GetTimestampFromMySql(($post->post_modified_gmt && $post->post_modified_gmt!='0000-00-00 00:00:00'?$post->post_modified_gmt:$post->post_date_gmt)),($isPage?$cf_pages:$cf_posts),$prio);
                               }
                           }
                       }
                    }

                    //Update the status every 100 posts and at the end.
                    //If the script breaks because of memory or time limit,
                    //we have a "last reponded" value which can be compared to the server settings
                    if($zz==100 || $z == $postCount) {
                        $status->SaveStep($z);
                        $zz=0;
                    } else $zz++;

                    $z++;

                    //Clean cache because it's incomplete
                    if(version_compare($wp_version,"2.5",">=")) {
                        //WP 2.5 makes a mysql query for every clean_post_cache to clear the child cache
                        //so I've vopied the function here until a patch arrives...
                        wp_cache_delete($post->ID, 'posts');
                        wp_cache_delete($post->ID, 'post_meta');
                        clean_object_term_cache($post->ID, 'post');
                    } else {
                        clean_post_cache($post->ID);
                    }
                }
                unset($postRes);
                unset($prioProvider);

                if($this->GetOption("b_safemode")!==true && $con) mysql_close($con);
            }
            if($debug) $this->AddElement(new GoogleSitemapGeneratorDebugEntry("Debug: End Postings"));
        }

        //Add the cats
        if($this->GetOption("in_cats")) {
            if($debug) $this->AddElement(new GoogleSitemapGeneratorDebugEntry("Debug: Start Cats"));

            if(!$this->IsTaxonomySupported()) {

                $catsRes=$wpdb->get_results("
                            SELECT
                                c.cat_ID AS ID,
                                MAX(p.post_modified_gmt) AS last_mod
                            FROM
                                `" . $wpdb->categories . "` c,
                                `" . $wpdb->post2cat . "` pc,
                                `" . $wpdb->posts . "` p
                            WHERE
                                pc.category_id = c.cat_ID
                                AND p.ID = pc.post_id
                                AND p.post_status = 'publish'
                                AND p.post_type='post'
                            GROUP
                                BY c.cat_id
                            ");
                if($catsRes) {
                    foreach($catsRes as $cat) {
                        if($cat && $cat->ID && $cat->ID>0) {
                            if($debug) if($debug) $this->AddElement(new GoogleSitemapGeneratorDebugEntry("Cat-ID:" . $cat->ID));
                            $this->AddUrl(get_category_link($cat->ID),$this->GetTimestampFromMySql($cat->last_mod),$this->GetOption("cf_cats"),$this->GetOption("pr_cats"));
                        }
                    }
                }
            } else {
                $cats = get_terms("category",array("hide_empty"=>true,"hierarchical"=>false));
                if($cats && is_array($cats) && count($cats)>0) {
                    foreach($cats AS $cat) {
                        $this->AddUrl(get_category_link($cat->term_id),0,$this->GetOption("cf_cats"),$this->GetOption("pr_cats"));
                    }
                }
            }
            if($debug) $this->AddElement(new GoogleSitemapGeneratorDebugEntry("Debug: End Cats"));
        }

        //Add the archives
        if($this->GetOption("in_arch")) {
            if($debug) $this->AddElement(new GoogleSitemapGeneratorDebugEntry("Debug: Start Archive"));
            $now = current_time('mysql');

            //WP2.1 introduced post_status='future', for earlier WP versions we need to check the post_date_gmt
            $arcresults = $wpdb->get_results("
                        SELECT DISTINCT
                            YEAR(post_date_gmt) AS `year`,
                            MONTH(post_date_gmt) AS `month`,
                            MAX(post_date_gmt) as last_mod,
                            count(ID) as posts
                        FROM
                            $wpdb->posts
                        WHERE
                            post_date < '$now'
                            AND post_status = 'publish'
                            AND post_type = 'post'
                            " . (floatval($wp_version) < 2.1?"AND {$wpdb->posts}.post_date_gmt <= '" . gmdate('Y-m-d H:i:59') . "'":"") . "
                        GROUP BY
                            YEAR(post_date_gmt),
                            MONTH(post_date_gmt)
                        ORDER BY
                            post_date_gmt DESC");
            if ($arcresults) {
                foreach ($arcresults as $arcresult) {

                    $url  = get_month_link($arcresult->year,   $arcresult->month);
                    $changeFreq="";

                    //Archive is the current one
                    if($arcresult->month==date("n") && $arcresult->year==date("Y")) {
                        $changeFreq=$this->GetOption("cf_arch_curr");
                    } else { // Archive is older
                        $changeFreq=$this->GetOption("cf_arch_old");
                    }

                    $this->AddUrl($url,$this->GetTimestampFromMySql($arcresult->last_mod),$changeFreq,$this->GetOption("pr_arch"));
                }
            }
            if($debug) $this->AddElement(new GoogleSitemapGeneratorDebugEntry("Debug: End Archive"));
        }

        //Add the author pages
        if($this->GetOption("in_auth")) {
            if($debug) $this->AddElement(new GoogleSitemapGeneratorDebugEntry("Debug: Start Author pages"));

            $linkFunc = null;

            //get_author_link is deprecated in WP 2.1, try to use get_author_posts_url first.
            if(function_exists('get_author_posts_url')) {
                $linkFunc = 'get_author_posts_url';
            } else if(function_exists('get_author_link')) {
                $linkFunc = 'get_author_link';
            } 

            //Who knows what happens in later WP versions, so check again if it worked
            if($linkFunc !== null) {
                //Unfortunately there is no API function to get all authors, so we have to do it the dirty way...
                //We retrieve only users with published and not password protected posts (and not pages)
                //WP2.1 introduced post_status='future', for earlier WP versions we need to check the post_date_gmt
                $sql = "SELECT DISTINCT
                            {$wpdb->users}.ID,
                            {$wpdb->users}.user_nicename,
                            MAX({$wpdb->posts}.post_modified_gmt) AS last_post
                        FROM
                            {$wpdb->users},
                            {$wpdb->posts}
                        WHERE
                            {$wpdb->posts}.post_author = {$wpdb->users}.ID
                            AND {$wpdb->posts}.post_status = 'publish'
                            AND {$wpdb->posts}.post_type = 'post'
                            AND {$wpdb->posts}.post_password = ''
                            " . (floatval($wp_version) < 2.1?"AND {$wpdb->posts}.post_date_gmt <= '" . gmdate('Y-m-d H:i:59') . "'":"") . "
                        GROUP BY
                            {$wpdb->users}.ID,
                            {$wpdb->users}.user_nicename";

                $authors = $wpdb->get_results($sql);

                if($authors && is_array($authors)) {
                    foreach($authors as $author) {
                        if($debug) if($debug) $this->AddElement(new GoogleSitemapGeneratorDebugEntry("Author-ID:" . $author->ID));
                        $url = ($linkFunc=='get_author_posts_url'?get_author_posts_url($author->ID,$author->user_nicename):get_author_link(false,$author->ID,$author->user_nicename));
                        $this->AddUrl($url,$this->GetTimestampFromMySql($author->last_post),$this->GetOption("cf_auth"),$this->GetOption("pr_auth"));
                    }
                }
            } else {
                //Too bad, no author pages for you :(
                if($debug) $this->AddElement(new GoogleSitemapGeneratorDebugEntry("Debug: No valid author link function found"));
            }

            if($debug) $this->AddElement(new GoogleSitemapGeneratorDebugEntry("Debug: End Author pages"));
        }

        //Add tag pages
        if($this->GetOption("in_tags") && $this->IsTaxonomySupported()) {
            if($debug) $this->AddElement(new GoogleSitemapGeneratorDebugEntry("Debug: Start Tags"));
            $tags = get_terms("post_tag",array("hide_empty"=>true,"hierarchical"=>false));
            if($tags && is_array($tags) && count($tags)>0) {
                foreach($tags AS $tag) {
                    $this->AddUrl(get_tag_link($tag->term_id),0,$this->GetOption("cf_tags"),$this->GetOption("pr_tags"));
                }
            }
            if($debug) $this->AddElement(new GoogleSitemapGeneratorDebugEntry("Debug: End Tags"));
        }

        //Add the custom pages
        if($debug) $this->AddElement(new GoogleSitemapGeneratorDebugEntry("Debug: Start Custom Pages"));
        if($this->_pages && is_array($this->_pages) && count($this->_pages)>0) {
            //#type $page GoogleSitemapGeneratorPage
            foreach($this->_pages AS $page) {
                $this->AddUrl($page->GetUrl(),$page->getLastMod(),$page->getChangeFreq(),$page->getPriority());
            }
        }

        if($debug) $this->AddElement(new GoogleSitemapGeneratorDebugEntry("Debug: End Custom Pages"));

        if($debug) $this->AddElement(new GoogleSitemapGeneratorDebugEntry("Debug: Start additional URLs"));

        do_action("sm_buildmap");

        if($debug) $this->AddElement(new GoogleSitemapGeneratorDebugEntry("Debug: End additional URLs"));

        $this->AddElement(new GoogleSitemapGeneratorXmlEntry("</urlset>"));

        $pingUrl='';

        if($this->GetOption("b_xml")) {
            if($this->_fileHandle && fclose($this->_fileHandle)) {
                $this->_fileHandle = null;
                $status->EndXml(true);
                $pingUrl=$this->GetXmlUrl();
            } else $status->EndXml(false,"Could not close the sitemap file.");
        }

        if($this->IsGzipEnabled()) {
            if($this->_fileZipHandle && fclose($this->_fileZipHandle)) {
                $this->_fileZipHandle = null;
                $status->EndZip(true);
                $pingUrl=$this->GetZipUrl();
            } else $status->EndZip(false,"Could not close the zipped sitemap file");
        }

        //Ping Google
        if($this->GetOption("b_ping") && !empty($pingUrl)) {
            $sPingUrl="http://www.google.com/webmasters/sitemaps/ping?sitemap=" . urlencode($pingUrl);
            $status->StartGooglePing($sPingUrl);
            $pingres=$this->RemoteOpen($sPingUrl);

            if($pingres==NULL || $pingres===false) {
                $status->EndGooglePing(false,$this->_lastError);
            } else {
                $status->EndGooglePing(true);
            }
        } 

        //Ping Ask.com
        if($this->GetOption("b_pingask") && !empty($pingUrl)) {
            $sPingUrl="http://submissions.ask.com/ping?sitemap=" . urlencode($pingUrl);
            $status->StartAskPing($sPingUrl);
            $pingres=$this->RemoteOpen($sPingUrl);

            if($pingres==NULL || $pingres===false || strpos($pingres,"successfully received and added")===false) { //Ask.com returns 200 OK even if there was an error, so we need to check the content.
                $status->EndAskPing(false,$this->_lastError);
            } else {
                $status->EndAskPing(true);
            }
        }

        //Ping YAHOO
        if($this->GetOption("sm_b_pingyahoo")===true && $this->GetOption("sm_b_yahookey")!="" && !empty($pingUrl)) {
            $sPingUrl="http://search.yahooapis.com/SiteExplorerService/V1/updateNotification?appid=" . $this->GetOption("sm_b_yahookey") . "&url=" . urlencode($pingUrl);
            $status->StartYahooPing($sPingUrl);
            $pingres=$this->RemoteOpen($sPingUrl);

            if($pingres==NULL || $pingres===false || strpos(strtolower($pingres),"success")===false) {
                $status->EndYahooPing(false,$this->_lastError);
            } else {
                $status->EndYahooPing(true);
            }
        }    

        //Ping MSN
        if($this->GetOption("b_pingmsn") && !empty($pingUrl)) {
            $sPingUrl="http://webmaster.live.com/ping.aspx?siteMap=" . urlencode($pingUrl);
            $status->StartMsnPing($sPingUrl);
            $pingres=$this->RemoteOpen($sPingUrl);

            if($pingres==NULL || $pingres===false || strpos($pingres,"Thanks for submitting your sitemap")===false) {
                $status->EndMsnPing(false,$this->_lastError);
            } else {
                $status->EndMsnPing(true);
            }
        }

        $status->End();    

        $this->_isActive = false;    

        //done...
        return $status;
    }

    function RemoteOpen($url) {
        $res = null;

        if(file_exists(ABSPATH . 'wp-includes/class-snoopy.php')) {
            require_once( ABSPATH . 'wp-includes/class-snoopy.php');

            $s = new Snoopy();
            $s->fetch($url);    

            if($s->status == 200) {
                $res = $s->results;
            }
        } else {
            $res = wp_remote_fopen($url);
        }
        return $res;
    }

    /**
     * Tracks the last error (gets called by PHP)
     *
     * @since 3.0
     * @access private
     * @author Arne Brachhold
     */
    function TrackError($log_level, $log_text, $error_file, $error_line) {
        $this->_lastError = $log_text;
    }

    /**
     * Echos option fields for an select field containing the valid change frequencies
     *
     * @since 3.0
     * @access private
     * @author Arne Brachhold
     * @param $currentVal The value which should be selected
     * @return all valid change frequencies as html option fields
     */
    function HtmlGetFreqNames($currentVal) {

        foreach($this->_freqNames AS $k=>$v) {
            echo "<option value=\"$k\" " . $this->HtmlGetSelected($k,$currentVal) .">" . $v . "</option>";
        }
    }

    /**
     * Echos option fields for an select field containing the valid priorities (0- 1.0)
     *
     * @since 3.0
     * @access private
     * @author Arne Brachhold
     * @param $currentVal string The value which should be selected
     * @return 0.0 - 1.0 as html option fields
     */
    function HtmlGetPriorityValues($currentVal) {
        $currentVal=(float) $currentVal;
        for($i=0.0; $i<=1.0; $i+=0.1) {
            echo "<option value=\"$i\" " . $this->HtmlGetSelected("$i","$currentVal") .">";
            _e(strval($i));
            echo "</option>";
        }
    }

    /**
     * Returns the checked attribute if the given values match
     *
     * @since 3.0
     * @access private
     * @author Arne Brachhold
     * @param $val string The current value
     * @param $equals string The value to match
     * @return The checked attribute if the given values match, an empty string if not
     */
    function HtmlGetChecked($val,$equals) {
        if($val==$equals) return $this->HtmlGetAttribute("checked");
        else return "";
    }

    /**
     * Returns the selected attribute if the given values match
     *
     * @since 3.0
     * @access private
     * @author Arne Brachhold
     * @param $val string The current value
     * @param $equals string The value to match
     * @return The selected attribute if the given values match, an empty string if not
     */
    function HtmlGetSelected($val,$equals) {
        if($val==$equals) return $this->HtmlGetAttribute("selected");
        else return "";
    }

    /**
     * Returns an formatted attribute. If the value is NULL, the name will be used.
     *
     * @since 3.0
     * @access private
     * @author Arne Brachhold
     * @param $attr string The attribute name
     * @param $value string The attribute value
     * @return The formatted attribute
     */
    function HtmlGetAttribute($attr,$value=NULL) {
        if($value==NULL) $value=$attr;
        return " " . $attr . "=\"" . $value . "\" ";
    }

    /**
     * Returns an array with GoogleSitemapGeneratorPage objects which is generated from POST values
     *
     * @since 3.0
     * @see GoogleSitemapGeneratorPage
     * @access private
     * @author Arne Brachhold
     * @return array An array with GoogleSitemapGeneratorPage objects
     */
    function HtmlApplyPages() {
        // Array with all page URLs
        $pages_ur=(!isset($_POST["sm_pages_ur"]) || !is_array($_POST["sm_pages_ur"])?array():$_POST["sm_pages_ur"]);

        //Array with all priorities
        $pages_pr=(!isset($_POST["sm_pages_pr"]) || !is_array($_POST["sm_pages_pr"])?array():$_POST["sm_pages_pr"]);

        //Array with all change frequencies
        $pages_cf=(!isset($_POST["sm_pages_cf"]) || !is_array($_POST["sm_pages_cf"])?array():$_POST["sm_pages_cf"]);

        //Array with all lastmods
        $pages_lm=(!isset($_POST["sm_pages_lm"]) || !is_array($_POST["sm_pages_lm"])?array():$_POST["sm_pages_lm"]);

        //Array where the new pages are stored
        $pages=array();

        //Loop through all defined pages and set their properties into an object
        if(isset($_POST["sm_pages_mark"]) && is_array($_POST["sm_pages_mark"])) {
            for($i=0; $i<count($_POST["sm_pages_mark"]); $i++) {
                //Create new object
                $p=new GoogleSitemapGeneratorPage();
                if(substr($pages_ur[$i],0,4)=="www.") $pages_ur[$i]="http://" . $pages_ur[$i];
                $p->SetUrl($pages_ur[$i]);
                $p->SetProprity($pages_pr[$i]);
                $p->SetChangeFreq($pages_cf[$i]);
                //Try to parse last modified, if -1 (note ===) automatic will be used (0)
                $lm=(!empty($pages_lm[$i])?strtotime($pages_lm[$i],time()):-1);
                if($lm===-1) $p->setLastMod(0);
                else $p->setLastMod($lm);

                //Add it to the array
                array_push($pages,$p);
            }
        }
        return $pages;
    }

    function GetTimestampFromMySql($mysqlDateTime) {
        list($date, $hours) = split(' ', $mysqlDateTime);
        list($year,$month,$day) = split('-',$date);
        list($hour,$min,$sec) = split(':',$hours);
        return mktime($hour, $min, $sec, $month, $day, $year);
    }

    function GetResourceLink($resourceID) {
        return trailingslashit(get_bloginfo('siteurl')) . '?res=' . $resourceID;
    }

    function GetRedirectLink($redir) {
        return trailingslashit("http://www.arnebrachhold.de/redir/" . $redir);
    }

    function GetBackLink() {
        $page = basename(__FILE__);
        if(isset($_GET['page']) && !empty($_GET['page'])) {
            $page = preg_replace('[^a-zA-Z0-9\.\_\-]','',$_GET['page']);
        }
        return $_SERVER['PHP_SELF'] . "?page=" .  $page;
    }

    function HtmlShowOptionsPage() {
        if(!class_exists("GoogleSitemapGeneratorUI")) {

            $path = trailingslashit(dirname(__FILE__));

            if(!file_exists( $path . 'sitemap-ui.php')) return false;
            require_once($path. 'sitemap-ui.php');
        } 

        $ui = new GoogleSitemapGeneratorUI($this);
        $ui->HtmlShowOptionsPage();
        return true;
    }
}