diff --git a/README.md b/README.md index fc399fb..962650a 100644 --- a/README.md +++ b/README.md @@ -11,7 +11,7 @@ ## Documentation SilverStripe provides support for the Google Sitemaps XML system, enabling -Google and other search engines to see all pages on your site. This helps +Google and other search engines to see all urls on your site. This helps your SilverStripe website rank well in search engines, and to encourage the information on your site to be discovered by Google quickly. @@ -20,21 +20,7 @@ be visited: http://yoursite.com/sitemap.xml See http://en.wikipedia.org/wiki/Sitemaps for info on this format -In addition, whenever you publish a new or republish an existing page, -SilverStripe automatically informs Google of the change, encouraging a Google -to take notice. If you install the SilverStripe Google Analytics module, you -can see if Google has updated your page as a result. +## Usage Overview -By default, SilverStripe informs Google that the importance of a page depends -on its position of in the sitemap. "Top level" pages are most important, and -the deeper a page is nested, the less important it is. (For each level, -Importance drops from 1.0, to 0.9, to 0.8, and so on, until 0.1 is reached). - -In the CMS, in the "Content/GoogleSitemap" tab, you can set the page importance -manually, including requesting to have the page excluded from the google sitemap. - -### Setup automatic pinging - - GoogleSitemap::enable_google_notificaton(); - +See docs/en/* for more information about configuring the module. diff --git a/_config.php b/_config.php index e8fb0e2..edb49c0 100644 --- a/_config.php +++ b/_config.php @@ -5,6 +5,10 @@ Director::addRules(10, array( 'sitemap.xml' => 'GoogleSitemap', )); -// add the extension -Object::add_extension('SiteTree', 'GoogleSitemapDecorator'); -?> \ No newline at end of file +// add the extension to pages +Object::add_extension('SiteTree', 'GoogleSitemapSiteTreeDecorator'); + +// if you need to add this to DataObjects include the following in +// your own _config: + +// GoogleSiteMap::register_dataobject('MyDataObject'); diff --git a/code/GoogleSitemap.php b/code/GoogleSitemap.php index 8f61704..8adf209 100644 --- a/code/GoogleSitemap.php +++ b/code/GoogleSitemap.php @@ -1,5 +1,4 @@ * GoogleSitemap::enable_google_notificaton(); * @@ -19,7 +19,6 @@ * * @package googlesitemaps */ - class GoogleSitemap extends Controller { /** @@ -41,60 +40,166 @@ class GoogleSitemap extends Controller { * @var boolean */ protected static $use_show_in_search = true; - + + /** + * List of DataObjects to show in sitemap.xml + * + * @var array + */ + public static $google_sitemap_dataobjects = array(); + + /** + * List of DataObjects change frequency + * + * @var array + */ + public static $google_sitemap_dataobjects_changefreq = array(); + + /** + * Decorates the given DataObject with {@link GoogleSitemapDecorator} + * and pushes the class name to the registered DataObjects. + * Note that all registered DataObjects need the method AbsoluteLink(). + * + * @param string $className name of DataObject to register + * @param string $changeFreq + * + * @return void + */ + public static function register_dataobject($className, $changeFreq = 'monthly') { + if (!self::is_registered($className)) { + Object::add_extension($className, 'GoogleSitemapDecorator'); + self::$google_sitemap_dataobjects[] = $className; + self::$google_sitemap_dataobjects_changefreq[] = $changeFreq; + } + } + + /** + * Checks whether the given class name is already registered or not. + * + * @param string $className Name of DataObject to check + * + * @return bool + */ + public static function is_registered($className) { + return in_array($className, self::$google_sitemap_dataobjects); + } + + /** + * Adds DataObjects to the existing DataObjectSet with pages from the + * site tree + * + * @param DataObjectSet $newPages + * + * @return DataObjectSet + */ + protected function addRegisteredDataObjects() { + $output = new DataObjectSet(); + + foreach(self::$google_sitemap_dataobjects as $index => $className) { + $dataObjectSet = DataObject::get($className); + + if($dataObjectSet) { + foreach($dataObjectSet as $dataObject) { + if($dataObject->canView() && (!isset($dataObject->Priority) || $dataObject->Priority > 0)) { + $dataObject->ChangeFreq = self::$google_sitemap_dataobjects_changefreq[$index]; + + if(!isset($dataObject->Priority)) { + $dataObject->Priority = 1.0; + } + + $output->push($dataObject); + } + } + } + } + + return $output; + } + + /** + * Returns all the links to {@link SiteTree} pages and + * {@link DataObject} urls on the page + * + * @return DataObjectSet + */ public function Items() { $filter = ''; - + $bt = defined('DB::USE_ANSI_SQL') ? "\"" : "`"; + if(self::$use_show_in_search) { $filter = "{$bt}ShowInSearch{$bt} = 1"; } - + $this->Pages = Versioned::get_by_stage('SiteTree', 'Live', $filter); $newPages = new DataObjectSet(); + if($this->Pages) { foreach($this->Pages as $page) { - // Only include pages from this host and pages which are not an instance of ErrorPage - // We prefix $_SERVER['HTTP_HOST'] with 'http://' so that parse_url to help parse_url identify the host name component; we could use another protocol (like - // 'ftp://' as the prefix and the code would work the same. - if(parse_url($page->AbsoluteLink(), PHP_URL_HOST) == parse_url('http://' . $_SERVER['HTTP_HOST'], PHP_URL_HOST) && !($page instanceof ErrorPage)) { + // Only include pages from this host and pages which are not an + // instance of ErrorPage. We prefix $_SERVER['HTTP_HOST'] with + // 'http://' so that parse_url to help parse_url identify the + // host name component; we could use another protocol (like ftp + // as the prefix and the code would work the same. + $pageHttp = parse_url($page->AbsoluteLink(), PHP_URL_HOST); + $hostHttp = parse_url('http://' . $_SERVER['HTTP_HOST'], PHP_URL_HOST); + + if(($pageHttp == $hostHttp) && !($page instanceof ErrorPage)) { - // If the page has been set to 0 priority, we set a flag so it won't be included + // If the page has been set to 0 priority, we set a flag so + // it won't be included if($page->canView() && (!isset($page->Priority) || $page->Priority > 0)) { - - $created = $page->dbObject('Created'); + // The one field that isn't easy to deal with in the template is + // Change frequency, so we set that here. + $date = date('Y-m-d H:i:s'); + + $prop = $page->toMap(); + $created = new SS_Datetime(); + $created->value = (isset($prop['Created'])) ? $prop['Created'] : $date; $now = new SS_Datetime(); - $now->value = date('Y-m-d H:i:s'); - $versions = $page->Version; + $now->value = $date; + $versions = (isset($prop['Version'])) ? $prop['Version'] : 1; + $timediff = $now->format('U') - $created->format('U'); - + // Check how many revisions have been made over the lifetime of the // Page for a rough estimate of it's changing frequency. - $period = $timediff / ($versions + 1); - - if($period > 60*60*24*365) { // > 1 year - $page->ChangeFreq='yearly'; - } elseif($period > 60*60*24*30) { // > ~1 month - $page->ChangeFreq='monthly'; - } elseif($period > 60*60*24*7) { // > 1 week - $page->ChangeFreq='weekly'; - } elseif($period > 60*60*24) { // > 1 day - $page->ChangeFreq='daily'; - } elseif($period > 60*60) { // > 1 hour - $page->ChangeFreq='hourly'; - } else { // < 1 hour - $page->ChangeFreq='always'; + + if($period > 60*60*24*365) { + // > 1 year + $page->ChangeFreq = 'yearly'; + } + elseif($period > 60*60*24*30) { + $page->ChangeFreq = 'monthly'; + } + elseif($period > 60*60*24*7) { + // > 1 week + $page->ChangeFreq = 'weekly'; + } + elseif($period > 60*60*24) { + // > 1 day + $page->ChangeFreq = 'daily'; + } + elseif($period > 60*60) { + // > 1 hour + $page->ChangeFreq = 'hourly'; + } else { + // < 1 hour + $page->ChangeFreq = 'always'; } - + $newPages->push($page); } } } - return $newPages; } + + $newPages->merge($this->addRegisteredDataObjects()); + + return $newPages; } /** @@ -115,16 +220,24 @@ class GoogleSitemap extends Controller { if(!GoogleSitemap::$google_notification_enabled || Director::isDev()) return; - $location = urlencode(Director::absoluteBaseURL() . '/sitemap.xml'); + $location = urlencode(Controller::join_links( + Director::absoluteBaseURL(), + 'sitemap.xml' + )); - $response = HTTP::sendRequest("www.google.com", "/webmasters/sitemaps/ping", - "sitemap=" . $location); + $response = HTTP::sendRequest( + "www.google.com", + "/webmasters/sitemaps/ping", + sprintf("sitemap=%s", $location) + ); return $response; } /** * Enable pings to google.com whenever sitemap changes. + * + * @return void */ public static function enable_google_notification() { self::$google_notification_enabled = true; @@ -132,11 +245,16 @@ class GoogleSitemap extends Controller { /** * Disables pings to google when the sitemap changes. + * + * @return void */ public static function disable_google_notification() { self::$google_notification_enabled = false; } + /** + * Default controller handler for the sitemap.xml file + */ function index($url) { if(self::$enabled) { SSViewer::set_source_file_comments(false); @@ -149,11 +267,21 @@ class GoogleSitemap extends Controller { } } + /** + * Enable the sitemap.xml file + * + * @return void + */ public static function enable() { self::$enabled = true; } + /** + * Disable the sitemap.xml file + * + * @return void + */ public static function disable() { self::$enabled = false; } -} \ No newline at end of file +} diff --git a/code/GoogleSitemapDecorator.php b/code/GoogleSitemapDecorator.php index 9c59cae..1b0968a 100644 --- a/code/GoogleSitemapDecorator.php +++ b/code/GoogleSitemapDecorator.php @@ -6,7 +6,16 @@ * * @package googlesitemaps */ -class GoogleSitemapDecorator extends SiteTreeDecorator { +class GoogleSitemapDecorator extends DataObjectDecorator { + + +} + +/** + * @package googlesitemaps + */ + +class GoogleSitemapSiteTreeDecorator extends SiteTreeDecorator { function extraStatics() { return array( diff --git a/docs/_manifest_exclude b/docs/_manifest_exclude new file mode 100644 index 0000000..e69de29 diff --git a/docs/en/index.md b/docs/en/index.md new file mode 100644 index 0000000..1545069 --- /dev/null +++ b/docs/en/index.md @@ -0,0 +1,84 @@ +# Google Sitemaps Module + +SilverStripe provides support for the Google Sitemaps XML system, enabling +Google and other search engines to see all pages on your site. This helps +your SilverStripe website rank well in search engines, and to encourage the +information on your site to be discovered by Google quickly. + +Therefore, all Silverstripe websites contain a special controller which can +be visited: http://yoursite.com/sitemap.xml + +See http://en.wikipedia.org/wiki/Sitemaps for info on this format + +In addition, whenever you publish a new or republish an existing page, +SilverStripe automatically informs Google of the change, encouraging a Google +to take notice. If you install the SilverStripe Google Analytics module, you +can see if Google has updated your page as a result. + +By default, SilverStripe informs Google that the importance of a page depends +on its position of in the sitemap. "Top level" pages are most important, and +the deeper a page is nested, the less important it is. (For each level, +Importance drops from 1.0, to 0.9, to 0.8, and so on, until 0.1 is reached). + +In the CMS, in the "Content/GoogleSitemap" tab, you can set the page importance +manually, including requesting to have the page excluded from the google sitemap. + + +## Setup automatic pinging + + GoogleSitemap::enable_google_notificaton(); + +### Include Dataobjects in listing + +The module provides support for including DataObject subclasses as pages in +the SiteTree such as comments, forum posts and other pages which are created +by DataObjects. + +To include a DataObject in the Sitemap it requires that your subclass defines +two functions. + + * AbsoluteLink() function which returns the URL for this DataObject + * canView() function which returns a boolean value. + +The SilverStripe convention is to use a Link function to define the AbsoluteLink. +This enables $Link to work for relative links (while in templates) and $AbsoluteLink +to work for RSS Feeds and the Sitemap Links. + +The following is a barebones example of a DataObject called 'MyDataObject'. It assumes +that you have a controller called 'MyController' which has a show method to show the +DataObject by it's ID. + + Link()); + } + + function Link() { + return 'MyController/show/'. $this->ID; + } + } + + +After those methods have been defined on your DataObject you now need to tell +googlesitemaps that it should be listed in the sitemap.xml file. Include the +following in your _config.php file. + + GoogleSitemap::register_dataobject('MyDataObject'); + +If you need to change the frequency of the indexing, you can pass the change +frequency (daily, weekly, monthly) as a second parameter to register(). + +So instead of the previous code you would write: + + GoogleSitemap::register('MyDataObject', 'daily'); + +See the following blog post for more information: + +http://www.silvercart.org/blog/dataobjects-and-googlesitemaps/ \ No newline at end of file