feat: implement Sitemapable (#181)

This commit is contained in:
Will Rossiter 2023-10-28 09:23:54 +13:00
parent def6be347c
commit 8a7c8aa9cc
No known key found for this signature in database
GPG Key ID: 7FD2A809B22259EF
10 changed files with 164 additions and 95 deletions

View File

@ -6,13 +6,13 @@
## Maintainer Contact
* Will Rossiter (Nickname: wrossiter, willr) <will@fullscreen.io>
- Will Rossiter (Nickname: wrossiter, willr) <will@fullscreen.io>
## Installation
> composer require "wilr/silverstripe-googlesitemaps"
If you're using Silverstripe 5 then version 3 or `dev-main` will work.
If you're using Silverstripe 5 then version `3` or `dev-main` will work.
For Silverstripe 4 use the `2.x` branch line.
@ -34,4 +34,4 @@ See docs/en for more information about configuring the module.
## Troubleshooting
* Flush this route to ensure the changes take effect (e.g http://yoursite.com/sitemap.xml?flush=1)
- Flush this route to ensure the changes take effect (e.g http://yoursite.com/sitemap.xml?flush=1)

10
_config.php Normal file
View File

@ -0,0 +1,10 @@
<?php
use SilverStripe\Core\ClassInfo;
use Wilr\GoogleSitemaps\GoogleSitemap;
if (0 === strpos(ltrim($_SERVER['REQUEST_URI'], '/'), 'sitemap')) {
foreach (ClassInfo::implementorsOf(Sitemapable::class) as $className) {
GoogleSitemap::register_dataobject($className);
}
}

View File

@ -35,6 +35,7 @@ config file `mysite/_config/googlesitemaps.yml` with the following outline:
After: googlesitemaps
---
Wilr\GoogleSitemaps\GoogleSitemap:
enabled: true
objects_per_sitemap: 1000
google_notification_enabled: false
@ -50,23 +51,12 @@ editing the `google_notification_enabled` option to true
After: googlesitemaps
---
Wilr\GoogleSitemaps\GoogleSitemap:
enabled: true
objects_per_sitemap: 1000
google_notification_enabled: true
use_show_in_search: true
### Bing Ping Support
To ping Bing whenever your sitemap is updated, set `bing_notification_enabled`
---
Name: customgooglesitemaps
After: googlesitemaps
---
Wilr\GoogleSitemaps\GoogleSitemap:
enabled: true
bing_notification_enabled: true
### Including DataObjects
The module provides support for including DataObject subclasses as pages in the
@ -76,8 +66,8 @@ database as DataObject subclasses.
To include a DataObject instance 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.
- AbsoluteLink() function which returns the URL for this DataObject
- canView() function which returns a boolean value.
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
@ -103,10 +93,9 @@ to show the DataObject by its ID.
}
}
After those methods have been defined on your DataObject you now need to tell
the Google Sitemaps module that it should be listed in the sitemap.xml file. To
do that, include the following in your _config.php file.
do that, include the following in your \_config.php file.
use Wilr\GoogleSitemaps\GoogleSitemap;
@ -138,3 +127,21 @@ urls to include.
'/Security/',
'/Security/login/'
));
### Sitemapable
For automatic registration of a DataObject subclass, implement the `Sitemapable`
extension
```
<?php
class MyDataObject extends DataObject implements Sitemapable
{
public function AbsoluteLink()
{
// ..
}
}
```

View File

@ -1,7 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<ruleset name="SilverStripe">
<description>CodeSniffer ruleset for SilverStripe coding conventions.</description>
<file>src</file>
<file>tests</file>
<!-- base rules are PSR-2 -->
<rule ref="PSR2">
<!-- Current exclusions -->

View File

@ -75,8 +75,7 @@ class GoogleSitemapController extends Controller
}
}
if (
GoogleSitemap::enabled()
if (GoogleSitemap::enabled()
&& $class
&& ($page > 0)
&& ($class == SiteTree::class || $class == 'GoogleSitemapRoute' || GoogleSitemap::is_registered($class))

View File

@ -90,8 +90,7 @@ class GoogleSitemap
* @var array
*/
private static $search_indexes = [
'google' => 'http://www.google.com/webmasters/sitemaps/ping?sitemap=',
'bing' => 'http://www.bing.com/ping?sitemap=',
'google' => 'http://www.google.com/webmasters/sitemaps/ping?sitemap='
];
/**

14
src/Sitemapable.php Normal file
View File

@ -0,0 +1,14 @@
<?php
namespace Wilr\GoogleSitemaps;
interface Sitemapable
{
/**
* Return the absolute URL for this object
*
* @return string
*/
public function AbsoluteLink();
}

View File

@ -20,12 +20,22 @@ use Wilr\GoogleSitemaps\Tests\Model\UnviewableDataObject;
class GoogleSitemapTest extends FunctionalTest
{
protected static $fixture_file = [
'GoogleSitemapTest.yml',
'GoogleSitemapPageTest.yml',
'GoogleSitemapTest.yml'
];
protected $usesDatabase = true;
public static function get_fixture_file()
{
$files = [__DIR__ . '/GoogleSitemapTest.yml'];
if (class_exists('Page')) {
$files[] = __DIR__ . '/GoogleSitemapPageTest.yml';
}
return $files;
}
protected static $extra_dataobjects = [
TestDataObject::class,
OtherDataObject::class,
@ -55,13 +65,13 @@ class GoogleSitemapTest extends FunctionalTest
public function testCanIncludeInGoogleSitemap(): void
{
GoogleSitemap::register_dataobject(TestDataObject::class, '');
$unused = $this->objFromFixture(TestDataObject::class, 'UnindexedDataObject');
$this->assertFalse($unused->canIncludeInGoogleSitemap());
$used = $this->objFromFixture(TestDataObject::class, 'DataObjectTest2');
$this->assertTrue($used->canIncludeInGoogleSitemap());
$used->setPrivate();
$this->assertFalse($used->canIncludeInGoogleSitemap());
}
public function testIndexFileWithCustomRoute(): void
@ -77,31 +87,29 @@ class GoogleSitemapTest extends FunctionalTest
public function testGetItems(): void
{
GoogleSitemap::register_dataobject(TestDataObject::class, '');
$google = new GoogleSitemap();
$items = GoogleSitemap::get_items(TestDataObject::class, 1);
$this->assertEquals(2, $items->count());
$items = $google->getItems(TestDataObject::class, 1);
$this->assertListEquals(array(
array("Priority" => "0.2"),
array("Priority" => "0.4")
), $items);
$this->assertEquals(3, $items->count());
GoogleSitemap::register_dataobject(OtherDataObject::class);
$this->assertEquals(1, GoogleSitemap::get_items(OtherDataObject::class, 1)->count());
$this->assertEquals(1, $google->getItems(OtherDataObject::class, 1)->count());
GoogleSitemap::register_dataobject(UnviewableDataObject::class);
$this->assertEquals(0, GoogleSitemap::get_items(UnviewableDataObject::class, 1)->count());
$this->assertEquals(0, $google->getItems(UnviewableDataObject::class, 1)->count());
}
public function testGetItemsWithCustomRoutes(): void
{
GoogleSitemap::register_routes(array(
GoogleSitemap::register_routes([
'/test-route/',
'/someother-route/',
'/fake-sitemap-route/'
));
]);
$items = GoogleSitemap::get_items('GoogleSitemapRoute', 1);
$google = new GoogleSitemap();
$items = $google->getItems('GoogleSitemapRoute', 1);
$this->assertEquals(3, $items->count());
}
@ -110,6 +118,16 @@ class GoogleSitemapTest extends FunctionalTest
GoogleSitemap::register_dataobject(TestDataObject::class);
GoogleSitemap::register_dataobject(OtherDataObject::class);
$obj = $this->objFromFixture(TestDataObject::class, 'DataObjectTest1');
$table = $obj->baseTable();
DB::query("UPDATE \"" . $table . "\" SET \"LastEdited\"='2023-02-13 00:00:00'");
$obj2 = $this->objFromFixture(OtherDataObject::class, 'OtherDataObjectTest2');
$table = $obj2->baseTable();
DB::query("UPDATE \"" . $table . "\" SET \"LastEdited\"='2023-02-13 00:00:00'");
$response = $this->get('sitemap.xml');
$body = $response->getBody();
@ -153,9 +171,19 @@ class GoogleSitemapTest extends FunctionalTest
Config::inst()->set(GoogleSitemap::class, 'objects_per_sitemap', 1);
GoogleSitemap::register_dataobject(TestDataObject::class);
$obj = $this->objFromFixture(TestDataObject::class, 'DataObjectTest1');
$obj1 = $this->objFromFixture(TestDataObject::class, 'DataObjectTest2');
$obj2 = $this->objFromFixture(TestDataObject::class, 'UnindexedDataObject');
$table = $obj->baseTable();
DB::query("UPDATE \"" . $table . "\" SET \"LastEdited\"='2023-02-13 00:00:00' WHERE \"ID\"='" . $obj->ID . "'");
DB::query("UPDATE \"" . $table . "\" SET \"LastEdited\"='2023-02-13 00:00:00' WHERE \"ID\"='" . $obj1->ID . "'");
DB::query("UPDATE \"" . $table . "\" SET \"LastEdited\"='2023-02-13 00:00:00' WHERE \"ID\"='" . $obj2->ID . "'");
$response = $this->get('sitemap.xml');
$body = $response->getBody();
$this->assertXmlStringEqualsXmlFile(__DIR__ . '/xml/' . __FUNCTION__ . '.xml', $body);
Config::inst()->set(GoogleSitemap::class, 'objects_per_sitemap', $original);
@ -164,11 +192,11 @@ class GoogleSitemapTest extends FunctionalTest
public function testRegisterRoutesIncludesAllRoutes(): void
{
GoogleSitemap::register_route('/test/');
GoogleSitemap::register_routes(array(
GoogleSitemap::register_routes([
'/test/', // duplication should be replaced
'/unittests/',
'/anotherlink/'
), 'weekly');
], 'weekly');
$response = $this->get('sitemap.xml/sitemap/GoogleSitemapRoute/1');
$body = $response->getBody();
@ -193,7 +221,7 @@ class GoogleSitemapTest extends FunctionalTest
public function testGetItemsWithPages(): void
{
if (!class_exists('Page')) {
if (!class_exists(SiteTree::class)) {
$this->markTestIncomplete('No cms module installed, page related test skipped');
}
@ -205,10 +233,10 @@ class GoogleSitemapTest extends FunctionalTest
$page2->publishSingle();
$page2->flushCache();
$this->assertListContains(array(
array('Title' => 'Testpage1'),
array('Title' => 'Testpage2')
), GoogleSitemap::inst()->getItems(SiteTree::class), "There should be 2 pages in the sitemap after publishing");
$this->assertListContains([
['Title' => 'Testpage1'],
['Title' => 'Testpage2']
], GoogleSitemap::inst()->getItems(SiteTree::class), "There should be 2 pages in the sitemap after publishing");
// check if we make a page readonly that it is hidden
$page2->CanViewType = 'LoggedInUsers';
@ -217,9 +245,9 @@ class GoogleSitemapTest extends FunctionalTest
$this->logOut();
$this->assertListEquals(array(
array('Title' => 'Testpage1')
), GoogleSitemap::inst()->getItems(SiteTree::class), "There should be only 1 page, other is logged in only");
$this->assertListEquals([
['Title' => 'Testpage1']
], GoogleSitemap::inst()->getItems(SiteTree::class), "There should be only 1 page, other is logged in only");
}
public function testAccess(): void

View File

@ -8,6 +8,7 @@ use SilverStripe\Control\Director;
class TestDataObject extends DataObject implements TestOnly
{
protected $private = false;
private static $db = array(
'Priority' => 'Varchar(10)'
@ -15,9 +16,19 @@ class TestDataObject extends DataObject implements TestOnly
public function canView($member = null)
{
if ($this->private) {
return false;
}
return true;
}
public function setPrivate()
{
$this->private = true;
}
public function AbsoluteLink()
{
return Director::absoluteBaseURL();