* @license PHP License * @package WB * @subpackage content */ /** * Load base class */ WBClass::load('WBString' , 'WBObserver' , 'WBContent' ,'WBContent_SEO'); /** * Content component: SEO_Sitemap * * Render Google sitemaps * @link http://www.sitemaps.org/ * * @version 1.1.0 * @package WB * @subpackage content */ class WBContent_SEO_Sitemap extends WBContent_SEO implements WBObserver { /** * my parameter list * * - 'action' either "index" or "sitemap" * - 'ttl' caching time of sitemaps * - 'datasource' list of modules to use * * @var array */ protected $config = array( 'action' => 'sitemap', 'sitemappath' => 'sitemap/', 'ttl' => 300, 'datasource' => array('Page') ); /** * list of seen URLs * @var array */ private $urls = array( 'robots.txt' => true ); /** * URL template for pages * @var string */ protected $pageLoc = '[[PROTOCOL]]://[[SERVER]][[SERVICE_HTML]]%s%1.2f%s'; protected $pageLocAlt = ''; /** * Datasource sitemap * @var WBDatasource_Sitemap */ private $ds; /** * handle to sitemap file * @var resource */ private $fh; /** * Available alternate languages * @var array */ private $languages = array(); /** * Run component * * Create sitemaps and sitemap index * * @see sendAndExit() * @return array parameter list */ public function run() { // reset locale settings to make sure decimal point works properly setlocale(LC_ALL, null); $this->loadLanguages(); $file = $this->mkSitemap(); $md5 = md5_file($file->realpath()); $this->res->addStream(fopen($file->realpath(), 'r'), $md5); $this->sendAndExit('text/xml'); } /** * Create actual sitemap per language * * @return WBFile */ private function mkSitemap() { WBString::setLanguagePath(null); $file = WBClass::create('WBFile'); if ($this->isTTLOK($file, '/var/cache/sitemap.xml')) { return $file; } $this->fh = fopen($file->realpath(), 'w'); $cnt = << EOT; fputs($this->fh, $cnt); // run datasources foreach ($this->config['datasource'] as $d) { // normalize if (!is_array($d)) { $d = array( 'name' => $d, 'params' => array() ); } $this->ds = WBClass::create('WBDatasource_Sitemap_' . $d['name']); $this->ds->configure($d['params']); $this->ds->attach($this); $this->ds->find(); $this->ds->detach($this); } fputs($this->fh, "\n"); fclose($this->fh); return $file; } /** * Get informed * * Callback for each URL * * @param WBDatasource_Sitemap $subject */ public function update($subject) { $path = $subject->getPath(); if (isset($this->urls[$path])) { return; } $this->urls[$path] = true; $cnt = array(''); $cnt[] = sprintf($this->pageLoc, $path, $subject->getPriority(), $subject->getSnippet()); foreach ($this->languages as $l => $p) { if (0 == strncmp('__default', $p, 9)) { $p = ''; } $cnt[] = sprintf($this->pageLocAlt, $l, $p, $path); } $cnt[] = "\n"; fputs($this->fh, implode("\n",$cnt)); } /** * Get language path mapping * * In case language path feature is uses, return settings * * @see etc-default/locale.xml */ private function loadLanguages() { $locale = WBClass::create('WBConfig'); $locale->load('locale'); $languages = $locale->get('locale/languages/requestpath', array()); if (!is_array($languages) || empty($languages)) { return; } $this->languages = array(); foreach ($languages as $l => $p) { $p .= '/'; $l = explode('_', $l); $l = $l[0]; $this->languages[$l] = $p; } $this->languages['x-default'] = ''; } /** * Check file's age * * Auxialliary function that helps to cache sitemaps * * @param WBFile $file * @param string $path * @return bool true if TTL is not over, yet */ private function isTTLOK($file, $path) { if (!$file->exists($path)) { $file->touch($path); return false; } if (1 > WBParam::get('wb/cache/use', 1)) { return false; } $ttl = intval($this->config['ttl']); if (0 > $ttl) { return false; } if (time() > filemtime($file->realpath()) + $ttl) { return false; } return true; } }