PageRenderTime 70ms CodeModel.GetById 31ms RepoModel.GetById 1ms app.codeStats 0ms

/blog/wp-content/plugins/google-sitemap-generator/sitemap-core.php

https://bitbucket.org/abtris/phpframeworks
PHP | 2496 lines | 1550 code | 353 blank | 593 comment | 317 complexity | 81bdad61cd3fb811d6e627b18706e307 MD5 | raw file
Possible License(s): LGPL-2.1, AGPL-1.0
  1. <?php
  2. /*
  3. $Id$
  4. */
  5. //Enable for dev! Good code doesn't generate any notices...
  6. //error_reporting(E_ALL);
  7. //ini_set("display_errors",1);
  8. #region PHP5 compat functions
  9. if (!function_exists('file_get_contents')) {
  10. /**
  11. * Replace file_get_contents()
  12. *
  13. * @category PHP
  14. * @package PHP_Compat
  15. * @link http://php.net/function.file_get_contents
  16. * @author Aidan Lister <aidan - php - net>
  17. * @version $Revision$
  18. * @internal resource_context is not supported
  19. * @since PHP 5
  20. */
  21. function file_get_contents($filename, $incpath = false, $resource_context = null) {
  22. if (false === $fh = fopen($filename, 'rb', $incpath)) {
  23. user_error('file_get_contents() failed to open stream: No such file or directory', E_USER_WARNING);
  24. return false;
  25. }
  26. clearstatcache();
  27. if ($fsize = @filesize($filename)) {
  28. $data = fread($fh, $fsize);
  29. } else {
  30. $data = '';
  31. while (!feof($fh)) {
  32. $data .= fread($fh, 8192);
  33. }
  34. }
  35. fclose($fh);
  36. return $data;
  37. }
  38. }
  39. if(!function_exists('file_put_contents')) {
  40. if (!defined('FILE_USE_INCLUDE_PATH')) {
  41. define('FILE_USE_INCLUDE_PATH', 1);
  42. }
  43. if (!defined('LOCK_EX')) {
  44. define('LOCK_EX', 2);
  45. }
  46. if (!defined('FILE_APPEND')) {
  47. define('FILE_APPEND', 8);
  48. }
  49. /**
  50. * Replace file_put_contents()
  51. *
  52. * @category PHP
  53. * @package PHP_Compat
  54. * @link http://php.net/function.file_put_contents
  55. * @author Aidan Lister <aidan - php - net>
  56. * @version $Revision$
  57. * @internal resource_context is not supported
  58. * @since PHP 5
  59. * @require PHP 4.0.0 (user_error)
  60. */
  61. function file_put_contents($filename, $content, $flags = null, $resource_context = null) {
  62. // If $content is an array, convert it to a string
  63. if (is_array($content)) {
  64. $content = implode('', $content);
  65. }
  66. // If we don't have a string, throw an error
  67. if (!is_scalar($content)) {
  68. user_error('file_put_contents() The 2nd parameter should be either a string or an array',E_USER_WARNING);
  69. return false;
  70. }
  71. // Get the length of data to write
  72. $length = strlen($content);
  73. // Check what mode we are using
  74. $mode = ($flags & FILE_APPEND)?'a':'wb';
  75. // Check if we're using the include path
  76. $use_inc_path = ($flags & FILE_USE_INCLUDE_PATH)?true:false;
  77. // Open the file for writing
  78. if (($fh = @fopen($filename, $mode, $use_inc_path)) === false) {
  79. user_error('file_put_contents() failed to open stream: Permission denied',E_USER_WARNING);
  80. return false;
  81. }
  82. // Attempt to get an exclusive lock
  83. $use_lock = ($flags & LOCK_EX) ? true : false ;
  84. if ($use_lock === true) {
  85. if (!flock($fh, LOCK_EX)) {
  86. return false;
  87. }
  88. }
  89. // Write to the file
  90. $bytes = 0;
  91. if (($bytes = @fwrite($fh, $content)) === false) {
  92. $errormsg = sprintf('file_put_contents() Failed to write %d bytes to %s',$length,$filename);
  93. user_error($errormsg, E_USER_WARNING);
  94. return false;
  95. }
  96. // Close the handle
  97. @fclose($fh);
  98. // Check all the data was written
  99. if ($bytes != $length) {
  100. $errormsg = sprintf('file_put_contents() Only %d of %d bytes written, possibly out of free disk space.',$bytes,$length);
  101. user_error($errormsg, E_USER_WARNING);
  102. return false;
  103. }
  104. // Return length
  105. return $bytes;
  106. }
  107. }
  108. #endregion
  109. /**
  110. * Represents the status (success and failures) of a building process
  111. * @author Arne Brachhold
  112. * @package sitemap
  113. * @since 3.0b5
  114. */
  115. class GoogleSitemapGeneratorStatus {
  116. function GoogleSitemapGeneratorStatus() {
  117. $this->_startTime = $this->GetMicrotimeFloat();
  118. $exists = get_option("sm_status");
  119. if($exists === false) add_option("sm_status","","Status","no");
  120. $this->Save();
  121. }
  122. function Save() {
  123. update_option("sm_status",$this);
  124. }
  125. function Load() {
  126. $status = @get_option("sm_status");
  127. if(is_a($status,"GoogleSitemapGeneratorStatus")) return $status;
  128. else return null;
  129. }
  130. /**
  131. * @var float $_startTime The start time of the building process
  132. * @access private
  133. */
  134. var $_startTime = 0;
  135. /**
  136. * @var float $_endTime The end time of the building process
  137. * @access private
  138. */
  139. var $_endTime = 0;
  140. /**
  141. * @var bool $$_hasChanged Indicates if the sitemap content has changed
  142. * @access private
  143. */
  144. var $_hasChanged = true;
  145. /**
  146. * @var int $_memoryUsage The amount of memory used in bytes
  147. * @access private
  148. */
  149. var $_memoryUsage = 0;
  150. /**
  151. * @var int $_lastPost The number of posts processed. This value is updated every 50 posts.
  152. * @access private
  153. */
  154. var $_lastPost = 0;
  155. /**
  156. * @var int $_lastTime The time when the last step-update occured. This value is updated every 50 posts.
  157. * @access private
  158. */
  159. var $_lastTime = 0;
  160. function End($hasChanged = true) {
  161. $this->_endTime = $this->GetMicrotimeFloat();
  162. $this->SetMemoryUsage();
  163. $this->_hasChanged = $hasChanged;
  164. $this->Save();
  165. }
  166. function SetMemoryUsage() {
  167. if(function_exists("memory_get_peak_usage")) {
  168. $this->_memoryUsage = memory_get_peak_usage(true);
  169. } else if(function_exists("memory_get_usage")) {
  170. $this->_memoryUsage = memory_get_usage(true);
  171. }
  172. }
  173. function GetMemoryUsage() {
  174. return round($this->_memoryUsage / 1024 / 1024,2);
  175. }
  176. function SaveStep($postCount) {
  177. $this->SetMemoryUsage();
  178. $this->_lastPost = $postCount;
  179. $this->_lastTime = $this->GetMicrotimeFloat();
  180. $this->Save();
  181. }
  182. function GetTime() {
  183. return round($this->_endTime - $this->_startTime,2);
  184. }
  185. function GetStartTime() {
  186. return round($this->_startTime, 2);
  187. }
  188. function GetLastTime() {
  189. return round($this->_lastTime - $this->_startTime,2);
  190. }
  191. function GetLastPost() {
  192. return $this->_lastPost;
  193. }
  194. var $_usedXml = false;
  195. var $_xmlSuccess = false;
  196. var $_xmlPath = '';
  197. var $_xmlUrl = '';
  198. function StartXml($path,$url) {
  199. $this->_usedXml = true;
  200. $this->_xmlPath = $path;
  201. $this->_xmlUrl = $url;
  202. $this->Save();
  203. }
  204. function EndXml($success) {
  205. $this->_xmlSuccess = $success;
  206. $this->Save();
  207. }
  208. var $_usedZip = false;
  209. var $_zipSuccess = false;
  210. var $_zipPath = '';
  211. var $_zipUrl = '';
  212. function StartZip($path,$url) {
  213. $this->_usedZip = true;
  214. $this->_zipPath = $path;
  215. $this->_zipUrl = $url;
  216. $this->Save();
  217. }
  218. function EndZip($success) {
  219. $this->_zipSuccess = $success;
  220. $this->Save();
  221. }
  222. var $_usedGoogle = false;
  223. var $_googleUrl = '';
  224. var $_gooogleSuccess = false;
  225. var $_googleStartTime = 0;
  226. var $_googleEndTime = 0;
  227. function StartGooglePing($url) {
  228. $this->_googleUrl = true;
  229. $this->_usedGoogle = true;
  230. $this->_googleStartTime = $this->GetMicrotimeFloat();
  231. $this->Save();
  232. }
  233. function EndGooglePing($success) {
  234. $this->_googleEndTime = $this->GetMicrotimeFloat();
  235. $this->_gooogleSuccess = $success;
  236. $this->Save();
  237. }
  238. function GetGoogleTime() {
  239. return round($this->_googleEndTime - $this->_googleStartTime,2);
  240. }
  241. var $_usedYahoo = false;
  242. var $_yahooUrl = '';
  243. var $_yahooSuccess = false;
  244. var $_yahooStartTime = 0;
  245. var $_yahooEndTime = 0;
  246. function StartYahooPing($url) {
  247. $this->_yahooUrl = $url;
  248. $this->_usedYahoo = true;
  249. $this->_yahooStartTime = $this->GetMicrotimeFloat();
  250. $this->Save();
  251. }
  252. function EndYahooPing($success) {
  253. $this->_yahooEndTime = $this->GetMicrotimeFloat();
  254. $this->_yahooSuccess = $success;
  255. $this->Save();
  256. }
  257. function GetYahooTime() {
  258. return round($this->_yahooEndTime - $this->_yahooStartTime,2);
  259. }
  260. var $_usedAsk = false;
  261. var $_askUrl = '';
  262. var $_askSuccess = false;
  263. var $_askStartTime = 0;
  264. var $_askEndTime = 0;
  265. function StartAskPing($url) {
  266. $this->_usedAsk = true;
  267. $this->_askUrl = $url;
  268. $this->_askStartTime = $this->GetMicrotimeFloat();
  269. $this->Save();
  270. }
  271. function EndAskPing($success) {
  272. $this->_askEndTime = $this->GetMicrotimeFloat();
  273. $this->_askSuccess = $success;
  274. $this->Save();
  275. }
  276. function GetAskTime() {
  277. return round($this->_askEndTime - $this->_askStartTime,2);
  278. }
  279. var $_usedMsn = false;
  280. var $_msnUrl = '';
  281. var $_msnSuccess = false;
  282. var $_msnStartTime = 0;
  283. var $_msnEndTime = 0;
  284. function StartMsnPing($url) {
  285. $this->_usedMsn = true;
  286. $this->_msnUrl = $url;
  287. $this->_msnStartTime = $this->GetMicrotimeFloat();
  288. $this->Save();
  289. }
  290. function EndMsnPing($success) {
  291. $this->_msnEndTime = $this->GetMicrotimeFloat();
  292. $this->_msnSuccess = $success;
  293. $this->Save();
  294. }
  295. function GetMsnTime() {
  296. return round($this->_msnEndTime - $this->_msnStartTime,2);
  297. }
  298. function GetMicrotimeFloat() {
  299. list($usec, $sec) = explode(" ", microtime());
  300. return ((float)$usec + (float)$sec);
  301. }
  302. }
  303. /**
  304. * Represents an item in the page list
  305. * @author Arne Brachhold
  306. * @package sitemap
  307. * @since 3.0
  308. */
  309. class GoogleSitemapGeneratorPage {
  310. /**
  311. * @var string $_url Sets the URL or the relative path to the blog dir of the page
  312. * @access private
  313. */
  314. var $_url;
  315. /**
  316. * @var float $_priority Sets the priority of this page
  317. * @access private
  318. */
  319. var $_priority;
  320. /**
  321. * @var string $_changeFreq Sets the chanfe frequency of the page. I want Enums!
  322. * @access private
  323. */
  324. var $_changeFreq;
  325. /**
  326. * @var int $_lastMod Sets the lastMod date as a UNIX timestamp.
  327. * @access private
  328. */
  329. var $_lastMod;
  330. /**
  331. * Initialize a new page object
  332. *
  333. * @since 3.0
  334. * @access public
  335. * @author Arne Brachhold
  336. * @param bool $enabled Should this page be included in thesitemap
  337. * @param string $url The URL or path of the file
  338. * @param float $priority The Priority of the page 0.0 to 1.0
  339. * @param string $changeFreq The change frequency like daily, hourly, weekly
  340. * @param int $lastMod The last mod date as a unix timestamp
  341. */
  342. function GoogleSitemapGeneratorPage($url="",$priority=0.0,$changeFreq="never",$lastMod=0) {
  343. $this->SetUrl($url);
  344. $this->SetProprity($priority);
  345. $this->SetChangeFreq($changeFreq);
  346. $this->SetLastMod($lastMod);
  347. }
  348. /**
  349. * Returns the URL of the page
  350. *
  351. * @return string The URL
  352. */
  353. function GetUrl() {
  354. return $this->_url;
  355. }
  356. /**
  357. * Sets the URL of the page
  358. *
  359. * @param string $url The new URL
  360. */
  361. function SetUrl($url) {
  362. $this->_url=(string) $url;
  363. }
  364. /**
  365. * Returns the priority of this page
  366. *
  367. * @return float the priority, from 0.0 to 1.0
  368. */
  369. function GetPriority() {
  370. return $this->_priority;
  371. }
  372. /**
  373. * Sets the priority of the page
  374. *
  375. * @param float $priority The new priority from 0.1 to 1.0
  376. */
  377. function SetProprity($priority) {
  378. $this->_priority=floatval($priority);
  379. }
  380. /**
  381. * Returns the change frequency of the page
  382. *
  383. * @return string The change frequncy like hourly, weekly, monthly etc.
  384. */
  385. function GetChangeFreq() {
  386. return $this->_changeFreq;
  387. }
  388. /**
  389. * Sets the change frequency of the page
  390. *
  391. * @param string $changeFreq The new change frequency
  392. */
  393. function SetChangeFreq($changeFreq) {
  394. $this->_changeFreq=(string) $changeFreq;
  395. }
  396. /**
  397. * Returns the last mod of the page
  398. *
  399. * @return int The lastmod value in seconds
  400. */
  401. function GetLastMod() {
  402. return $this->_lastMod;
  403. }
  404. /**
  405. * Sets the last mod of the page
  406. *
  407. * @param int $lastMod The lastmod of the page
  408. */
  409. function SetLastMod($lastMod) {
  410. $this->_lastMod=intval($lastMod);
  411. }
  412. function Render() {
  413. if($this->_url == "/" || empty($this->_url)) return '';
  414. $r="";
  415. $r.= "\t<url>\n";
  416. $r.= "\t\t<loc>" . $this->EscapeXML($this->_url) . "</loc>\n";
  417. if($this->_lastMod>0) $r.= "\t\t<lastmod>" . date('Y-m-d\TH:i:s+00:00',$this->_lastMod) . "</lastmod>\n";
  418. if(!empty($this->_changeFreq)) $r.= "\t\t<changefreq>" . $this->_changeFreq . "</changefreq>\n";
  419. if($this->_priority!==false && $this->_priority!=="") $r.= "\t\t<priority>" . number_format($this->_priority,1) . "</priority>\n";
  420. $r.= "\t</url>\n";
  421. return $r;
  422. }
  423. function EscapeXML($string) {
  424. return str_replace ( array ( '&', '"', "'", '<', '>'), array ( '&amp;' , '&quot;', '&apos;' , '&lt;' , '&gt;'), $string);
  425. }
  426. }
  427. class GoogleSitemapGeneratorXmlEntry {
  428. var $_xml;
  429. function GoogleSitemapGeneratorXmlEntry($xml) {
  430. $this->_xml = $xml;
  431. }
  432. function Render() {
  433. return $this->_xml;
  434. }
  435. }
  436. class GoogleSitemapGeneratorDebugEntry extends GoogleSitemapGeneratorXmlEntry {
  437. function Render() {
  438. return "<!-- " . $this->_xml . " -->";
  439. }
  440. }
  441. /**
  442. * Base class for all priority providers
  443. * @author Arne Brachhold
  444. * @package sitemap
  445. * @since 3.0
  446. */
  447. class GoogleSitemapGeneratorPrioProviderBase {
  448. /**
  449. * @var int $_totalComments The total number of comments of all posts
  450. * @access protected
  451. */
  452. var $_totalComments=0;
  453. /**
  454. * @var int $_totalComments The total number of posts
  455. * @access protected
  456. */
  457. var $_totalPosts=0;
  458. /**
  459. * Returns the (translated) name of this priority provider
  460. *
  461. * @since 3.0
  462. * @access public
  463. * @author Arne Brachhold
  464. * @return string The translated name
  465. */
  466. function GetName() {
  467. return "";
  468. }
  469. /**
  470. * Returns the (translated) description of this priority provider
  471. *
  472. * @since 3.0
  473. * @access public
  474. * @author Arne Brachhold
  475. * @return string The translated description
  476. */
  477. function GetDescription() {
  478. return "";
  479. }
  480. /**
  481. * Initializes a new priority provider
  482. *
  483. * @param $totalComments int The total number of comments of all posts
  484. * @param $totalPosts int The total number of posts
  485. * @since 3.0
  486. * @access public
  487. * @author Arne Brachhold
  488. */
  489. function GoogleSitemapGeneratorPrioProviderBase($totalComments,$totalPosts) {
  490. $this->_totalComments=$totalComments;
  491. $this->_totalPosts=$totalPosts;
  492. }
  493. /**
  494. * Returns the priority for a specified post
  495. *
  496. * @param $postID int The ID of the post
  497. * @param $commentCount int The number of comments for this post
  498. * @since 3.0
  499. * @access public
  500. * @author Arne Brachhold
  501. * @return int The calculated priority
  502. */
  503. function GetPostPriority($postID,$commentCount) {
  504. return 0;
  505. }
  506. }
  507. /**
  508. * Priority Provider which calculates the priority based on the number of comments
  509. * @author Arne Brachhold
  510. * @package sitemap
  511. * @since 3.0
  512. */
  513. class GoogleSitemapGeneratorPrioByCountProvider extends GoogleSitemapGeneratorPrioProviderBase {
  514. /**
  515. * Returns the (translated) name of this priority provider
  516. *
  517. * @since 3.0
  518. * @access public
  519. * @author Arne Brachhold
  520. * @return string The translated name
  521. */
  522. function GetName() {
  523. return __("Comment Count",'sitemap');
  524. }
  525. /**
  526. * Returns the (translated) description of this priority provider
  527. *
  528. * @since 3.0
  529. * @access public
  530. * @author Arne Brachhold
  531. * @return string The translated description
  532. */
  533. function GetDescription() {
  534. return __("Uses the number of comments of the post to calculate the priority",'sitemap');
  535. }
  536. /**
  537. * Initializes a new priority provider which calculates the post priority based on the number of comments
  538. *
  539. * @param $totalComments int The total number of comments of all posts
  540. * @param $totalPosts int The total number of posts
  541. * @since 3.0
  542. * @access public
  543. * @author Arne Brachhold
  544. */
  545. function GoogleSitemapGeneratorPrioByCountProvider($totalComments,$totalPosts) {
  546. parent::GoogleSitemapGeneratorPrioProviderBase($totalComments,$totalPosts);
  547. }
  548. /**
  549. * Returns the priority for a specified post
  550. *
  551. * @param $postID int The ID of the post
  552. * @param $commentCount int The number of comments for this post
  553. * @since 3.0
  554. * @access public
  555. * @author Arne Brachhold
  556. * @return int The calculated priority
  557. */
  558. function GetPostPriority($postID,$commentCount) {
  559. $prio=0;
  560. if($this->_totalComments>0 && $commentCount>0) {
  561. $prio = round(($commentCount*100/$this->_totalComments)/100,1);
  562. } else {
  563. $prio = 0;
  564. }
  565. return $prio;
  566. }
  567. }
  568. /**
  569. * Priority Provider which calculates the priority based on the average number of comments
  570. * @author Arne Brachhold
  571. * @package sitemap
  572. * @since 3.0
  573. */
  574. class GoogleSitemapGeneratorPrioByAverageProvider extends GoogleSitemapGeneratorPrioProviderBase {
  575. /**
  576. * @var int $_average The average number of comments per post
  577. * @access protected
  578. */
  579. var $_average=0.0;
  580. /**
  581. * Returns the (translated) name of this priority provider
  582. *
  583. * @since 3.0
  584. * @access public
  585. * @author Arne Brachhold
  586. * @return string The translated name
  587. */
  588. function GetName() {
  589. return __("Comment Average",'sitemap');
  590. }
  591. /**
  592. * Returns the (translated) description of this priority provider
  593. *
  594. * @since 3.0
  595. * @access public
  596. * @author Arne Brachhold
  597. * @return string The translated description
  598. */
  599. function GetDescription() {
  600. return __("Uses the average comment count to calculate the priority",'sitemap');
  601. }
  602. /**
  603. * Initializes a new priority provider which calculates the post priority based on the average number of comments
  604. *
  605. * @param $totalComments int The total number of comments of all posts
  606. * @param $totalPosts int The total number of posts
  607. * @since 3.0
  608. * @access public
  609. * @author Arne Brachhold
  610. */
  611. function GoogleSitemapGeneratorPrioByAverageProvider($totalComments,$totalPosts) {
  612. parent::GoogleSitemapGeneratorPrioProviderBase($totalComments,$totalPosts);
  613. if($this->_totalComments>0 && $this->_totalPosts>0) {
  614. $this->_average= (double) $this->_totalComments / $this->_totalPosts;
  615. }
  616. }
  617. /**
  618. * Returns the priority for a specified post
  619. *
  620. * @param $postID int The ID of the post
  621. * @param $commentCount int The number of comments for this post
  622. * @since 3.0
  623. * @access public
  624. * @author Arne Brachhold
  625. * @return int The calculated priority
  626. */
  627. function GetPostPriority($postID,$commentCount) {
  628. $prio = 0;
  629. //Do not divide by zero!
  630. if($this->_average==0) {
  631. if($commentCount>0) $prio = 1;
  632. else $prio = 0;
  633. } else {
  634. $prio = $commentCount/$this->_average;
  635. if($prio>1) $prio = 1;
  636. else if($prio<0) $prio = 0;
  637. }
  638. return round($prio,1);
  639. }
  640. }
  641. /**
  642. * Priority Provider which calculates the priority based on the popularity by the PopularityContest Plugin
  643. * @author Arne Brachhold
  644. * @package sitemap
  645. * @since 3.0
  646. */
  647. class GoogleSitemapGeneratorPrioByPopularityContestProvider extends GoogleSitemapGeneratorPrioProviderBase {
  648. /**
  649. * Returns the (translated) name of this priority provider
  650. *
  651. * @since 3.0
  652. * @access public
  653. * @author Arne Brachhold
  654. * @return string The translated name
  655. */
  656. function GetName() {
  657. return __("Popularity Contest",'sitemap');
  658. }
  659. /**
  660. * Returns the (translated) description of this priority provider
  661. *
  662. * @since 3.0
  663. * @access public
  664. * @author Arne Brachhold
  665. * @return string The translated description
  666. */
  667. function GetDescription() {
  668. 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')))));
  669. }
  670. /**
  671. * Initializes a new priority provider which calculates the post priority based on the popularity by the PopularityContest Plugin
  672. *
  673. * @param $totalComments int The total number of comments of all posts
  674. * @param $totalPosts int The total number of posts
  675. * @since 3.0
  676. * @access public
  677. * @author Arne Brachhold
  678. */
  679. function GoogleSitemapGeneratorPrioByPopularityContestProvider($totalComments,$totalPosts) {
  680. parent::GoogleSitemapGeneratorPrioProviderBase($totalComments,$totalPosts);
  681. }
  682. /**
  683. * Returns the priority for a specified post
  684. *
  685. * @param $postID int The ID of the post
  686. * @param $commentCount int The number of comments for this post
  687. * @since 3.0
  688. * @access public
  689. * @author Arne Brachhold
  690. * @return int The calculated priority
  691. */
  692. function GetPostPriority($postID,$commentCount) {
  693. //$akpc is the global instance of the Popularity Contest Plugin
  694. global $akpc,$posts;
  695. $res=0;
  696. //Better check if its there
  697. if(!empty($akpc) && is_object($akpc)) {
  698. //Is the method we rely on available?
  699. if(method_exists($akpc,"get_post_rank")) {
  700. if(!is_array($posts) || !$posts) $posts = array();
  701. if(!isset($posts[$postID])) $posts[$postID] = get_post($postID);
  702. //popresult comes as a percent value
  703. $popresult=$akpc->get_post_rank($postID);
  704. if(!empty($popresult) && strpos($popresult,"%")!==false) {
  705. //We need to parse it to get the priority as an int (percent)
  706. $matches=null;
  707. preg_match("/([0-9]{1,3})\%/si",$popresult,$matches);
  708. if(!empty($matches) && is_array($matches) && count($matches)==2) {
  709. //Divide it so 100% = 1, 10% = 0.1
  710. $res=round(intval($matches[1])/100,1);
  711. }
  712. }
  713. }
  714. }
  715. return $res;
  716. }
  717. }
  718. /**
  719. * Class to generate a sitemaps.org Sitemaps compliant sitemap of a WordPress blog.
  720. *
  721. * @package sitemap
  722. * @author Arne Brachhold
  723. * @since 3.0
  724. */
  725. class GoogleSitemapGenerator {
  726. /**
  727. * @var Version of the generator in SVN
  728. */
  729. var $_svnVersion = '$Id$';
  730. /**
  731. * @var array The unserialized array with the stored options
  732. */
  733. var $_options = array();
  734. /**
  735. * @var array The saved additional pages
  736. */
  737. var $_pages = array();
  738. /**
  739. * @var array The values and names of the change frequencies
  740. */
  741. var $_freqNames = array();
  742. /**
  743. * @var array A list of class names which my be called for priority calculation
  744. */
  745. var $_prioProviders = array();
  746. /**
  747. * @var bool True if init complete (options loaded etc)
  748. */
  749. var $_initiated = false;
  750. /**
  751. * @var string Holds the last error if one occurs when writing the files
  752. */
  753. var $_lastError=null;
  754. /**
  755. * @var int The last handled post ID
  756. */
  757. var $_lastPostID = 0;
  758. /**
  759. * @var bool Defines if the sitemap building process is active at the moment
  760. */
  761. var $_isActive = false;
  762. /**
  763. * @var bool Defines if the sitemap building process has been scheduled via Wp cron
  764. */
  765. var $_isScheduled = false;
  766. /**
  767. * @var object The file handle which is used to write the sitemap file
  768. */
  769. var $_fileHandle = null;
  770. /**
  771. * @var object The file handle which is used to write the zipped sitemap file
  772. */
  773. var $_fileZipHandle = null;
  774. /**
  775. * Returns the path to the blog directory
  776. *
  777. * @since 3.0
  778. * @access private
  779. * @author Arne Brachhold
  780. * @return string The full path to the blog directory
  781. */
  782. function GetHomePath() {
  783. $res="";
  784. //Check if we are in the admin area -> get_home_path() is avaiable
  785. if(function_exists("get_home_path")) {
  786. $res = get_home_path();
  787. } else {
  788. //get_home_path() is not available, but we can't include the admin
  789. //libraries because many plugins check for the "check_admin_referer"
  790. //function to detect if you are on an admin page. So we have to copy
  791. //the get_home_path function in our own...
  792. $home = get_option( 'home' );
  793. if ( $home != '' && $home != get_option( 'siteurl' ) ) {
  794. $home_path = parse_url( $home );
  795. $home_path = $home_path['path'];
  796. $root = str_replace( $_SERVER["PHP_SELF"], '', $_SERVER["SCRIPT_FILENAME"] );
  797. $home_path = trailingslashit( $root.$home_path );
  798. } else {
  799. $home_path = ABSPATH;
  800. }
  801. $res = $home_path;
  802. }
  803. return $res;
  804. }
  805. /**
  806. * Returns the path to the directory where the plugin file is located
  807. * @since 3.0b5
  808. * @access private
  809. * @author Arne Brachhold
  810. * @return string The path to the plugin directory
  811. */
  812. function GetPluginPath() {
  813. $path = dirname(__FILE__);
  814. return trailingslashit(str_replace("\\","/",$path));
  815. }
  816. /**
  817. * Returns the URL to the directory where the plugin file is located
  818. * @since 3.0b5
  819. * @access private
  820. * @author Arne Brachhold
  821. * @return string The URL to the plugin directory
  822. */
  823. function GetPluginUrl() {
  824. //Try to use WP API if possible, introduced in WP 2.6
  825. if (function_exists('plugins_url')) return trailingslashit(plugins_url(basename(dirname(__FILE__))));
  826. //Try to find manually... can't work if wp-content was renamed or is redirected
  827. $path = dirname(__FILE__);
  828. $path = str_replace("\\","/",$path);
  829. $path = trailingslashit(get_bloginfo('wpurl')) . trailingslashit(substr($path,strpos($path,"wp-content/")));
  830. return $path;
  831. }
  832. /**
  833. * Returns the URL to default XSLT style if it exists
  834. * @since 3.0b5
  835. * @access private
  836. * @author Arne Brachhold
  837. * @return string The URL to the default stylesheet, empry string if not available.
  838. */
  839. function GetDefaultStyle() {
  840. $p = $this->GetPluginPath();
  841. if(file_exists($p . "sitemap.xsl")) {
  842. return $this->GetPluginUrl() . 'sitemap.xsl';
  843. }
  844. return '';
  845. }
  846. /**
  847. * Sets up the default configuration
  848. *
  849. * @since 3.0
  850. * @access private
  851. * @author Arne Brachhold
  852. */
  853. function InitOptions() {
  854. $this->_options=array();
  855. $this->_options["sm_b_prio_provider"]="GoogleSitemapGeneratorPrioByCountProvider"; //Provider for automatic priority calculation
  856. $this->_options["sm_b_filename"]="sitemap.xml"; //Name of the Sitemap file
  857. $this->_options["sm_b_debug"]=true; //Write debug messages in the xml file
  858. $this->_options["sm_b_xml"]=true; //Create a .xml file
  859. $this->_options["sm_b_gzip"]=true; //Create a gzipped .xml file(.gz) file
  860. $this->_options["sm_b_ping"]=true; //Auto ping Google
  861. $this->_options["sm_b_pingyahoo"]=false; //Auto ping YAHOO
  862. $this->_options["sm_b_yahookey"]=''; //YAHOO Application Key
  863. $this->_options["sm_b_pingask"]=true; //Auto ping Ask.com
  864. $this->_options["sm_b_pingmsn"]=true; //Auto ping MSN
  865. $this->_options["sm_b_manual_enabled"]=false; //Allow manual creation of the sitemap via GET request
  866. $this->_options["sm_b_auto_enabled"]=true; //Rebuild sitemap when content is changed
  867. $this->_options["sm_b_auto_delay"]=true; //Use WP Cron to execute the building process in the background
  868. $this->_options["sm_b_manual_key"]=md5(microtime());//The secret key to build the sitemap via GET request
  869. $this->_options["sm_b_memory"] = ''; //Set Memory Limit (e.g. 16M)
  870. $this->_options["sm_b_time"] = -1; //Set time limit in seconds, 0 for unlimited, -1 for disabled
  871. $this->_options["sm_b_max_posts"] = -1; //Maximum number of posts, <= 0 for all
  872. $this->_options["sm_b_safemode"] = false; //Enable MySQL Safe Mode (doesn't use unbuffered results)
  873. $this->_options["sm_b_style_default"] = true; //Use default style
  874. $this->_options["sm_b_style"] = ''; //Include a stylesheet in the XML
  875. $this->_options["sm_b_robots"] = true; //Add sitemap location to WordPress' virtual robots.txt file
  876. $this->_options["sm_b_exclude"] = array(); //List of post / page IDs to exclude
  877. $this->_options["sm_b_exclude_cats"] = array(); //List of post / page IDs to exclude
  878. $this->_options["sm_b_location_mode"]="auto"; //Mode of location, auto or manual
  879. $this->_options["sm_b_filename_manual"]=""; //Manuel filename
  880. $this->_options["sm_b_fileurl_manual"]=""; //Manuel fileurl
  881. $this->_options["sm_in_home"]=true; //Include homepage
  882. $this->_options["sm_in_posts"]=true; //Include posts
  883. $this->_options["sm_in_posts_sub"]=false; //Include post pages (<!--nextpage--> tag)
  884. $this->_options["sm_in_pages"]=true; //Include static pages
  885. $this->_options["sm_in_cats"]=false; //Include categories
  886. $this->_options["sm_in_arch"]=false; //Include archives
  887. $this->_options["sm_in_auth"]=false; //Include author pages
  888. $this->_options["sm_in_tags"]=false; //Include tag pages
  889. $this->_options["sm_cf_home"]="daily"; //Change frequency of the homepage
  890. $this->_options["sm_cf_posts"]="monthly"; //Change frequency of posts
  891. $this->_options["sm_cf_pages"]="weekly"; //Change frequency of static pages
  892. $this->_options["sm_cf_cats"]="weekly"; //Change frequency of categories
  893. $this->_options["sm_cf_auth"]="weekly"; //Change frequency of author pages
  894. $this->_options["sm_cf_arch_curr"]="daily"; //Change frequency of the current archive (this month)
  895. $this->_options["sm_cf_arch_old"]="yearly"; //Change frequency of older archives
  896. $this->_options["sm_cf_tags"]="weekly"; //Change frequency of tags
  897. $this->_options["sm_pr_home"]=1.0; //Priority of the homepage
  898. $this->_options["sm_pr_posts"]=0.6; //Priority of posts (if auto prio is disabled)
  899. $this->_options["sm_pr_posts_min"]=0.2; //Minimum Priority of posts, even if autocalc is enabled
  900. $this->_options["sm_pr_pages"]=0.6; //Priority of static pages
  901. $this->_options["sm_pr_cats"]=0.3; //Priority of categories
  902. $this->_options["sm_pr_arch"]=0.3; //Priority of archives
  903. $this->_options["sm_pr_auth"]=0.3; //Priority of author pages
  904. $this->_options["sm_pr_tags"]=0.3; //Priority of tags
  905. $this->_options["sm_i_donated"]=false; //Did you donate? Thank you! :)
  906. $this->_options["sm_i_hide_donated"]=false; //And hide the thank you..
  907. $this->_options["sm_i_install_date"]=time(); //The installation date
  908. $this->_options["sm_i_hide_note"]=false; //Hide the note which appears after 30 days
  909. $this->_options["sm_i_hide_donors"]=false; //Hide the list of donations
  910. }
  911. /**
  912. * Loads the configuration from the database
  913. *
  914. * @since 3.0
  915. * @access private
  916. * @author Arne Brachhold
  917. */
  918. function LoadOptions() {
  919. $this->InitOptions();
  920. //First init default values, then overwrite it with stored values so we can add default
  921. //values with an update which get stored by the next edit.
  922. $storedoptions=get_option("sm_options");
  923. if($storedoptions && is_array($storedoptions)) {
  924. foreach($storedoptions AS $k=>$v) {
  925. $this->_options[$k]=$v;
  926. }
  927. } else update_option("sm_options",$this->_options); //First time use, store default values
  928. }
  929. /**
  930. * Initializes a new Google Sitemap Generator
  931. *
  932. * @since 3.0
  933. * @access private
  934. * @author Arne Brachhold
  935. */
  936. function GoogleSitemapGenerator() {
  937. }
  938. /**
  939. * Returns the version of the generator
  940. *
  941. * @since 3.0
  942. * @access public
  943. * @author Arne Brachhold
  944. * @return int The version
  945. */
  946. function GetVersion() {
  947. return GoogleSitemapGeneratorLoader::GetVersion();
  948. }
  949. /**
  950. * Returns all parent classes of a class
  951. *
  952. * @param $className string The name of the class
  953. *
  954. * @since 3.0
  955. * @access private
  956. * @author Arne Brachhold
  957. * @return array An array which contains the names of the parent classes
  958. */
  959. function GetParentClasses($classname) {
  960. $parent = get_parent_class($classname);
  961. $parents = array();
  962. if (!empty($parent)) {
  963. $parents = $this->GetParentClasses($parent);
  964. $parents[] = strtolower($parent);
  965. }
  966. return $parents;
  967. }
  968. /**
  969. * Returns if a class is a subclass of another class
  970. *
  971. * @param $className string The name of the class
  972. * @param $$parentName string The name of the parent class
  973. *
  974. * @since 3.0
  975. * @access private
  976. * @author Arne Brachhold
  977. * @return bool true if the given class is a subclass of the other one
  978. */
  979. function IsSubclassOf($className, $parentName) {
  980. $className = strtolower($className);
  981. $parentName = strtolower($parentName);
  982. if(empty($className) || empty($parentName) || !class_exists($className) || !class_exists($parentName)) return false;
  983. $parents=$this->GetParentClasses($className);
  984. return in_array($parentName,$parents);
  985. }
  986. /**
  987. * Loads up the configuration and validates the prioity providers
  988. *
  989. * This method is only called if the sitemaps needs to be build or the admin page is displayed.
  990. *
  991. * @since 3.0
  992. * @access private
  993. * @author Arne Brachhold
  994. */
  995. function Initate() {
  996. if(!$this->_initiated) {
  997. //Loading language file...
  998. //load_plugin_textdomain('sitemap');
  999. //Hmm, doesn't work if the plugin file has its own directory.
  1000. //Let's make it our way... load_plugin_textdomain() searches only in the wp-content/plugins dir.
  1001. $currentLocale = get_locale();
  1002. if(!empty($currentLocale)) {
  1003. $moFile = dirname(__FILE__) . "/lang/sitemap-" . $currentLocale . ".mo";
  1004. if(@file_exists($moFile) && is_readable($moFile)) load_textdomain('sitemap', $moFile);
  1005. }
  1006. $this->_freqNames = array(
  1007. "always"=>__("Always","sitemap"),
  1008. "hourly"=>__("Hourly","sitemap"),
  1009. "daily"=>__("Daily","sitemap"),
  1010. "weekly"=>__("Weekly","sitemap"),
  1011. "monthly"=>__("Monthly","sitemap"),
  1012. "yearly"=>__("Yearly","sitemap"),
  1013. "never"=>__("Never","sitemap")
  1014. );
  1015. $this->LoadOptions();
  1016. $this->LoadPages();
  1017. //Register our own priority providers
  1018. add_filter("sm_add_prio_provider",array(&$this, 'AddDefaultPrioProviders'));
  1019. //Let other plugins register their providers
  1020. $r = apply_filters("sm_add_prio_provider",$this->_prioProviders);
  1021. //Check if no plugin return null
  1022. if($r != null) $this->_prioProviders = $r;
  1023. $this->ValidatePrioProviders();
  1024. $this->_initiated = true;
  1025. }
  1026. }
  1027. /**
  1028. * Returns the instance of the Sitemap Generator
  1029. *
  1030. * @since 3.0
  1031. * @access public
  1032. * @return GoogleSitemapGenerator The instance or null if not available.
  1033. * @author Arne Brachhold
  1034. */
  1035. function &GetInstance() {
  1036. if(isset($GLOBALS["sm_instance"])) {
  1037. return $GLOBALS["sm_instance"];
  1038. } else return null;
  1039. }
  1040. /**
  1041. * Returns if the sitemap building process is currently active
  1042. *
  1043. * @since 3.0
  1044. * @access public
  1045. * @return bool true if active
  1046. * @author Arne Brachhold
  1047. */
  1048. function IsActive() {
  1049. $inst = &GoogleSitemapGenerator::GetInstance();
  1050. return ($inst != null && $inst->_isActive);
  1051. }
  1052. /**
  1053. * Returns if the compressed sitemap was activated
  1054. *
  1055. * @since 3.0b8
  1056. * @access private
  1057. * @author Arne Brachhold
  1058. * @return true if compressed
  1059. */
  1060. function IsGzipEnabled() {
  1061. return ($this->GetOption("b_gzip")===true && function_exists("gzwrite"));
  1062. }
  1063. /**
  1064. * Returns if this version of WordPress supports the new taxonomy system
  1065. *
  1066. * @since 3.0b8
  1067. * @access private
  1068. * @author Arne Brachhold
  1069. * @return true if supported
  1070. */
  1071. function IsTaxonomySupported() {
  1072. return (function_exists("get_taxonomy") && function_exists("get_terms"));
  1073. }
  1074. /**
  1075. * Enables the Google Sitemap Generator and registers the WordPress hooks
  1076. *
  1077. * @since 3.0
  1078. * @access public
  1079. * @author Arne Brachhold
  1080. */
  1081. function Enable() {
  1082. if(!isset($GLOBALS["sm_instance"])) {
  1083. $GLOBALS["sm_instance"]=new GoogleSitemapGenerator();
  1084. }
  1085. }
  1086. /**
  1087. * Checks if sitemap building after content changed is enabled and rebuild the sitemap
  1088. *
  1089. * @param int $postID The ID of the post to handle. Used to avoid double rebuilding if more than one hook was fired.
  1090. * @since 3.0
  1091. * @access public
  1092. * @author Arne Brachhold
  1093. */
  1094. function CheckForAutoBuild($postID) {
  1095. global $wp_version;
  1096. $this->Initate();
  1097. //Build one time per post and if not importing.
  1098. if($this->GetOption("b_auto_enabled")===true && $this->_lastPostID != $postID && (!defined('WP_IMPORTING') || WP_IMPORTING != true)) {
  1099. //Build the sitemap directly or schedule it with WP cron
  1100. if($this->GetOption("b_auto_delay")==true && floatval($wp_version) >= 2.1) {
  1101. if(!$this->_isScheduled) {
  1102. //Schedule in 15 seconds, this should be enough to catch all changes.
  1103. //Clear all other existing hooks, so the sitemap is only built once.
  1104. wp_clear_scheduled_hook('sm_build_cron');
  1105. wp_schedule_single_event(time()+15,'sm_build_cron');
  1106. $this->_isScheduled = true;
  1107. }
  1108. } else {
  1109. //Build sitemap only once and never in bulk mode
  1110. if(!$this->_lastPostID && (!isset($_GET["delete"]) || count((array) $_GET['delete'])<=0)) {
  1111. $this->BuildSitemap();
  1112. }
  1113. }
  1114. $this->_lastPostID = $postID;
  1115. }
  1116. }
  1117. /**
  1118. * Checks if the rebuild request was send and starts to rebuilt the sitemap
  1119. *
  1120. * @since 3.0
  1121. * @access public
  1122. * @author Arne Brachhold
  1123. */
  1124. function CheckForManualBuild() {
  1125. if(!empty($_GET["sm_command"]) && !empty($_GET["sm_key"])) {
  1126. $this->Initate();
  1127. if($this->GetOption("b_manual_enabled")===true && $_GET["sm_command"]=="build" && $_GET["sm_key"]==$this->GetOption("b_manual_key")) {
  1128. $this->BuildSitemap();
  1129. echo "DONE";
  1130. exit;
  1131. }
  1132. }
  1133. }
  1134. /**
  1135. * Validates all given Priority Providers by checking them for required methods and existence
  1136. *
  1137. * @since 3.0
  1138. * @access private
  1139. * @author Arne Brachhold
  1140. */
  1141. function ValidatePrioProviders() {
  1142. $validProviders=array();
  1143. for($i=0; $i<count($this->_prioProviders); $i++) {
  1144. if(class_exists($this->_prioProviders[$i])) {
  1145. if($this->IsSubclassOf($this->_prioProviders[$i],"GoogleSitemapGeneratorPrioProviderBase")) {
  1146. array_push($validProviders,$this->_prioProviders[$i]);
  1147. }
  1148. }
  1149. }
  1150. $this->_prioProviders=$validProviders;
  1151. if(!$this->GetOption("b_prio_provider")) {
  1152. if(!in_array($this->GetOption("b_prio_provider"),$this->_prioProviders,true)) {
  1153. $this->SetOption("b_prio_provider","");
  1154. }
  1155. }
  1156. }
  1157. /**
  1158. * Adds the default Priority Providers to the provider list
  1159. *
  1160. * @since 3.0
  1161. * @access private
  1162. * @author Arne Brachhold
  1163. */
  1164. function AddDefaultPrioProviders($providers) {
  1165. array_push($providers,"GoogleSitemapGeneratorPrioByCountProvider");
  1166. array_push($providers,"GoogleSitemapGeneratorPrioByAverageProvider");
  1167. if(class_exists("ak_popularity_contest")) {
  1168. array_push($providers,"GoogleSitemapGeneratorPrioByPopularityContestProvider");
  1169. }
  1170. return $providers;
  1171. }
  1172. /**
  1173. * Loads the stored pages from the database
  1174. *
  1175. * @since 3.0
  1176. * @access private
  1177. * @author Arne Brachhold
  1178. */
  1179. function LoadPages() {
  1180. global $wpdb;
  1181. $needsUpdate=false;
  1182. $pagesString=$wpdb->get_var("SELECT option_value FROM $wpdb->options WHERE option_name = 'sm_cpages'");
  1183. //Class sm_page was renamed with 3.0 -> rename it in serialized value for compatibility
  1184. if(!empty($pagesString) && strpos($pagesString,"sm_page")!==false) {
  1185. $pagesString = str_replace("O:7:\"sm_page\"","O:26:\"GoogleSitemapGeneratorPage\"",$pagesString);
  1186. $needsUpdate=true;
  1187. }
  1188. if(!empty($pagesString)) {
  1189. $storedpages=unserialize($pagesString);
  1190. $this->_pages=$storedpages;
  1191. } else {
  1192. $this->_pages=array();
  1193. }
  1194. if($needsUpdate) $this->SavePages();
  1195. }
  1196. /**
  1197. * Saved the additional pages back to the database
  1198. *
  1199. * @since 3.0
  1200. * @access private
  1201. * @author Arne Brachhold
  1202. * @return true on success
  1203. */
  1204. function SavePages() {
  1205. $oldvalue = get_option("sm_cpages");
  1206. if($oldvalue == $this->_pages) {
  1207. return true;
  1208. } else {
  1209. delete_option("sm_cpages");
  1210. //Add the option, Note the autoload=false because when the autoload happens, our class GoogleSitemapGeneratorPage doesn't exist
  1211. add_option("sm_cpages",$this->_pages,"Storage for custom pages of the sitemap plugin","no");
  1212. return true;
  1213. }
  1214. }
  1215. /**
  1216. * Returns the URL for the sitemap file
  1217. *
  1218. * @since 3.0
  1219. * @access private
  1220. * @author Arne Brachhold
  1221. * @param bool $forceAuto Force the return value to the autodetected value.
  1222. * @return The URL to the Sitemap file
  1223. */
  1224. function GetXmlUrl($forceAuto=false) {
  1225. if(!$forceAuto && $this->GetOption("b_location_mode")=="manual") {
  1226. return $this->GetOption("b_fileurl_manual");
  1227. } else {
  1228. return trailingslashit(get_bloginfo('siteurl')). $this->GetOption("b_filename");
  1229. }
  1230. }
  1231. /**
  1232. * Returns the URL for the gzipped sitemap file
  1233. *
  1234. * @since 3.0
  1235. * @access private
  1236. * @author Arne Brachhold
  1237. * @param bool $forceAuto Force the return value to the autodetected value.
  1238. * @return The URL to the gzipped Sitemap file
  1239. */
  1240. function GetZipUrl($forceAuto=false) {
  1241. return $this->GetXmlUrl($forceAuto) . ".gz";
  1242. }
  1243. /**
  1244. * Returns the file system path to the sitemap file
  1245. *
  1246. * @since 3.0
  1247. * @access private
  1248. * @author Arne Brachhold
  1249. * @param bool $forceAuto Force the return value to the autodetected value.
  1250. * @return The file system path;
  1251. */
  1252. function GetXmlPath($forceAuto=false) {
  1253. if(!$forceAuto && $this->GetOption("b_location_mode")=="manual") {
  1254. return $this->GetOption("b_filename_manual");
  1255. } else {
  1256. return $this->GetHomePath() . $this->GetOption("b_filename");
  1257. }
  1258. }
  1259. /**
  1260. * Returns the file system path to the gzipped sitemap file
  1261. *
  1262. * @since 3.0
  1263. * @access private
  1264. * @author Arne Brachhold
  1265. * @param bool $forceAuto Force the return value to the autodetected value.
  1266. * @return The file system path;
  1267. */
  1268. function GetZipPath($forceAuto=false) {
  1269. return $this->GetXmlPath($forceAuto) . ".gz";
  1270. }
  1271. /**
  1272. * Returns the option value for the given key
  1273. *
  1274. * @since 3.0
  1275. * @access private
  1276. * @author Arne Brachhold
  1277. * @param $key string The Configuration Key
  1278. * @return mixed The value
  1279. */
  1280. function GetOption($key) {
  1281. $key="sm_" . $key;
  1282. if(array_key_exists($key,$this->_options)) {
  1283. return $this->_options[$key];
  1284. } else return null;
  1285. }
  1286. /**
  1287. * Sets an option to a new value
  1288. *
  1289. * @since 3.0
  1290. * @access private
  1291. * @author Arne Brachhold
  1292. * @param $key string The configuration key
  1293. * @param $value mixed The new object
  1294. */
  1295. function SetOption($key,$value) {
  1296. if(strstr($key,"sm_")!==0) $key="sm_" . $key;
  1297. $this->_options[$key]=$value;
  1298. }
  1299. /**
  1300. * Saves the options back to the database
  1301. *
  1302. * @since 3.0
  1303. * @access private
  1304. * @author Arne Brachhold
  1305. * @return bool true on success
  1306. */
  1307. function SaveOptions() {
  1308. $oldvalue = get_option("sm_options");
  1309. if($oldvalue == $this->_options) {
  1310. return true;
  1311. } else return update_option("sm_options",$this->_options);
  1312. }
  1313. /**
  1314. * Retrieves the number of comments of a post in a asso. array
  1315. * The key is the postID, the value the number of comments
  1316. *
  1317. * @since 3.0
  1318. * @access private
  1319. * @author Arne Brachhold
  1320. * @return array An array with postIDs and their comment count
  1321. */
  1322. function GetComments() {
  1323. global $wpdb;
  1324. $comments=array();
  1325. //Query comments and add them into the array
  1326. $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`");
  1327. if($commentRes) {
  1328. foreach($commentRes as $comment) {
  1329. $comments[$comment->post_id]=$comment->comment_count;
  1330. }
  1331. }
  1332. return $comments;
  1333. }
  1334. /**
  1335. * Calculates the full number of comments from an sm_getComments() generated array
  1336. *
  1337. * @since 3.0
  1338. * @access private
  1339. * @author Arne Brachhold
  1340. * @param $comments array The Array with posts and c0mment count
  1341. * @see sm_getComments
  1342. * @return The full number of comments
  1343. */
  1344. function GetCommentCount($comments) {
  1345. $commentCount=0;
  1346. foreach($comments AS $k=>$v) {
  1347. $commentCount+=$v;
  1348. }
  1349. return $commentCount;
  1350. }
  1351. /**
  1352. * Adds a url to the sitemap. You can use this method or call AddElement directly.
  1353. *
  1354. * @since 3.0
  1355. * @access public
  1356. * @author Arne Brachhold
  1357. * @param $loc string The location (url) of the page
  1358. * @param $lastMod int The last Modification time as a UNIX timestamp
  1359. * @param $changeFreq string The change frequenty of the page, Valid values are "always", "hourly", "daily", "weekly", "monthly", "yearly" and "never".
  1360. * @param $priorty float The priority of the page, between 0.0 and 1.0
  1361. * @see AddElement
  1362. * @return string The URL node
  1363. */
  1364. function AddUrl($loc,$lastMod=0,$changeFreq="monthly",$priority=0.5) {
  1365. $page = new GoogleSitemapGeneratorPage($loc,$priority,$changeFreq,$lastMod);
  1366. $this->AddElement($page);
  1367. }
  1368. /**
  1369. * Adds an element to the sitemap
  1370. *
  1371. * @since 3.0
  1372. * @access private
  1373. * @author Arne Brachhold
  1374. * @param $page The element
  1375. */
  1376. function AddElement(&$page) {
  1377. if(empty($page)) return;
  1378. $s = $page->Render();
  1379. if($this->_fileZipHandle && $this->IsGzipEnabled()) {
  1380. gzwrite($this->_fileZipHandle,$s);
  1381. }
  1382. if($this->_fileHandle && $this->GetOption("b_xml")) {
  1383. fwrite($this->_fileHandle,$s);
  1384. }
  1385. }
  1386. /**
  1387. * Checks if a file is writable and tries to make it if not.
  1388. *
  1389. * @since 3.05b
  1390. * @access private
  1391. * @author VJTD3 <http://www.VJTD3.com>
  1392. * @return bool true if writable
  1393. */
  1394. function IsFileWritable($filename) {
  1395. //can we write?
  1396. if(!is_writable($filename)) {
  1397. //no we can't.
  1398. if(!@chmod($filename, 0666)) {
  1399. $pathtofilename = dirname($filename);
  1400. //Lets check if parent directory is writable.
  1401. if(!is_writable($pathtofilename)) {
  1402. //it's not writeable too.
  1403. if(!@chmod($pathtoffilename, 0666)) {
  1404. //darn couldn't fix up parrent directory this hosting is foobar.
  1405. //Lets error because of the permissions problems.
  1406. return false;
  1407. }
  1408. }
  1409. }
  1410. }
  1411. //we can write, return 1/true/happy dance.
  1412. return true;
  1413. }
  1414. function DoRobots() {
  1415. $this->Initate();
  1416. if($this->GetOption('b_robots') === true) {
  1417. $smUrl = $this->GetXmlUrl();
  1418. if($this->IsGzipEnabled()) {
  1419. $smUrl = $this->GetZipUrl();
  1420. }
  1421. echo "\nSitemap: " . $smUrl . "\n";
  1422. }
  1423. }
  1424. /**
  1425. * Builds the sitemap and writes it into a xml file.
  1426. *
  1427. * @since 3.0
  1428. * @access public
  1429. * @author Arne Brachhold <himself [at] arnebrachhold [dot] de>
  1430. * @return array An array with messages such as failed writes etc.
  1431. */
  1432. function BuildSitemap() {
  1433. global $wpdb, $posts, $wp_version;
  1434. $this->Initate();
  1435. if($this->GetOption("b_memory")!='') {
  1436. @ini_set("memory_limit",$this->GetOption("b_memory"));
  1437. }
  1438. if($this->GetOption("b_time")!=-1) {
  1439. @set_time_limit($this->GetOption("b_time"));
  1440. }
  1441. //This object saves the status information of the script directly to the database
  1442. $status = new GoogleSitemapGeneratorStatus();
  1443. //Other plugins can detect if the building process is active
  1444. $this->_isActive = true;
  1445. //$this->AddElement(new GoogleSitemapGeneratorXmlEntry());
  1446. //Debug mode?
  1447. $debug=$this->GetOption("b_debug");
  1448. if($this->GetOption("b_xml")) {
  1449. $fileName = $this->GetXmlPath();
  1450. $status->StartXml($this->GetXmlPath(),$this->GetXmlUrl());
  1451. if($this->IsFileWritable($fileName)) {
  1452. $this->_fileHandle = fopen($fileName,"w");
  1453. if(!$this->_fileHandle) $status->EndXml(false,"Not openable");
  1454. } else $status->EndXml(false,"not writable");
  1455. }
  1456. //Write gzipped sitemap file
  1457. if($this->IsGzipEnabled()) {
  1458. $fileName = $this->GetZipPath();
  1459. $status->StartZip($this->GetZipPath(),$this->GetZipUrl());
  1460. if($this->IsFileWritable($fileName)) {
  1461. $this->_fileZipHandle = gzopen($fileName,"w1");
  1462. if(!$this->_fileZipHandle) $status->EndZip(false,"Not openable");
  1463. } else $status->EndZip(false,"not writable");
  1464. }
  1465. if(!$this->_fileHandle && !$this->_fileZipHandle) {
  1466. $status->End();
  1467. return;
  1468. }
  1469. //Content of the XML file
  1470. $this->AddElement(new GoogleSitemapGeneratorXmlEntry('<?xml version="1.0" encoding="UTF-8"' . '?' . '>'));
  1471. $styleSheet = ($this->GetDefaultStyle() && $this->GetOption('b_style_default')===true?$this->GetDefaultStyle():$this->GetOption('b_style'));
  1472. if(!empty($styleSheet)) {
  1473. $this->AddElement(new GoogleSitemapGeneratorXmlEntry('<' . '?xml-stylesheet type="text/xsl" href="' . $styleSheet . '"?' . '>'));
  1474. }
  1475. $this->AddElement(new GoogleSitemapGeneratorDebugEntry("generator=\"wordpress/" . get_bloginfo('version') . "\""));
  1476. $this->AddElement(new GoogleSitemapGeneratorDebugEntry("sitemap-generator-url=\"http://www.arnebrachhold.de\" sitemap-generator-version=\"" . $this->GetVersion() . "\""));
  1477. $this->AddElement(new GoogleSitemapGeneratorDebugEntry("generated-on=\"" . date(get_option("date_format") . " " . get_option("time_format")) . "\""));
  1478. //All comments as an asso. Array (postID=>commentCount)
  1479. $comments=($this->GetOption("b_prio_provider")!=""?$this->GetComments():array());
  1480. //Full number of comments
  1481. $commentCount=(count($comments)>0?$this->GetCommentCount($comments):0);
  1482. if($debug && $this->GetOption("b_prio_provider")!="") {
  1483. $this->AddElement(new GoogleSitemapGeneratorDebugEntry("Debug: Total comment count: " . $commentCount));
  1484. }
  1485. //Go XML!
  1486. $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">'));
  1487. $home = get_bloginfo('url');
  1488. //Add the home page (WITH a slash!)
  1489. if($this->GetOption("in_home")) {
  1490. $this->AddUrl(trailingslashit($home),$this->GetTimestampFromMySql(get_lastpostmodified('GMT')),$this->GetOption("cf_home"),$this->GetOption("pr_home"));
  1491. }
  1492. //Add the posts
  1493. if($this->GetOption("in_posts") || $this->GetOption("in_pages")) {
  1494. if($debug) $this->AddElement(new GoogleSitemapGeneratorDebugEntry("Debug: Start Postings"));
  1495. //Pre 2.1 compatibility. 2.1 introduced 'future' as post_status so we don't need to check post_date
  1496. $wpCompat = (floatval($wp_version) < 2.1);
  1497. $useQTransLate = false; //function_exists('qtrans_convertURL') && function_exists('qtrans_getEnabledLanguages'); Not really working yet
  1498. $excludes = $this->GetOption('b_exclude'); //Excluded posts
  1499. $exclCats = $this->GetOption("b_exclude_cats"); // Excluded cats
  1500. if($exclCats && count($exclCats)>0 && $this->IsTaxonomySupported()) {
  1501. $exPosts = get_objects_in_term($exclCats,"category"); // Get all posts in excl. cats
  1502. if(is_array($exPosts) && count($exPosts) > 0) { //Merge it with the list of user excluded posts
  1503. $excludes = array_merge($excludes, $exPosts);
  1504. }
  1505. }
  1506. $contentStmt = '';
  1507. if($useQTransLate) {
  1508. $contentStmt.=', post_content ';
  1509. }
  1510. $postPageStmt = '';
  1511. $inSubPages = ($this->GetOption('in_posts_sub')===true);
  1512. if($inSubPages && $this->GetOption('in_posts')===true) {
  1513. $pageDivider='<!--nextpage-->';
  1514. $postPageStmt = ", (character_length(`post_content`) - character_length(REPLACE(`post_content`, '$pageDivider', ''))) / " . strlen($pageDivider) . " as postPages";
  1515. }
  1516. $sql="SELECT `ID`, `post_author`, `post_date`, `post_date_gmt`, `post_status`, `post_name`, `post_modified`, `post_modified_gmt`, `post_parent`, `post_type` $postPageStmt $contentStmt FROM `" . $wpdb->posts . "` WHERE ";
  1517. $where = '(';
  1518. if($this->GetOption('in_posts')) {
  1519. //WP < 2.1: posts are post_status = publish
  1520. //WP >= 2.1: post_type must be 'post', no date check required because future posts are post_status='future'
  1521. if($wpCompat) $where.="(post_status = 'publish' AND post_date_gmt <= '" . gmdate('Y-m-d H:i:59') . "')";
  1522. else $where.=" (post_status = 'publish' AND (post_type = 'post' OR post_type = '')) ";
  1523. }
  1524. if($this->GetOption('in_pages')) {
  1525. if($this->GetOption('in_posts')) {
  1526. $where.=" OR ";
  1527. }
  1528. if($wpCompat) {
  1529. //WP < 2.1: posts have post_status = published, pages have post_status = static
  1530. $where.=" post_status='static' ";
  1531. } else {
  1532. //WP >= 2.1: posts have post_type = 'post' and pages have post_type = 'page'. Both must be published.
  1533. $where.=" (post_status = 'publish' AND post_type = 'page') ";
  1534. }
  1535. }
  1536. $where.=") ";
  1537. if(is_array($excludes) && count($excludes)>0) {
  1538. $where.=" AND ID NOT IN ('" . implode("','",$excludes) . "')";
  1539. }
  1540. $where.=" AND post_password='' ORDER BY post_modified DESC";
  1541. $sql .= $where;
  1542. if($this->GetOption("b_max_posts")>0) {
  1543. $sql.=" LIMIT 0," . $this->GetOption("b_max_posts");
  1544. }
  1545. $postCount = intval($wpdb->get_var("SELECT COUNT(*) AS cnt FROM `" . $wpdb->posts . "` WHERE ". $where,0,0));
  1546. //Create a new connection because we are using mysql_unbuffered_query and don't want to disturb the WP connection
  1547. //Safe Mode for other plugins which use mysql_query() without a connection handler and will destroy our resultset :(
  1548. $con = $postRes = null;
  1549. //In 2.2, a bug which prevented additional DB connections was fixed
  1550. if(floatval($wp_version) < 2.2) {
  1551. $this->SetOption("b_safemode",true);
  1552. }
  1553. if($this->GetOption("b_safemode")===true) {
  1554. $postRes = mysql_query($sql,$wpdb->dbh);
  1555. if(!$postRes) {
  1556. trigger_error("MySQL query failed: " . mysql_error(),E_USER_NOTICE); //E_NOTE will be displayed on our debug mode
  1557. return;
  1558. }
  1559. } else {
  1560. $con = mysql_connect(DB_HOST,DB_USER,DB_PASSWORD,true);
  1561. if(!$con) {
  1562. trigger_error("MySQL Connection failed: " . mysql_error(),E_USER_NOTICE);
  1563. return;
  1564. }
  1565. if(!mysql_select_db(DB_NAME,$con)) {
  1566. trigger_error("MySQL DB Select failed: " . mysql_error(),E_USER_NOTICE);
  1567. return;
  1568. }
  1569. $postRes = mysql_unbuffered_query($sql,$con);
  1570. if(!$postRes) {
  1571. trigger_error("MySQL unbuffered query failed: " . mysql_error(),E_USER_NOTICE);
  1572. return;
  1573. }
  1574. }
  1575. if($postRes) {
  1576. //#type $prioProvider GoogleSitemapGeneratorPrioProviderBase
  1577. $prioProvider=NULL;
  1578. if($this->GetOption("b_prio_provider") != '') {
  1579. $providerClass=$this->GetOption('b_prio_provider');
  1580. $prioProvider = new $providerClass($commentCount,$postCount);
  1581. }
  1582. //$posts is used by Alex King's Popularity Contest plugin
  1583. //if($posts == null || !is_array($posts)) {
  1584. // $posts = &$postRes;
  1585. //}
  1586. $z = 1;
  1587. $zz = 1;
  1588. //Default priorities
  1589. $default_prio_posts = $this->GetOption('pr_posts');
  1590. $default_prio_pages = $this->GetOption('pr_pages');
  1591. //Change frequencies
  1592. $cf_pages = $this->GetOption('cf_pages');
  1593. $cf_posts = $this->GetOption('cf_posts');
  1594. $minPrio=$this->GetOption('pr_posts_min');
  1595. //Cycle through all posts and add them
  1596. while($post = mysql_fetch_object($postRes)) {
  1597. //Fill the cache with our DB result. Since it's incomplete (no text-content for example), we will clean it later.
  1598. $cache = array(&$post);
  1599. update_post_cache($cache);
  1600. //Set the current working post for other plugins which depend on "the loop"
  1601. $GLOBALS['post'] = &$post;
  1602. $permalink = get_permalink($post->ID);
  1603. if($permalink != $home) {
  1604. $isPage = false;
  1605. if($wpCompat) {
  1606. $isPage = ($post->post_status == 'static');
  1607. } else {
  1608. $isPage = ($post->post_type == 'page');
  1609. }
  1610. //Default Priority if auto calc is disabled
  1611. $prio = 0;
  1612. if($isPage) {
  1613. //Priority for static pages
  1614. $prio = $default_prio_pages;
  1615. } else {
  1616. //Priority for normal posts
  1617. $prio = $default_prio_posts;
  1618. }
  1619. //If priority calc. is enabled, calculate (but only for posts, not pages)!
  1620. if($prioProvider !== null && !$isPage) {
  1621. //Comment count for this post
  1622. $cmtcnt = (isset($comments[$post->ID])?$comments[$post->ID]:0);
  1623. $prio = $prioProvider->GetPostPriority($post->ID, $cmtcnt, $post);
  1624. if($debug) $this->AddElement(new GoogleSitemapGeneratorDebugEntry('Debug: Priority report of postID ' . $post->ID . ': Comments: ' . $cmtcnt . ' of ' . $commentCount . ' = ' . $prio . ' points'));
  1625. }
  1626. if(!$isPage && $minPrio>0 && $prio<$minPrio) {
  1627. $prio = $minPrio;
  1628. }
  1629. //Add it
  1630. $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);
  1631. if($inSubPages) {
  1632. $subPage = '';
  1633. for($p =1; $p<=$post->postPages; $p++) {
  1634. if(get_option('permalink_structure') == '') {
  1635. $subPage = $permalink . '&amp;page=' . $p;
  1636. } else {
  1637. $subPage = trailingslashit($permalink) . user_trailingslashit($p, 'single_paged');
  1638. }
  1639. $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);
  1640. }
  1641. }
  1642. // Multilingual Support with qTranslate, thanks to Qian Qin
  1643. if($useQTransLate) {
  1644. global $q_config;
  1645. foreach(qtrans_getEnabledLanguages($post->post_content) as $language) {
  1646. if($language!=$q_config['default_language']) {
  1647. $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);
  1648. }
  1649. }
  1650. }
  1651. }
  1652. //Update the status every 100 posts and at the end.
  1653. //If the script breaks because of memory or time limit,
  1654. //we have a "last reponded" value which can be compared to the server settings
  1655. if($zz==100 || $z == $postCount) {
  1656. $status->SaveStep($z);
  1657. $zz=0;
  1658. } else $zz++;
  1659. $z++;
  1660. //Clean cache because it's incomplete
  1661. if(version_compare($wp_version,"2.5",">=")) {
  1662. //WP 2.5 makes a mysql query for every clean_post_cache to clear the child cache
  1663. //so I've copied the function here until a patch arrives...
  1664. wp_cache_delete($post->ID, 'posts');
  1665. wp_cache_delete($post->ID, 'post_meta');
  1666. clean_object_term_cache($post->ID, 'post');
  1667. } else {
  1668. clean_post_cache($post->ID);
  1669. }
  1670. }
  1671. unset($postRes);
  1672. unset($prioProvider);
  1673. if($this->GetOption("b_safemode")!==true && $con) mysql_close($con);
  1674. }
  1675. if($debug) $this->AddElement(new GoogleSitemapGeneratorDebugEntry("Debug: End Postings"));
  1676. }
  1677. //Add the cats
  1678. if($this->GetOption("in_cats")) {
  1679. if($debug) $this->AddElement(new GoogleSitemapGeneratorDebugEntry("Debug: Start Cats"));
  1680. $exclCats = $this->GetOption("b_exclude_cats"); // Excluded cats
  1681. if($exclCats == null) $exclCats=array();
  1682. if(!$this->IsTaxonomySupported()) {
  1683. $catsRes=$wpdb->get_results("
  1684. SELECT
  1685. c.cat_ID AS ID,
  1686. MAX(p.post_modified_gmt) AS last_mod
  1687. FROM
  1688. `" . $wpdb->categories . "` c,
  1689. `" . $wpdb->post2cat . "` pc,
  1690. `" . $wpdb->posts . "` p
  1691. WHERE
  1692. pc.category_id = c.cat_ID
  1693. AND p.ID = pc.post_id
  1694. AND p.post_status = 'publish'
  1695. AND p.post_type='post'
  1696. GROUP
  1697. BY c.cat_id
  1698. ");
  1699. if($catsRes) {
  1700. foreach($catsRes as $cat) {
  1701. if($cat && $cat->ID && $cat->ID>0 && !in_array($cat->ID, $exclCats)) {
  1702. if($debug) if($debug) $this->AddElement(new GoogleSitemapGeneratorDebugEntry("Cat-ID:" . $cat->ID));
  1703. $this->AddUrl(get_category_link($cat->ID),$this->GetTimestampFromMySql($cat->last_mod),$this->GetOption("cf_cats"),$this->GetOption("pr_cats"));
  1704. }
  1705. }
  1706. }
  1707. } else {
  1708. $cats = get_terms("category",array("hide_empty"=>true,"hierarchical"=>false));
  1709. if($cats && is_array($cats) && count($cats)>0) {
  1710. foreach($cats AS $cat) {
  1711. if(!in_array($cat->term_id, $exclCats)) $this->AddUrl(get_category_link($cat->term_id),0,$this->GetOption("cf_cats"),$this->GetOption("pr_cats"));
  1712. }
  1713. }
  1714. }
  1715. if($debug) $this->AddElement(new GoogleSitemapGeneratorDebugEntry("Debug: End Cats"));
  1716. }
  1717. //Add the archives
  1718. if($this->GetOption("in_arch")) {
  1719. if($debug) $this->AddElement(new GoogleSitemapGeneratorDebugEntry("Debug: Start Archive"));
  1720. $now = current_time('mysql');
  1721. //WP2.1 introduced post_status='future', for earlier WP versions we need to check the post_date_gmt
  1722. $arcresults = $wpdb->get_results("
  1723. SELECT DISTINCT
  1724. YEAR(post_date_gmt) AS `year`,
  1725. MONTH(post_date_gmt) AS `month`,
  1726. MAX(post_date_gmt) as last_mod,
  1727. count(ID) as posts
  1728. FROM
  1729. $wpdb->posts
  1730. WHERE
  1731. post_date < '$now'
  1732. AND post_status = 'publish'
  1733. AND post_type = 'post'
  1734. " . (floatval($wp_version) < 2.1?"AND {$wpdb->posts}.post_date_gmt <= '" . gmdate('Y-m-d H:i:59') . "'":"") . "
  1735. GROUP BY
  1736. YEAR(post_date_gmt),
  1737. MONTH(post_date_gmt)
  1738. ORDER BY
  1739. post_date_gmt DESC");
  1740. if ($arcresults) {
  1741. foreach ($arcresults as $arcresult) {
  1742. $url = get_month_link($arcresult->year, $arcresult->month);
  1743. $changeFreq="";
  1744. //Archive is the current one
  1745. if($arcresult->month==date("n") && $arcresult->year==date("Y")) {
  1746. $changeFreq=$this->GetOption("cf_arch_curr");
  1747. } else { // Archive is older
  1748. $changeFreq=$this->GetOption("cf_arch_old");
  1749. }
  1750. $this->AddUrl($url,$this->GetTimestampFromMySql($arcresult->last_mod),$changeFreq,$this->GetOption("pr_arch"));
  1751. }
  1752. }
  1753. if($debug) $this->AddElement(new GoogleSitemapGeneratorDebugEntry("Debug: End Archive"));
  1754. }
  1755. //Add the author pages
  1756. if($this->GetOption("in_auth")) {
  1757. if($debug) $this->AddElement(new GoogleSitemapGeneratorDebugEntry("Debug: Start Author pages"));
  1758. $linkFunc = null;
  1759. //get_author_link is deprecated in WP 2.1, try to use get_author_posts_url first.
  1760. if(function_exists('get_author_posts_url')) {
  1761. $linkFunc = 'get_author_posts_url';
  1762. } else if(function_exists('get_author_link')) {
  1763. $linkFunc = 'get_author_link';
  1764. }
  1765. //Who knows what happens in later WP versions, so check again if it worked
  1766. if($linkFunc !== null) {
  1767. //Unfortunately there is no API function to get all authors, so we have to do it the dirty way...
  1768. //We retrieve only users with published and not password protected posts (and not pages)
  1769. //WP2.1 introduced post_status='future', for earlier WP versions we need to check the post_date_gmt
  1770. $sql = "SELECT DISTINCT
  1771. {$wpdb->users}.ID,
  1772. {$wpdb->users}.user_nicename,
  1773. MAX({$wpdb->posts}.post_modified_gmt) AS last_post
  1774. FROM
  1775. {$wpdb->users},
  1776. {$wpdb->posts}
  1777. WHERE
  1778. {$wpdb->posts}.post_author = {$wpdb->users}.ID
  1779. AND {$wpdb->posts}.post_status = 'publish'
  1780. AND {$wpdb->posts}.post_type = 'post'
  1781. AND {$wpdb->posts}.post_password = ''
  1782. " . (floatval($wp_version) < 2.1?"AND {$wpdb->posts}.post_date_gmt <= '" . gmdate('Y-m-d H:i:59') . "'":"") . "
  1783. GROUP BY
  1784. {$wpdb->users}.ID,
  1785. {$wpdb->users}.user_nicename";
  1786. $authors = $wpdb->get_results($sql);
  1787. if($authors && is_array($authors)) {
  1788. foreach($authors as $author) {
  1789. if($debug) if($debug) $this->AddElement(new GoogleSitemapGeneratorDebugEntry("Author-ID:" . $author->ID));
  1790. $url = ($linkFunc=='get_author_posts_url'?get_author_posts_url($author->ID,$author->user_nicename):get_author_link(false,$author->ID,$author->user_nicename));
  1791. $this->AddUrl($url,$this->GetTimestampFromMySql($author->last_post),$this->GetOption("cf_auth"),$this->GetOption("pr_auth"));
  1792. }
  1793. }
  1794. } else {
  1795. //Too bad, no author pages for you :(
  1796. if($debug) $this->AddElement(new GoogleSitemapGeneratorDebugEntry("Debug: No valid author link function found"));
  1797. }
  1798. if($debug) $this->AddElement(new GoogleSitemapGeneratorDebugEntry("Debug: End Author pages"));
  1799. }
  1800. //Add tag pages
  1801. if($this->GetOption("in_tags") && $this->IsTaxonomySupported()) {
  1802. if($debug) $this->AddElement(new GoogleSitemapGeneratorDebugEntry("Debug: Start Tags"));
  1803. $tags = get_terms("post_tag",array("hide_empty"=>true,"hierarchical"=>false));
  1804. if($tags && is_array($tags) && count($tags)>0) {
  1805. foreach($tags AS $tag) {
  1806. $this->AddUrl(get_tag_link($tag->term_id),0,$this->GetOption("cf_tags"),$this->GetOption("pr_tags"));
  1807. }
  1808. }
  1809. if($debug) $this->AddElement(new GoogleSitemapGeneratorDebugEntry("Debug: End Tags"));
  1810. }
  1811. //Add the custom pages
  1812. if($debug) $this->AddElement(new GoogleSitemapGeneratorDebugEntry("Debug: Start Custom Pages"));
  1813. if($this->_pages && is_array($this->_pages) && count($this->_pages)>0) {
  1814. //#type $page GoogleSitemapGeneratorPage
  1815. foreach($this->_pages AS $page) {
  1816. $this->AddUrl($page->GetUrl(),$page->getLastMod(),$page->getChangeFreq(),$page->getPriority());
  1817. }
  1818. }
  1819. if($debug) $this->AddElement(new GoogleSitemapGeneratorDebugEntry("Debug: End Custom Pages"));
  1820. if($debug) $this->AddElement(new GoogleSitemapGeneratorDebugEntry("Debug: Start additional URLs"));
  1821. do_action("sm_buildmap");
  1822. if($debug) $this->AddElement(new GoogleSitemapGeneratorDebugEntry("Debug: End additional URLs"));
  1823. $this->AddElement(new GoogleSitemapGeneratorXmlEntry("</urlset>"));
  1824. $pingUrl='';
  1825. if($this->GetOption("b_xml")) {
  1826. if($this->_fileHandle && fclose($this->_fileHandle)) {
  1827. $this->_fileHandle = null;
  1828. $status->EndXml(true);
  1829. $pingUrl=$this->GetXmlUrl();
  1830. } else $status->EndXml(false,"Could not close the sitemap file.");
  1831. }
  1832. if($this->IsGzipEnabled()) {
  1833. if($this->_fileZipHandle && fclose($this->_fileZipHandle)) {
  1834. $this->_fileZipHandle = null;
  1835. $status->EndZip(true);
  1836. $pingUrl=$this->GetZipUrl();
  1837. } else $status->EndZip(false,"Could not close the zipped sitemap file");
  1838. }
  1839. //Ping Google
  1840. if($this->GetOption("b_ping") && !empty($pingUrl)) {
  1841. $sPingUrl="http://www.google.com/webmasters/sitemaps/ping?sitemap=" . urlencode($pingUrl);
  1842. $status->StartGooglePing($sPingUrl);
  1843. $pingres=$this->RemoteOpen($sPingUrl);
  1844. if($pingres==NULL || $pingres===false) {
  1845. $status->EndGooglePing(false,$this->_lastError);
  1846. trigger_error("Failed to ping Google: " . htmlspecialchars(strip_tags($pingres)),E_USER_NOTICE);
  1847. } else {
  1848. $status->EndGooglePing(true);
  1849. }
  1850. }
  1851. //Ping Ask.com
  1852. if($this->GetOption("b_pingask") && !empty($pingUrl)) {
  1853. $sPingUrl="http://submissions.ask.com/ping?sitemap=" . urlencode($pingUrl);
  1854. $status->StartAskPing($sPingUrl);
  1855. $pingres=$this->RemoteOpen($sPingUrl);
  1856. 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.
  1857. $status->EndAskPing(false,$this->_lastError);
  1858. trigger_error("Failed to ping Ask.com: " . htmlspecialchars(strip_tags($pingres)),E_USER_NOTICE);
  1859. } else {
  1860. $status->EndAskPing(true);
  1861. }
  1862. }
  1863. //Ping YAHOO
  1864. if($this->GetOption("b_pingyahoo")===true && $this->GetOption("b_yahookey")!="" && !empty($pingUrl)) {
  1865. $sPingUrl="http://search.yahooapis.com/SiteExplorerService/V1/updateNotification?appid=" . $this->GetOption("b_yahookey") . "&url=" . urlencode($pingUrl);
  1866. $status->StartYahooPing($sPingUrl);
  1867. $pingres=$this->RemoteOpen($sPingUrl);
  1868. if($pingres==NULL || $pingres===false || strpos(strtolower($pingres),"success")===false) {
  1869. trigger_error("Failed to ping YAHOO: " . htmlspecialchars(strip_tags($pingres)),E_USER_NOTICE);
  1870. $status->EndYahooPing(false,$this->_lastError);
  1871. } else {
  1872. $status->EndYahooPing(true);
  1873. }
  1874. }
  1875. //Ping Bing
  1876. if($this->GetOption("b_pingmsn") && !empty($pingUrl)) {
  1877. $sPingUrl="http://www.bing.com/webmaster/ping.aspx?siteMap=" . urlencode($pingUrl);
  1878. $status->StartMsnPing($sPingUrl);
  1879. $pingres=$this->RemoteOpen($sPingUrl);
  1880. if($pingres==NULL || $pingres===false || strpos($pingres,"Thanks for submitting your sitemap")===false) {
  1881. trigger_error("Failed to ping Bing: " . htmlspecialchars(strip_tags($pingres)),E_USER_NOTICE);
  1882. $status->EndMsnPing(false,$this->_lastError);
  1883. } else {
  1884. $status->EndMsnPing(true);
  1885. }
  1886. }
  1887. $status->End();
  1888. $this->_isActive = false;
  1889. //done...
  1890. return $status;
  1891. }
  1892. function RemoteOpen($url,$method = 'get', $postData = null, $timeout = 10) {
  1893. global $wp_version;
  1894. //Before WP 2.7, wp_remote_fopen was quite crappy so Snoopy was favoured.
  1895. if(floatval($wp_version) < 2.7) {
  1896. if(!file_exists(ABSPATH . 'wp-includes/class-snoopy.php')) {
  1897. trigger_error('Snoopy Web Request failed: Snoopy not found.',E_USER_NOTICE);
  1898. return false; //Hoah?
  1899. }
  1900. require_once( ABSPATH . 'wp-includes/class-snoopy.php');
  1901. $s = new Snoopy();
  1902. $s->read_timeout = $timeout;
  1903. if($method == 'get') {
  1904. $s->fetch($url);
  1905. } else {
  1906. $s->submit($url,$postData);
  1907. }
  1908. if($s->status != "200") trigger_error('Snoopy Web Request failed: Status: ' . $s->status . "; Content: " . htmlspecialchars($s->results),E_USER_NOTICE);
  1909. return $s->results;
  1910. } else {
  1911. $options = array();
  1912. $options['timeout'] = $timeout;
  1913. if($method == 'get') {
  1914. $response = wp_remote_get( $url, $options );
  1915. } else {
  1916. $response = wp_remote_post($url, array_merge($options,array('body'=>$postData)));
  1917. }
  1918. if ( is_wp_error( $response ) ) {
  1919. $errs = $response->get_error_messages();
  1920. $errs = htmlspecialchars(implode('; ', $errs));
  1921. trigger_error('WP HTTP API Web Request failed: ' . $errs,E_USER_NOTICE);
  1922. return false;
  1923. }
  1924. return $response['body'];
  1925. }
  1926. return false;
  1927. }
  1928. /**
  1929. * Tracks the last error (gets called by PHP)
  1930. *
  1931. * @since 3.0
  1932. * @access private
  1933. * @author Arne Brachhold
  1934. */
  1935. function TrackError($log_level, $log_text, $error_file, $error_line) {
  1936. $this->_lastError = $log_text;
  1937. }
  1938. /**
  1939. * Echos option fields for an select field containing the valid change frequencies
  1940. *
  1941. * @since 3.0
  1942. * @access private
  1943. * @author Arne Brachhold
  1944. * @param $currentVal The value which should be selected
  1945. * @return all valid change frequencies as html option fields
  1946. */
  1947. function HtmlGetFreqNames($currentVal) {
  1948. foreach($this->_freqNames AS $k=>$v) {
  1949. echo "<option value=\"$k\" " . $this->HtmlGetSelected($k,$currentVal) .">" . $v . "</option>";
  1950. }
  1951. }
  1952. /**
  1953. * Echos option fields for an select field containing the valid priorities (0- 1.0)
  1954. *
  1955. * @since 3.0
  1956. * @access private
  1957. * @author Arne Brachhold
  1958. * @param $currentVal string The value which should be selected
  1959. * @return 0.0 - 1.0 as html option fields
  1960. */
  1961. function HtmlGetPriorityValues($currentVal) {
  1962. $currentVal=(float) $currentVal;
  1963. for($i=0.0; $i<=1.0; $i+=0.1) {
  1964. $v = number_format($i,1,".","");
  1965. $t = number_format_i18n($i,1);
  1966. echo "<option value=\"" . $v . "\" " . $this->HtmlGetSelected("$i","$currentVal") .">";
  1967. echo $t;
  1968. echo "</option>";
  1969. }
  1970. }
  1971. /**
  1972. * Returns the checked attribute if the given values match
  1973. *
  1974. * @since 3.0
  1975. * @access private
  1976. * @author Arne Brachhold
  1977. * @param $val string The current value
  1978. * @param $equals string The value to match
  1979. * @return The checked attribute if the given values match, an empty string if not
  1980. */
  1981. function HtmlGetChecked($val,$equals) {
  1982. if($val==$equals) return $this->HtmlGetAttribute("checked");
  1983. else return "";
  1984. }
  1985. /**
  1986. * Returns the selected attribute if the given values match
  1987. *
  1988. * @since 3.0
  1989. * @access private
  1990. * @author Arne Brachhold
  1991. * @param $val string The current value
  1992. * @param $equals string The value to match
  1993. * @return The selected attribute if the given values match, an empty string if not
  1994. */
  1995. function HtmlGetSelected($val,$equals) {
  1996. if($val==$equals) return $this->HtmlGetAttribute("selected");
  1997. else return "";
  1998. }
  1999. /**
  2000. * Returns an formatted attribute. If the value is NULL, the name will be used.
  2001. *
  2002. * @since 3.0
  2003. * @access private
  2004. * @author Arne Brachhold
  2005. * @param $attr string The attribute name
  2006. * @param $value string The attribute value
  2007. * @return The formatted attribute
  2008. */
  2009. function HtmlGetAttribute($attr,$value=NULL) {
  2010. if($value==NULL) $value=$attr;
  2011. return " " . $attr . "=\"" . $value . "\" ";
  2012. }
  2013. /**
  2014. * Returns an array with GoogleSitemapGeneratorPage objects which is generated from POST values
  2015. *
  2016. * @since 3.0
  2017. * @see GoogleSitemapGeneratorPage
  2018. * @access private
  2019. * @author Arne Brachhold
  2020. * @return array An array with GoogleSitemapGeneratorPage objects
  2021. */
  2022. function HtmlApplyPages() {
  2023. // Array with all page URLs
  2024. $pages_ur=(!isset($_POST["sm_pages_ur"]) || !is_array($_POST["sm_pages_ur"])?array():$_POST["sm_pages_ur"]);
  2025. //Array with all priorities
  2026. $pages_pr=(!isset($_POST["sm_pages_pr"]) || !is_array($_POST["sm_pages_pr"])?array():$_POST["sm_pages_pr"]);
  2027. //Array with all change frequencies
  2028. $pages_cf=(!isset($_POST["sm_pages_cf"]) || !is_array($_POST["sm_pages_cf"])?array():$_POST["sm_pages_cf"]);
  2029. //Array with all lastmods
  2030. $pages_lm=(!isset($_POST["sm_pages_lm"]) || !is_array($_POST["sm_pages_lm"])?array():$_POST["sm_pages_lm"]);
  2031. //Array where the new pages are stored
  2032. $pages=array();
  2033. //Loop through all defined pages and set their properties into an object
  2034. if(isset($_POST["sm_pages_mark"]) && is_array($_POST["sm_pages_mark"])) {
  2035. for($i=0; $i<count($_POST["sm_pages_mark"]); $i++) {
  2036. //Create new object
  2037. $p=new GoogleSitemapGeneratorPage();
  2038. if(substr($pages_ur[$i],0,4)=="www.") $pages_ur[$i]="http://" . $pages_ur[$i];
  2039. $p->SetUrl($pages_ur[$i]);
  2040. $p->SetProprity($pages_pr[$i]);
  2041. $p->SetChangeFreq($pages_cf[$i]);
  2042. //Try to parse last modified, if -1 (note ===) automatic will be used (0)
  2043. $lm=(!empty($pages_lm[$i])?strtotime($pages_lm[$i],time()):-1);
  2044. if($lm===-1) $p->setLastMod(0);
  2045. else $p->setLastMod($lm);
  2046. //Add it to the array
  2047. array_push($pages,$p);
  2048. }
  2049. }
  2050. return $pages;
  2051. }
  2052. function GetTimestampFromMySql($mysqlDateTime) {
  2053. list($date, $hours) = split(' ', $mysqlDateTime);
  2054. list($year,$month,$day) = split('-',$date);
  2055. list($hour,$min,$sec) = split(':',$hours);
  2056. return mktime($hour, $min, $sec, $month, $day, $year);
  2057. }
  2058. function GetRedirectLink($redir) {
  2059. return trailingslashit("http://www.arnebrachhold.de/redir/" . $redir);
  2060. }
  2061. function GetBackLink() {
  2062. $page = basename(__FILE__);
  2063. if(isset($_GET['page']) && !empty($_GET['page'])) {
  2064. $page = preg_replace('[^a-zA-Z0-9\.\_\-]','',$_GET['page']);
  2065. }
  2066. if(function_exists("admin_url")) return admin_url(basename($_SERVER["PHP_SELF"])) . "?page=" . $page;
  2067. else return $_SERVER['PHP_SELF'] . "?page=" . $page;
  2068. }
  2069. function HtmlShowOptionsPage() {
  2070. $ui = $this->GetUI();
  2071. if($ui) {
  2072. $ui->HtmlShowOptionsPage();
  2073. return true;
  2074. }
  2075. return false;
  2076. }
  2077. var $_ui = null;
  2078. function GetUI() {
  2079. global $wp_version;
  2080. if($this->_ui === null) {
  2081. $className='GoogleSitemapGeneratorUI';
  2082. $fileName='sitemap-ui.php';
  2083. if(!class_exists($className)) {
  2084. $path = trailingslashit(dirname(__FILE__));
  2085. if(!file_exists( $path . $fileName)) return false;
  2086. require_once($path. $fileName);
  2087. }
  2088. $this->_ui = new $className($this);
  2089. }
  2090. return $this->_ui;
  2091. }
  2092. function HtmlShowHelp() {
  2093. }
  2094. }