mirror of
https://github.com/wilr/silverstripe-googlesitemaps.git
synced 2024-09-30 05:09:11 +02:00
Compare commits
187 Commits
Author | SHA1 | Date | |
---|---|---|---|
|
794778ae20 | ||
|
05e6963f04 | ||
|
3f75c06a77 | ||
|
a13d3dd9de | ||
|
59acd419ef | ||
|
aeeb960566 | ||
|
c7f260bb33 | ||
|
c2f5162e97 | ||
|
59e56c3b69 | ||
|
d9da059a83 | ||
|
54ddc6ccf7 | ||
|
cbccf88a62 | ||
|
8a7c8aa9cc | ||
|
def6be347c | ||
|
523d196d12 | ||
|
f064330acd | ||
|
f1bddb626d | ||
|
77a366871e | ||
|
81b8d870a4 | ||
|
9b0e0f1064 | ||
|
7370c85dae | ||
|
bf6156c103 | ||
|
a29de80408 | ||
|
0fbfec9f66 | ||
|
54e5043622 | ||
|
286115174d | ||
|
cfcb002cf3 | ||
|
0e965ecd46 | ||
|
cb56ac11d9 | ||
|
6adb11b5bf | ||
|
8d321bd0a1 | ||
|
35665402cb | ||
|
7326028c7c | ||
|
401f723e7e | ||
|
71e62966d0 | ||
|
1915f9ab59 | ||
|
1d535ade02 | ||
|
56b25a424f | ||
|
67e8d601d5 | ||
|
67c8ad044e | ||
|
a333b56711 | ||
|
745de7a7af | ||
|
b5f322646f | ||
|
312b78f88b | ||
|
f026001f8b | ||
|
ced2213be4 | ||
|
17b21683a2 | ||
|
7668ba8f48 | ||
|
4214c091e5 | ||
|
799f669da2 | ||
|
982d96c203 | ||
|
56ef63f6c3 | ||
|
c1fc291e59 | ||
|
36c9b9f1d4 | ||
|
1397fa0e30 | ||
|
473b88ee05 | ||
|
2caacf8e86 | ||
|
7f8937dd1f | ||
|
764a8f5bb2 | ||
|
d8beb33d86 | ||
|
2860c390b0 | ||
|
41390002d5 | ||
|
710b8c3518 | ||
|
4d131b06ac | ||
|
f7b48e2b99 | ||
|
71c662e4d1 | ||
|
64ead69f4e | ||
|
572da5fa8a | ||
|
a0a8c34d2f | ||
|
adb3ed94fc | ||
|
c3fcd84542 | ||
|
776e6bd9d9 | ||
|
36091bc840 | ||
|
f343f312eb | ||
|
6e49a3e01e | ||
|
b42305dfef | ||
|
729311249c | ||
|
326b1ce4b0 | ||
|
98f65de824 | ||
|
628dbd8404 | ||
|
c5388b6e5b | ||
|
29be1bc504 | ||
|
c00f420a8b | ||
|
36ef636249 | ||
|
e51dd3877f | ||
|
f51c9e12bf | ||
|
64a9465d7e | ||
|
bfaaa42e10 | ||
|
d5b35cf36c | ||
|
b11c6d771e | ||
|
450283bd1b | ||
|
1d8c6faa2e | ||
|
d1d5c2b268 | ||
|
9e7aed0f2d | ||
|
742724dad4 | ||
|
38c489c931 | ||
|
0daa163ec1 | ||
|
c8e142718d | ||
|
2699956b23 | ||
|
27898476d8 | ||
|
71567fec58 | ||
|
d88ed57a07 | ||
|
030dd83613 | ||
|
b40a66422b | ||
|
313e4649c1 | ||
|
840f664cd9 | ||
|
56b23dbdf8 | ||
|
77f6a02ae7 | ||
|
526a0eec4d | ||
|
e0821f8f03 | ||
|
830abcc4f9 | ||
|
fa92f0d13f | ||
|
8307b56f3f | ||
|
d8f8cafd2d | ||
|
b8766edbee | ||
|
9f845f5af7 | ||
|
4333bb6777 | ||
|
51fa6f4c25 | ||
|
671522de5f | ||
|
286c79160a | ||
|
552684ce12 | ||
|
930fd7f9ac | ||
|
e607ea9fc4 | ||
|
31f9182e95 | ||
|
31a5bab455 | ||
|
c031c58774 | ||
|
083251bd6d | ||
|
bb7fbb8308 | ||
|
cc0c20b3a8 | ||
|
26838c70d0 | ||
|
49053f7d8f | ||
|
02197a35b0 | ||
|
11a894a10e | ||
|
4701bd985a | ||
|
6fd2901aaf | ||
|
ecd3551ec0 | ||
|
356350ede5 | ||
|
9f461b8d7c | ||
|
90ddb6c98f | ||
|
b7da114f99 | ||
|
5228024ba5 | ||
|
7e42cbef76 | ||
|
99282db32a | ||
|
9c4e8be4bd | ||
|
9f6e1bab41 | ||
|
0cfd3d80f5 | ||
|
289ed77139 | ||
|
7af5077d99 | ||
|
5d10786341 | ||
|
41238f5186 | ||
|
dd41e39484 | ||
|
a641ded926 | ||
|
e2a1dc5250 | ||
|
c549ca4d3f | ||
|
4e1db4d3f9 | ||
|
58376e277c | ||
|
a1ac8d229a | ||
|
82778473a8 | ||
|
0ffc6ac2be | ||
|
26f1bbefdd | ||
|
40a3a0adeb | ||
|
f9202c28ad | ||
|
3a3de779af | ||
|
092b2e10ae | ||
|
91a543ea63 | ||
|
6d530af1a2 | ||
|
744d879efb | ||
|
d3a12fe225 | ||
|
5c2547a3a3 | ||
|
05e62cbbc8 | ||
|
a87116b3c3 | ||
|
5f26b65061 | ||
|
096138d594 | ||
|
edfcd22021 | ||
|
92bcb27cac | ||
|
e67f807f8a | ||
|
eeac25c93e | ||
|
27e79a4e02 | ||
|
bbd5e29630 | ||
|
198a850f8d | ||
|
12e46e5b2b | ||
|
8ebf4e9f22 | ||
|
3b3742a1bc | ||
|
5f06483b73 | ||
|
708cb5d166 | ||
|
ba69d670e5 | ||
|
9a95e58d6e |
17
.editorconfig
Normal file
17
.editorconfig
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
# For more information about the properties used in this file,
|
||||||
|
# please see the EditorConfig documentation:
|
||||||
|
# http://editorconfig.org
|
||||||
|
|
||||||
|
[*]
|
||||||
|
charset = utf-8
|
||||||
|
end_of_line = lf
|
||||||
|
indent_size = 4
|
||||||
|
indent_style = space
|
||||||
|
insert_final_newline = true
|
||||||
|
trim_trailing_whitespace = true
|
||||||
|
|
||||||
|
[{*.yml,package.json}]
|
||||||
|
indent_size = 2
|
||||||
|
|
||||||
|
# The indent size used in the package.json file cannot be changed:
|
||||||
|
# https://github.com/npm/npm/pull/3180#issuecomment-16336516
|
4
.gitattributes
vendored
Normal file
4
.gitattributes
vendored
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
/tests export-ignore
|
||||||
|
/docs export-ignore
|
||||||
|
/.gitignore export-ignore
|
||||||
|
/.travis.yml export-ignore
|
11
.github/workflows/ci.yml
vendored
Normal file
11
.github/workflows/ci.yml
vendored
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
name: CI
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
pull_request:
|
||||||
|
workflow_dispatch:
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
ci:
|
||||||
|
name: CI
|
||||||
|
uses: silverstripe/gha-ci/.github/workflows/ci.yml@v1
|
3
.gitignore
vendored
3
.gitignore
vendored
@ -1 +1,4 @@
|
|||||||
.DS_Store
|
.DS_Store
|
||||||
|
/vendor
|
||||||
|
/public
|
||||||
|
/composer.lock
|
||||||
|
22
.travis.yml
22
.travis.yml
@ -1,22 +0,0 @@
|
|||||||
# See https://github.com/silverstripe-labs/silverstripe-travis-support for setup details
|
|
||||||
|
|
||||||
language: php
|
|
||||||
php:
|
|
||||||
- 5.3
|
|
||||||
|
|
||||||
env:
|
|
||||||
- DB=MYSQL CORE_RELEASE=3.1
|
|
||||||
- DB=PGSQL CORE_RELEASE=master
|
|
||||||
|
|
||||||
matrix:
|
|
||||||
include:
|
|
||||||
- php: 5.4
|
|
||||||
env: DB=MYSQL CORE_RELEASE=master
|
|
||||||
|
|
||||||
before_script:
|
|
||||||
- git clone git://github.com/silverstripe-labs/silverstripe-travis-support.git ~/travis-support
|
|
||||||
- php ~/travis-support/travis_setup.php --source `pwd` --target ~/builds/ss
|
|
||||||
- cd ~/builds/ss
|
|
||||||
|
|
||||||
script:
|
|
||||||
- phpunit googlesitemaps/tests/
|
|
2
.upgrade.yml
Normal file
2
.upgrade.yml
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
mappings:
|
||||||
|
GoogleSitemap: Wilr\GoogleSitemaps\GoogleSitemap
|
37
LICENSE
37
LICENSE
@ -1,24 +1,13 @@
|
|||||||
* Copyright (c) 2011, Silverstripe Ltd.
|
BSD 3-Clause License
|
||||||
* All rights reserved.
|
|
||||||
*
|
Copyright (c) 2020, Fullscreen Interactive All rights reserved.
|
||||||
* Redistribution and use in source and binary forms, with or without
|
|
||||||
* modification, are permitted provided that the following conditions are met:
|
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
|
||||||
* * Redistributions of source code must retain the above copyright
|
|
||||||
* notice, this list of conditions and the following disclaimer.
|
Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
|
||||||
* * Redistributions in binary form must reproduce the above copyright
|
|
||||||
* notice, this list of conditions and the following disclaimer in the
|
Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
|
||||||
* documentation and/or other materials provided with the distribution.
|
|
||||||
* * Neither the name of the <organization> nor the
|
Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
|
||||||
* names of its contributors may be used to endorse or promote products
|
|
||||||
* derived from this software without specific prior written permission.
|
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
*
|
|
||||||
* THIS SOFTWARE IS PROVIDED BY Silverstripe Ltd. ``AS IS'' AND ANY
|
|
||||||
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
|
||||||
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
|
||||||
* DISCLAIMED. IN NO EVENT SHALL Silverstripe Ltd. BE LIABLE FOR ANY
|
|
||||||
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
|
||||||
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
|
||||||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
|
||||||
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
||||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
|
||||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
||||||
|
31
README.md
31
README.md
@ -1,28 +1,37 @@
|
|||||||
# Google Sitemaps Module
|
# Google Sitemaps Module
|
||||||
|
|
||||||
[![Build Status](https://secure.travis-ci.org/silverstripe-labs/silverstripe-googlesitemaps.png?branch=master)](http://travis-ci.org/silverstripe-labs/silverstripe-googlesitemaps)
|
[![Build Status](http://img.shields.io/travis/wilr/silverstripe-googlesitemaps.svg?style=flat-square)](http://travis-ci.org/wilr/silverstripe-googlesitemaps)
|
||||||
|
[![Version](http://img.shields.io/packagist/v/wilr/silverstripe-googlesitemaps.svg?style=flat-square)](https://packagist.org/packages/wilr/silverstripe-googlesitemaps)
|
||||||
|
[![License](http://img.shields.io/packagist/l/wilr/silverstripe-googlesitemaps.svg?style=flat-square)](LICENSE.md)
|
||||||
|
|
||||||
## Maintainer Contact
|
## Maintainer Contact
|
||||||
|
|
||||||
* Will Rossiter (Nickname: wrossiter, willr) <will@fullscreen.io>
|
- Will Rossiter (Nickname: wrossiter, willr) <will@fullscreen.io>
|
||||||
|
|
||||||
## Requirements
|
## Installation
|
||||||
|
|
||||||
* SilverStripe 3.0
|
> composer require "wilr/silverstripe-googlesitemaps"
|
||||||
|
|
||||||
|
If you're using Silverstripe 5 then version `3` or `dev-main` will work.
|
||||||
|
|
||||||
|
For Silverstripe 4 use the `2.x` branch line.
|
||||||
|
|
||||||
## Documentation
|
## Documentation
|
||||||
|
|
||||||
SilverStripe provides support for the Google Sitemaps XML system, enabling
|
Provides support for the [Sitemaps XML Protocol](http://www.sitemaps.org/protocol.html),
|
||||||
Google and other search engines to see all urls on your site. This helps
|
enabling Google, Bing and other search engines to index the web pages on your
|
||||||
your SilverStripe website rank well in search engines, and to encourage the
|
site. This helps your SilverStripe website rank well in search engines, and to
|
||||||
information on your site to be discovered by Google quickly.
|
encourage the information on your site to be discovered by Google quickly.
|
||||||
|
|
||||||
Therefore, all Silverstripe websites contain a special controller which can
|
Any new pages published or unpublished on your website automatically update the
|
||||||
be visited: http://yoursite.com/sitemap.xml
|
Sitemap.
|
||||||
|
|
||||||
See http://en.wikipedia.org/wiki/Sitemaps for info on this format.
|
The XML Sitemap can be accessed by going to http://yoursite.com/sitemap.xml
|
||||||
|
|
||||||
## Usage Overview
|
## Usage Overview
|
||||||
|
|
||||||
See docs/en for more information about configuring the module.
|
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)
|
||||||
|
15
_config.php
15
_config.php
@ -1,11 +1,10 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
// add the extension to pages
|
use SilverStripe\Core\ClassInfo;
|
||||||
if (class_exists('SiteTree')) {
|
use Wilr\GoogleSitemaps\GoogleSitemap;
|
||||||
SiteTree::add_extension('GoogleSitemapSiteTreeExtension');
|
|
||||||
|
if (0 === strpos(ltrim($_SERVER['REQUEST_URI'], '/'), 'sitemap')) {
|
||||||
|
foreach (ClassInfo::implementorsOf(Sitemapable::class) as $className) {
|
||||||
|
GoogleSitemap::register_dataobject($className);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// if you need to add this to DataObjects include the following in
|
|
||||||
// your own _config:
|
|
||||||
|
|
||||||
// GoogleSiteMap::register_dataobject('MyDataObject');
|
|
||||||
|
@ -1,8 +1,14 @@
|
|||||||
---
|
---
|
||||||
Name: googlesitemaps
|
Name: googlesitemaps
|
||||||
---
|
---
|
||||||
GoogleSitemap:
|
Wilr\GoogleSitemaps\GoogleSitemap:
|
||||||
enabled: true
|
enabled: true
|
||||||
objects_per_sitemap: 1000
|
objects_per_sitemap: 1000
|
||||||
google_notification_enabled: false
|
|
||||||
use_show_in_search: true
|
use_show_in_search: true
|
||||||
|
---
|
||||||
|
Only:
|
||||||
|
classexists: SilverStripe\CMS\Model\SiteTree
|
||||||
|
---
|
||||||
|
SilverStripe\CMS\Model\SiteTree:
|
||||||
|
extensions:
|
||||||
|
- Wilr\GoogleSitemaps\Extensions\GoogleSitemapSiteTreeExtension
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
---
|
---
|
||||||
Name: googlesitemaproutes
|
Name: googlesitemaproutes
|
||||||
---
|
---
|
||||||
Director:
|
SilverStripe\Control\Director:
|
||||||
rules:
|
rules:
|
||||||
'sitemap.xml': 'GoogleSitemapController'
|
'sitemap.xml': 'Wilr\GoogleSitemaps\Control\GoogleSitemapController'
|
||||||
|
1
code-of-conduct.md
Normal file
1
code-of-conduct.md
Normal file
@ -0,0 +1 @@
|
|||||||
|
When having discussions about this module in issues or pull request please adhere to the [SilverStripe Community Code of Conduct](https://docs.silverstripe.org/en/contributing/code_of_conduct).
|
@ -1,443 +0,0 @@
|
|||||||
<?php
|
|
||||||
/**
|
|
||||||
* Sitemaps are a way to tell Google about pages on your site that they might
|
|
||||||
* not otherwise discover. In its simplest terms, a XML Sitemap usually called
|
|
||||||
* a Sitemap, with a capital S—is a list of the pages on your website.
|
|
||||||
*
|
|
||||||
* Creating and submitting a Sitemap helps make sure that Google knows about
|
|
||||||
* all the pages on your site, including URLs that may not be discoverable by
|
|
||||||
* Google's normal crawling process.
|
|
||||||
*
|
|
||||||
* The GoogleSitemap handle requests to 'sitemap.xml'
|
|
||||||
* the other two classes are used to render the sitemap.
|
|
||||||
*
|
|
||||||
* You can notify ("ping") Google about a changed sitemap
|
|
||||||
* automatically whenever a new page is published or unpublished.
|
|
||||||
* By default, Google is not notified, and will pick up your new
|
|
||||||
* sitemap whenever the GoogleBot visits your website.
|
|
||||||
*
|
|
||||||
* Enabling notification of Google after every publish (in your _config.php):
|
|
||||||
*
|
|
||||||
* <example>
|
|
||||||
* GoogleSitemap::enable_google_notificaton();
|
|
||||||
* </example>
|
|
||||||
*
|
|
||||||
* @see http://www.google.com/support/webmasters/bin/answer.py?hl=en&answer=34609
|
|
||||||
*
|
|
||||||
* @package googlesitemaps
|
|
||||||
*/
|
|
||||||
class GoogleSitemap {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* List of {@link DataObject} class names to include. As well as the change
|
|
||||||
* frequency and priority of each class.
|
|
||||||
*
|
|
||||||
* @var array
|
|
||||||
*/
|
|
||||||
private static $dataobjects = array();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* List of custom routes to include in the sitemap (such as controller
|
|
||||||
* subclasses) as well as the change frequency and priority.
|
|
||||||
*
|
|
||||||
* @var array
|
|
||||||
*/
|
|
||||||
private static $routes = 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 how often is this DataObject updated?
|
|
||||||
* Possible values:
|
|
||||||
* always, hourly, daily, weekly, monthly, yearly, never
|
|
||||||
* @param string $priority How important is this DataObject in comparison to other urls?
|
|
||||||
* Possible values: 0.1, 0.2 ... , 0.9, 1.0
|
|
||||||
*
|
|
||||||
* @return void
|
|
||||||
*/
|
|
||||||
public static function register_dataobject($className, $changeFreq = 'monthly', $priority = '0.6') {
|
|
||||||
if (!self::is_registered($className)) {
|
|
||||||
$className::add_extension('GoogleSitemapExtension');
|
|
||||||
|
|
||||||
self::$dataobjects[$className] = array(
|
|
||||||
'frequency' => ($changeFreq) ? $changeFreq : 'monthly',
|
|
||||||
'priority' => ($priority) ? $priority : '0.6'
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Registers multiple dataobjects in a single line. See {@link register_dataobject}
|
|
||||||
* for the heavy lifting
|
|
||||||
*
|
|
||||||
* @param array $dataobjects array of class names of DataObject to register
|
|
||||||
* @param string $changeFreq how often is this DataObject updated?
|
|
||||||
* Possible values:
|
|
||||||
* always, hourly, daily, weekly, monthly, yearly, never
|
|
||||||
* @param string $priority How important is this DataObject in comparison to other urls?
|
|
||||||
* Possible values: 0.1, 0.2 ... , 0.9, 1.0
|
|
||||||
*
|
|
||||||
* @return void
|
|
||||||
*/
|
|
||||||
public static function register_dataobjects($dataobjects, $changeFreq = 'monthly', $priority = '0.6') {
|
|
||||||
foreach($dataobjects as $obj) {
|
|
||||||
self::register_dataobject($obj, $changeFreq, $priority);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 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 isset(self::$dataobjects[$className]);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Unregisters a class from the sitemap. Mostly used for the test suite
|
|
||||||
*
|
|
||||||
* @param string
|
|
||||||
*/
|
|
||||||
public static function unregister_dataobject($className) {
|
|
||||||
unset(self::$dataobjects[$className]);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Clears registered {@link DataObjects}. Useful for unit tests.
|
|
||||||
*
|
|
||||||
* @return void
|
|
||||||
*/
|
|
||||||
public static function clear_registered_dataobjects() {
|
|
||||||
self::$dataobjects = array();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Register a given route to the sitemap list
|
|
||||||
*
|
|
||||||
* @param string
|
|
||||||
* @param string
|
|
||||||
* @param string
|
|
||||||
*
|
|
||||||
* @return void
|
|
||||||
*/
|
|
||||||
public static function register_route($route, $changeFreq = 'monthly', $priority = '0.6') {
|
|
||||||
self::$routes = array_merge(self::$routes, array(
|
|
||||||
$route => array(
|
|
||||||
'frequency' => ($changeFreq) ? $changeFreq : 'monthly',
|
|
||||||
'priority' => ($priority) ? $priority : '0.6'
|
|
||||||
)
|
|
||||||
));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Registers a given list of relative urls. Will be merged with the current
|
|
||||||
* registered routes. If you want to replace them, please call {@link clear_routes}
|
|
||||||
*
|
|
||||||
* @param array
|
|
||||||
* @param string
|
|
||||||
* @param string
|
|
||||||
*
|
|
||||||
* @return void
|
|
||||||
*/
|
|
||||||
public static function register_routes($routes, $changeFreq = 'monthly', $priority = '0.6') {
|
|
||||||
foreach($routes as $route) {
|
|
||||||
self::register_route($route, $changeFreq, $priority);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Clears registered routes
|
|
||||||
*
|
|
||||||
* @return void
|
|
||||||
*/
|
|
||||||
public static function clear_registered_routes() {
|
|
||||||
self::$routes = array();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Constructs the list of data to include in the rendered sitemap. Links
|
|
||||||
* can include pages from the website, dataobjects (such as forum posts)
|
|
||||||
* as well as custom registered paths.
|
|
||||||
*
|
|
||||||
* @param string
|
|
||||||
* @param int
|
|
||||||
*
|
|
||||||
* @return ArrayList
|
|
||||||
*/
|
|
||||||
public static function get_items($class, $page = 1) {
|
|
||||||
$output = new ArrayList();
|
|
||||||
$count = Config::inst()->get('GoogleSitemap', 'objects_per_sitemap');
|
|
||||||
$filter = Config::inst()->get('GoogleSitemap', 'use_show_in_search');
|
|
||||||
|
|
||||||
if($class == "SiteTree") {
|
|
||||||
$filter = ($filter) ? "\"ShowInSearch\" = 1" : "";
|
|
||||||
$instances = Versioned::get_by_stage('SiteTree', 'Live', $filter);
|
|
||||||
}
|
|
||||||
else if($class == "GoogleSitemapRoute") {
|
|
||||||
$instances = array_slice(self::$routes, ($page - 1) * $count, $count);
|
|
||||||
$output = new ArrayList();
|
|
||||||
|
|
||||||
if($instances) {
|
|
||||||
foreach($instances as $route => $config) {
|
|
||||||
$output->push(new ArrayData(array(
|
|
||||||
'AbsoluteLink' => Director::absoluteURL($route),
|
|
||||||
'ChangeFrequency' => $config['frequency'],
|
|
||||||
'GooglePriority' => $config['priority']
|
|
||||||
)));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return $output;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
$instances = new DataList($class);
|
|
||||||
}
|
|
||||||
|
|
||||||
$instances = $instances->limit(
|
|
||||||
$count,
|
|
||||||
($page - 1) * $count
|
|
||||||
);
|
|
||||||
|
|
||||||
if($instances) {
|
|
||||||
foreach($instances as $obj) {
|
|
||||||
if($obj->canIncludeInGoogleSitemap()) {
|
|
||||||
$output->push($obj);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return $output;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the string frequency of edits for a particular dataobject class.
|
|
||||||
*
|
|
||||||
* Frequency for {@link SiteTree} objects can be determined from the version
|
|
||||||
* history.
|
|
||||||
*
|
|
||||||
* @param string
|
|
||||||
*
|
|
||||||
* @return string
|
|
||||||
*/
|
|
||||||
public static function get_frequency_for_class($class) {
|
|
||||||
foreach(self::$dataobjects as $type => $config) {
|
|
||||||
if($class == $type) {
|
|
||||||
return $config['frequency'];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the default priority of edits for a particular dataobject class.
|
|
||||||
*
|
|
||||||
* @param string
|
|
||||||
*
|
|
||||||
* @return float
|
|
||||||
*/
|
|
||||||
public static function get_priority_for_class($class) {
|
|
||||||
foreach(self::$dataobjects as $type => $config) {
|
|
||||||
if($class == $type) {
|
|
||||||
return $config['priority'];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0.5;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The google site map is broken down into multiple smaller files to
|
|
||||||
* prevent overbearing a server. By default separate {@link DataObject}
|
|
||||||
* records are keep in separate files and broken down into chunks.
|
|
||||||
*
|
|
||||||
* @return ArrayList
|
|
||||||
*/
|
|
||||||
public static function get_sitemaps() {
|
|
||||||
$countPerFile = Config::inst()->get('GoogleSitemap', 'objects_per_sitemap');
|
|
||||||
$sitemaps = new ArrayList();
|
|
||||||
$filter = Config::inst()->get('GoogleSitemap', 'use_show_in_search');
|
|
||||||
|
|
||||||
if(class_exists('SiteTree')) {
|
|
||||||
$filter = ($filter) ? "\"ShowInSearch\" = 1" : "";
|
|
||||||
$instances = Versioned::get_by_stage('SiteTree', 'Live', $filter);
|
|
||||||
$count = $instances->count();
|
|
||||||
|
|
||||||
$neededForPage = ceil($count / $countPerFile);
|
|
||||||
|
|
||||||
for($i = 1; $i <= $neededForPage; $i++) {
|
|
||||||
$sliced = $instances
|
|
||||||
->limit($countPerFile, ($i - 1) * $countPerFile)
|
|
||||||
->last();
|
|
||||||
|
|
||||||
$lastModified = ($sliced) ? $sliced->dbObject('LastEdited')->Format('Y-m-d') : date('Y-m-d');
|
|
||||||
|
|
||||||
$sitemaps->push(new ArrayData(array(
|
|
||||||
'ClassName' => 'SiteTree',
|
|
||||||
'LastModified' => $lastModified,
|
|
||||||
'Page' => $i
|
|
||||||
)));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if(count(self::$dataobjects) > 0) {
|
|
||||||
foreach(self::$dataobjects as $class => $config) {
|
|
||||||
$list = new DataList($class);
|
|
||||||
$list = $list->sort('LastEdited ASC');
|
|
||||||
|
|
||||||
$neededForClass = ceil($list->count() / $countPerFile);
|
|
||||||
|
|
||||||
for($i = 1; $i <= $neededForClass; $i++) {
|
|
||||||
// determine the last modified date for this slice
|
|
||||||
$sliced = $list
|
|
||||||
->limit($countPerFile, ($i - 1) * $countPerFile)
|
|
||||||
->last();
|
|
||||||
|
|
||||||
$lastModified = ($sliced) ? $sliced->dbObject('LastEdited')->Format('Y-m-d') : date('Y-m-d');
|
|
||||||
|
|
||||||
$sitemaps->push(new ArrayData(array(
|
|
||||||
'ClassName' => $class,
|
|
||||||
'Page' => $i,
|
|
||||||
'LastModified' => $lastModified
|
|
||||||
)));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if(count(self::$routes) > 0) {
|
|
||||||
$needed = ceil(count(self::$routes) / $countPerFile);
|
|
||||||
|
|
||||||
for($i = 1; $i <= $needed; $i++) {
|
|
||||||
$sitemaps->push(new ArrayData(array(
|
|
||||||
'ClassName' => 'GoogleSitemapRoute',
|
|
||||||
'Page' => $i
|
|
||||||
)));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return $sitemaps;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Notifies Google about changes to your sitemap. This behavior is disabled
|
|
||||||
* by default, to enable, read the documentation provided in the docs folder.
|
|
||||||
*
|
|
||||||
* After notifications have been enabled, every publish / unpublish of a page.
|
|
||||||
* will notify Google of the update.
|
|
||||||
*
|
|
||||||
* If the site is in development mode no ping will be sent regardless whether
|
|
||||||
* the Google notification is enabled.
|
|
||||||
*
|
|
||||||
* @return string Response text
|
|
||||||
*/
|
|
||||||
public static function ping() {
|
|
||||||
if(!self::enabled()) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Don't ping if the site has disabled it, or if the site is in dev mode
|
|
||||||
$active = Config::inst()->get('GoogleSitemap', 'google_notification_enabled');
|
|
||||||
|
|
||||||
if(!$active || Director::isDev()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
$location = urlencode(Controller::join_links(
|
|
||||||
Director::absoluteBaseURL(),
|
|
||||||
'sitemap.xml'
|
|
||||||
));
|
|
||||||
|
|
||||||
$response = self::send_ping(
|
|
||||||
"www.google.com", "/webmasters/sitemaps/ping", sprintf("sitemap=%s", $location)
|
|
||||||
);
|
|
||||||
|
|
||||||
return $response;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Send an HTTP request to the host.
|
|
||||||
*
|
|
||||||
* @return String Response text
|
|
||||||
*/
|
|
||||||
protected static function send_ping($host, $path, $query) {
|
|
||||||
$socket = fsockopen($host, 80, $errno, $error);
|
|
||||||
if (!$socket) {
|
|
||||||
return $error;
|
|
||||||
}
|
|
||||||
if ($query) {
|
|
||||||
$query = '?' . $query;
|
|
||||||
}
|
|
||||||
$request = "GET {$path}{$query} HTTP/1.1\r\nHost: $host\r\nConnection: Close\r\n\r\n";
|
|
||||||
fwrite($socket, $request);
|
|
||||||
$response = stream_get_contents($socket);
|
|
||||||
|
|
||||||
return $response;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Enable pings to google.com whenever sitemap changes.
|
|
||||||
*
|
|
||||||
* @return void
|
|
||||||
*/
|
|
||||||
public static function enable_google_notification() {
|
|
||||||
Deprecation::notice('1.1', 'GoogleSitemap::enable() is deprecated. Please use Config API instead. See documentation.');
|
|
||||||
|
|
||||||
Config::inst()->remove('GoogleSitemap', 'google_notification_enabled');
|
|
||||||
Config::inst()->update('GoogleSitemap', 'google_notification_enabled', true);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Disables pings to google when the sitemap changes.
|
|
||||||
*
|
|
||||||
* @deprecated 1.1
|
|
||||||
* @return void
|
|
||||||
*/
|
|
||||||
public static function disable_google_notification() {
|
|
||||||
Deprecation::notice('1.1', 'GoogleSitemap::enable() is deprecated. Please use Config API instead. See documentation.');
|
|
||||||
|
|
||||||
Config::inst()->remove('GoogleSitemap', 'google_notification_enabled');
|
|
||||||
Config::inst()->update('GoogleSitemap', 'google_notification_enabled', false);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Enable Google Sitemap support. Requests to the sitemap.xml route will
|
|
||||||
* result in an XML sitemap being provided.
|
|
||||||
*
|
|
||||||
* @deprecated 1.1
|
|
||||||
* @return void
|
|
||||||
*/
|
|
||||||
public static function enable() {
|
|
||||||
Deprecation::notice('1.1', 'GoogleSitemap::enable() is deprecated. Please use Config API instead. See documentation.');
|
|
||||||
|
|
||||||
Config::inst()->remove('GoogleSitemap', 'enabled');
|
|
||||||
Config::inst()->update('GoogleSitemap', 'enabled', true);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Is GoogleSitemap enabled?
|
|
||||||
*
|
|
||||||
* @return boolean
|
|
||||||
*/
|
|
||||||
public static function enabled() {
|
|
||||||
return (Config::inst()->get('GoogleSitemap', 'enabled', Config::INHERITED));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Disable Google Sitemap support. Any requests to the sitemap.xml route
|
|
||||||
* will produce a 404 response.
|
|
||||||
*
|
|
||||||
* @deprecated 1,1
|
|
||||||
* @return void
|
|
||||||
*/
|
|
||||||
public static function disable() {
|
|
||||||
Deprecation::notice('1.1', 'GoogleSitemap::disable() is deprecated. Please use Config API instead. See documentation.');
|
|
||||||
|
|
||||||
Config::inst()->remove('GoogleSitemap', 'enabled');
|
|
||||||
Config::inst()->update('GoogleSitemap', 'enabled', false);
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,73 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Controller for displaying the sitemap.xml. The module displays an index
|
|
||||||
* sitemap at the sitemap.xml level, then outputs the individual objects
|
|
||||||
* at a second level.
|
|
||||||
*
|
|
||||||
* <code>
|
|
||||||
* http://site.com/sitemap.xml/
|
|
||||||
* http://site.com/sitemap.xml/sitemap/$ClassName-$Page.xml
|
|
||||||
* </code>
|
|
||||||
*
|
|
||||||
* @package googlesitemaps
|
|
||||||
*/
|
|
||||||
class GoogleSitemapController extends Controller {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @var array
|
|
||||||
*/
|
|
||||||
private static $allowed_actions = array(
|
|
||||||
'index',
|
|
||||||
'sitemap'
|
|
||||||
);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Default controller action for the sitemap.xml file. Renders a index
|
|
||||||
* file containing a list of links to sub sitemaps containing the data.
|
|
||||||
*
|
|
||||||
* @return mixed
|
|
||||||
*/
|
|
||||||
public function index($url) {
|
|
||||||
if(GoogleSitemap::enabled()) {
|
|
||||||
Config::inst()->update('SSViewer', 'set_source_file_comments', false);
|
|
||||||
|
|
||||||
$this->getResponse()->addHeader('Content-Type', 'application/xml; charset="utf-8"');
|
|
||||||
|
|
||||||
$sitemaps = GoogleSitemap::get_sitemaps();
|
|
||||||
$this->extend('updateGoogleSitemaps', $sitemaps);
|
|
||||||
|
|
||||||
return array(
|
|
||||||
'Sitemaps' => $sitemaps
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
return new SS_HTTPResponse('Page not found', 404);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Specific controller action for displaying a particular list of links
|
|
||||||
* for a class
|
|
||||||
*
|
|
||||||
* @return mixed
|
|
||||||
*/
|
|
||||||
public function sitemap() {
|
|
||||||
$class = $this->request->param('ID');
|
|
||||||
$page = $this->request->param('OtherID');
|
|
||||||
|
|
||||||
if(GoogleSitemap::enabled() && $class && $page) {
|
|
||||||
Config::inst()->update('SSViewer', 'set_source_file_comments', false);
|
|
||||||
|
|
||||||
$this->getResponse()->addHeader('Content-Type', 'application/xml; charset="utf-8"');
|
|
||||||
|
|
||||||
$items = GoogleSitemap::get_items($class, $page);
|
|
||||||
$this->extend('updateGoogleSitemapItems', $items, $class, $page);
|
|
||||||
|
|
||||||
return array(
|
|
||||||
'Items' => $items
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
return new SS_HTTPResponse('Page not found', 404);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,116 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Decorate the page object to provide google sitemaps with
|
|
||||||
* additionally options and configuration.
|
|
||||||
*
|
|
||||||
* @package googlesitemaps
|
|
||||||
*/
|
|
||||||
class GoogleSitemapExtension extends DataExtension {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return boolean
|
|
||||||
*/
|
|
||||||
public function canIncludeInGoogleSitemap() {
|
|
||||||
$can = true;
|
|
||||||
|
|
||||||
if($this->owner->hasMethod('AbsoluteLink')) {
|
|
||||||
$hostHttp = parse_url(Director::protocolAndHost(), PHP_URL_HOST);
|
|
||||||
$objHttp = parse_url($this->owner->AbsoluteLink(), PHP_URL_HOST);
|
|
||||||
|
|
||||||
if($objHttp != $hostHttp) {
|
|
||||||
$can = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if($can) {
|
|
||||||
$can = $this->owner->canView();
|
|
||||||
}
|
|
||||||
|
|
||||||
if($can) {
|
|
||||||
$can = $this->owner->getGooglePriority();
|
|
||||||
}
|
|
||||||
|
|
||||||
$this->owner->invokeWithExtensions('alterCanIncludeInGoogleSitemap', $can);
|
|
||||||
|
|
||||||
return $can;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return void
|
|
||||||
*/
|
|
||||||
public function onAfterPublish() {
|
|
||||||
GoogleSitemap::ping();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return void
|
|
||||||
*/
|
|
||||||
public function onAfterUnpublish() {
|
|
||||||
GoogleSitemap::ping();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The default value of the priority field depends on the depth of the page in
|
|
||||||
* the site tree, so it must be calculated dynamically.
|
|
||||||
*
|
|
||||||
* @return mixed
|
|
||||||
*/
|
|
||||||
public function getGooglePriority() {
|
|
||||||
$field = $this->owner->hasField('Priority');
|
|
||||||
|
|
||||||
if(isset($this->Priority) || ($field && $this->Priority = $this->owner->getField('Priority'))) {
|
|
||||||
return ($this->Priority < 0) ? false : $this->Priority;
|
|
||||||
}
|
|
||||||
|
|
||||||
return GoogleSitemap::get_priority_for_class($this->owner->class);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns a pages change frequency calculated by pages age and number of
|
|
||||||
* versions. Google expects always, hourly, daily, weekly, monthly, yearly
|
|
||||||
* or never as values.
|
|
||||||
*
|
|
||||||
* @see http://support.google.com/webmasters/bin/answer.py?hl=en&answer=183668&topic=8476&ctx=topic
|
|
||||||
*
|
|
||||||
* @return SS_Datetime
|
|
||||||
*/
|
|
||||||
public function getChangeFrequency() {
|
|
||||||
if($freq = GoogleSitemap::get_frequency_for_class($this->owner->class)) {
|
|
||||||
return $freq;
|
|
||||||
}
|
|
||||||
|
|
||||||
$date = date('Y-m-d H:i:s');
|
|
||||||
|
|
||||||
$created = new SS_Datetime();
|
|
||||||
$created->value = ($this->owner->Created) ? $this->owner->Created : $date;
|
|
||||||
|
|
||||||
$now = new SS_Datetime();
|
|
||||||
$now->value = $date;
|
|
||||||
|
|
||||||
$versions = ($this->owner->Version) ? $this->owner->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) {
|
|
||||||
$freq = 'yearly';
|
|
||||||
} elseif ($period > 60 * 60 * 24 * 30) {
|
|
||||||
$freq = 'monthly';
|
|
||||||
} elseif ($period > 60 * 60 * 24 * 7) {
|
|
||||||
$freq = 'weekly';
|
|
||||||
} elseif ($period > 60 * 60 * 24) {
|
|
||||||
$freq = 'daily';
|
|
||||||
} elseif ($period > 60 * 60) {
|
|
||||||
$freq = 'hourly';
|
|
||||||
} else {
|
|
||||||
$freq = 'always';
|
|
||||||
}
|
|
||||||
|
|
||||||
return $freq;
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,91 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @package googlesitemaps
|
|
||||||
*/
|
|
||||||
class GoogleSitemapSiteTreeExtension extends GoogleSitemapExtension {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @var array
|
|
||||||
*/
|
|
||||||
private static $db = array(
|
|
||||||
"Priority" => "Varchar(5)"
|
|
||||||
);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param FieldList
|
|
||||||
*/
|
|
||||||
public function updateSettingsFields(&$fields) {
|
|
||||||
$prorities = array(
|
|
||||||
'-1' => _t('GoogleSitemaps.PRIORITYNOTINDEXED', "Not indexed"),
|
|
||||||
'1.0' => '1 - ' . _t('GoogleSitemaps.PRIORITYMOSTIMPORTANT', "Most important"),
|
|
||||||
'0.9' => '2',
|
|
||||||
'0.8' => '3',
|
|
||||||
'0.7' => '4',
|
|
||||||
'0.6' => '5',
|
|
||||||
'0.5' => '6',
|
|
||||||
'0.4' => '7',
|
|
||||||
'0.3' => '8',
|
|
||||||
'0.2' => '9',
|
|
||||||
'0.1' => '10 - ' . _t('GoogleSitemaps.PRIORITYLEASTIMPORTANT', "Least important")
|
|
||||||
);
|
|
||||||
|
|
||||||
$tabset = $fields->findOrMakeTab('Root.Settings');
|
|
||||||
|
|
||||||
$message = "<p>";
|
|
||||||
$message .= sprintf(_t('GoogleSitemaps.METANOTEPRIORITY', "Manually specify a Google Sitemaps priority for this page (%s)"),
|
|
||||||
'<a href="http://www.google.com/support/webmasters/bin/answer.py?hl=en&answer=71936#prioritize" target="_blank">?</a>'
|
|
||||||
);
|
|
||||||
$message .= "</p>";
|
|
||||||
|
|
||||||
$tabset->push(new Tab('GoogleSitemap', _t('GoogleSitemaps.TABGOOGLESITEMAP', 'Google Sitemap'),
|
|
||||||
new LiteralField("GoogleSitemapIntro", $message),
|
|
||||||
$priority = new DropdownField("Priority", $this->owner->fieldLabel('Priority'), $prorities, $this->owner->Priority)
|
|
||||||
));
|
|
||||||
|
|
||||||
$priority->setEmptyString(_t('GoogleSitemaps.PRIORITYAUTOSET', 'Auto-set based on page depth'));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param FieldList
|
|
||||||
*
|
|
||||||
* @return void
|
|
||||||
*/
|
|
||||||
public function updateFieldLabels(&$labels) {
|
|
||||||
parent::updateFieldLabels($labels);
|
|
||||||
|
|
||||||
$labels['Priority'] = _t('GoogleSitemaps.METAPAGEPRIO', "Page Priority");
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return boolean
|
|
||||||
*/
|
|
||||||
public function canIncludeInGoogleSitemap() {
|
|
||||||
$result = parent::canIncludeInGoogleSitemap();
|
|
||||||
$result = ($this->owner instanceof ErrorPage) ? false : $result;
|
|
||||||
|
|
||||||
return $result;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return mixed
|
|
||||||
*/
|
|
||||||
public function getGooglePriority() {
|
|
||||||
setlocale(LC_ALL, "en_US.UTF8");
|
|
||||||
$priority = $this->owner->getField('Priority');
|
|
||||||
|
|
||||||
if(!$priority) {
|
|
||||||
$parentStack = $this->owner->parentStack();
|
|
||||||
$numParents = is_array($parentStack) ? count($parentStack) - 1 : 0;
|
|
||||||
|
|
||||||
$num = max(0.1, 1.0 - ($numParents / 10));
|
|
||||||
$result = str_replace(",", ".", $num);
|
|
||||||
|
|
||||||
return $result;
|
|
||||||
} else if ($priority == -1) {
|
|
||||||
return false;
|
|
||||||
} else {
|
|
||||||
return (is_float($priority) && $priority <= 1.0) ? $priority : 0.5;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,15 +1,53 @@
|
|||||||
{
|
{
|
||||||
"name": "silverstripe/googlesitemaps",
|
"name": "wilr/silverstripe-googlesitemaps",
|
||||||
"description": "SilverStripe support for the Google Sitemaps XML system, enabling 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.",
|
"description": "SilverStripe support for the Google Sitemaps XML, enabling 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 quickly.",
|
||||||
"type": "silverstripe-module",
|
"type": "silverstripe-vendormodule",
|
||||||
"keywords": ["silverstripe", "googlesitemaps", "seo"],
|
"keywords": [
|
||||||
"homepage": "https://github.com/silverstripe-labs/silverstripe-googlesitemaps",
|
"silverstripe",
|
||||||
"license": "BSD",
|
"googlesitemaps",
|
||||||
"authors": [{
|
"seo"
|
||||||
|
],
|
||||||
|
"homepage": "https://github.com/wilr/silverstripe-googlesitemaps",
|
||||||
|
"license": "BSD-3-Clause",
|
||||||
|
"authors": [
|
||||||
|
{
|
||||||
"name": "Will Rossiter",
|
"name": "Will Rossiter",
|
||||||
"email": "will@fullscreen.io"
|
"email": "will@fullscreen.io"
|
||||||
}],
|
}
|
||||||
|
],
|
||||||
"require": {
|
"require": {
|
||||||
"silverstripe/framework": "~3.1"
|
"php": "^8.1",
|
||||||
|
"silverstripe/framework": "^5"
|
||||||
|
},
|
||||||
|
"require-dev": {
|
||||||
|
"phpunit/phpunit": "^9.5",
|
||||||
|
"squizlabs/php_codesniffer": "^3"
|
||||||
|
},
|
||||||
|
"replace": {
|
||||||
|
"silverstripe/googlesitemaps": "*"
|
||||||
|
},
|
||||||
|
"extra": {
|
||||||
|
"branch-alias": {
|
||||||
|
"dev-main": "3.x-dev"
|
||||||
|
},
|
||||||
|
"expose": [
|
||||||
|
"images",
|
||||||
|
"css"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"autoload": {
|
||||||
|
"psr-4": {
|
||||||
|
"Wilr\\GoogleSitemaps\\": "src/",
|
||||||
|
"Wilr\\GoogleSitemaps\\Tests\\": "tests/"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"prefer-stable": true,
|
||||||
|
"minimum-stability": "dev",
|
||||||
|
"config": {
|
||||||
|
"allow-plugins": {
|
||||||
|
"composer/installers": true,
|
||||||
|
"silverstripe/vendor-plugin": true,
|
||||||
|
"silverstripe/recipe-plugin": true
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
252
css/style.css
252
css/style.css
@ -1,131 +1,145 @@
|
|||||||
|
:root {
|
||||||
|
--color-black: #292B2E;
|
||||||
|
--color-blue-bright: #005AE1;
|
||||||
|
--color-blue-dark: #003E94;
|
||||||
|
--color-blue-light: #E6EDF4;
|
||||||
|
--color-grey-dark: #595D64;
|
||||||
|
--color-grey-light: #f7f8f8;
|
||||||
|
--color-grey-medium: #DCDEE0;
|
||||||
|
--color-white: #fff;
|
||||||
|
}
|
||||||
|
|
||||||
* {
|
* {
|
||||||
margin: 0;
|
margin: 0;
|
||||||
padding: 0;
|
padding: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
html {
|
||||||
|
font-size: 10px;
|
||||||
|
-moz-osx-font-smoothing: grayscale;
|
||||||
|
-webkit-font-smoothing: antialiased;
|
||||||
|
}
|
||||||
|
|
||||||
|
body, table {
|
||||||
|
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
|
||||||
|
font-size: 1.4rem;
|
||||||
|
font-weight: 400;
|
||||||
|
line-height: 1.4;
|
||||||
|
}
|
||||||
|
|
||||||
body {
|
body {
|
||||||
font-family: Helvetica, Arial, sans-serif;
|
background: var(--color-white);
|
||||||
font-size: 14px;
|
color: var(--color-grey-dark);
|
||||||
color: #545353;
|
|
||||||
color: #005A92;
|
|
||||||
background: #B0BEC7;
|
|
||||||
padding: 20px;
|
|
||||||
}
|
}
|
||||||
#content {
|
|
||||||
margin: 0 auto;
|
table {
|
||||||
max-width: 1200px;
|
border-collapse: collapse;
|
||||||
background: #fff;
|
color: var(--color-black);
|
||||||
padding: 20px 30px;
|
text-align: left;
|
||||||
-webkit-box-shadow: 2px 2px 5px 1px rgba(0, 0, 0, 0.2);
|
width: 100%;
|
||||||
box-shadow: 2px 2px 5px 1px rgba(0, 0, 0, 0.2);
|
|
||||||
-webkit-border-radius: 5px;
|
|
||||||
border-radius: 5px;
|
|
||||||
}
|
}
|
||||||
h1{
|
|
||||||
font-size: 20px;
|
a {
|
||||||
line-height: 24px;
|
|
||||||
font-weight: bold;
|
|
||||||
color: #1556B2;
|
|
||||||
color: #005A92;
|
|
||||||
/* text-shadow: 1px 1px 1px rgba(0,0,0,0.7);
|
|
||||||
filter: dropshadow(color=#000, offx=2, offy=2);*/
|
|
||||||
padding-left: 31px;
|
|
||||||
background: url("../images/logo_small.png") transparent left top no-repeat scroll;
|
|
||||||
margin: 0 0 20px 0;
|
|
||||||
display: inline-block;
|
|
||||||
}
|
|
||||||
h1 a{
|
|
||||||
font: inherit;
|
|
||||||
color: inherit;
|
color: inherit;
|
||||||
text-decoration: none;
|
text-decoration: none;
|
||||||
}
|
}
|
||||||
h1 .ss_link{
|
|
||||||
visibility: hidden;
|
.content {
|
||||||
font-size: 9px;
|
margin: 0 auto;
|
||||||
|
max-width: 122rem;
|
||||||
|
padding: 2rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.content__title {
|
||||||
|
align-items: center;
|
||||||
|
color: var(--color-black);
|
||||||
|
display: inline-flex;
|
||||||
|
font-size: 2rem;
|
||||||
|
font-weight: 600;
|
||||||
|
margin: 0 0 3rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.content__title::before {
|
||||||
|
content: "";
|
||||||
|
background: url("../images/cms_logo.svg") left center no-repeat;
|
||||||
|
border-right: 0.1rem solid var(--color-grey-medium);
|
||||||
|
display: inline-block;
|
||||||
|
height: 3.2rem;
|
||||||
|
margin-right: 1.5rem;
|
||||||
|
padding-right: 1.5rem;
|
||||||
|
width: 3.2rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.content__text {
|
||||||
|
margin: 1rem 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.content__text a {
|
||||||
|
color: var(--color-blue-bright);
|
||||||
|
text-decoration: underline;
|
||||||
|
}
|
||||||
|
|
||||||
|
.content__text a:hover {
|
||||||
|
color: var(--color-blue-dark);
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.table-wrapper {
|
||||||
|
border-top: 0.1rem solid var(--color-grey-medium);
|
||||||
|
margin: 3rem 0 5rem;
|
||||||
|
overflow-x: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.table__cell {
|
||||||
|
padding: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.table__cell--w-10 {
|
||||||
|
width: 10%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.table__cell--w-15 {
|
||||||
|
width: 15%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.table__cell--w-65 {
|
||||||
|
width: 65%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.table__cell--w-85 {
|
||||||
|
width: 85%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.table tbody tr:nth-child(odd) td {
|
||||||
|
background-color: var(--color-grey-light);
|
||||||
|
}
|
||||||
|
|
||||||
|
.table tbody tr:hover td {
|
||||||
|
background-color: var(--color-blue-light);
|
||||||
|
}
|
||||||
|
|
||||||
|
.table a:hover {
|
||||||
|
text-decoration: underline;
|
||||||
|
}
|
||||||
|
|
||||||
|
.image-table__cell {
|
||||||
|
padding: 1rem 1rem 0 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.image-table__cell--image {
|
||||||
|
width: 6rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.image-table__cell--text {
|
||||||
|
width: calc(100% - 6rem);
|
||||||
|
}
|
||||||
|
|
||||||
|
.image-table__image {
|
||||||
|
height: 4rem;
|
||||||
|
object-fit: cover;
|
||||||
|
width: 6rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.image-table__title {
|
||||||
display: block;
|
display: block;
|
||||||
text-align: right;
|
|
||||||
margin-top: -5px;
|
|
||||||
}
|
|
||||||
h1:hover .ss_link{
|
|
||||||
visibility: visible;
|
|
||||||
text-decoration: underline;
|
|
||||||
|
|
||||||
}
|
|
||||||
.expl {
|
|
||||||
margin: 10px 3px;
|
|
||||||
line-height: 1.3em;
|
|
||||||
}
|
|
||||||
.expl a {
|
|
||||||
color: #1556B2;
|
|
||||||
font-weight: bold;
|
|
||||||
text-decoration: none;
|
|
||||||
}
|
|
||||||
.expl a:hover{
|
|
||||||
text-decoration: underline;
|
|
||||||
}
|
|
||||||
|
|
||||||
table.tablesorter {
|
|
||||||
background-color: #CDCDCD;
|
|
||||||
margin:20px 0pt 15px;
|
|
||||||
font-size: 8pt;
|
|
||||||
width: 100%;
|
|
||||||
text-align: left;
|
|
||||||
border: none;
|
|
||||||
border-collapse: collapse;
|
|
||||||
border-bottom: 1px solid #005A92;
|
|
||||||
}
|
|
||||||
table.tablesorter thead tr th, table.tablesorter tfoot tr th {
|
|
||||||
/*background-color: #e6EEEE;*/
|
|
||||||
background-color: #F5FAFA;
|
|
||||||
font-size: 8pt;
|
|
||||||
padding: 4px 20px 4px 10px;
|
|
||||||
}
|
|
||||||
table.tablesorter thead tr .headerSortDown, table.tablesorter thead tr .headerSortUp {
|
|
||||||
background-color: #B0BEC7;
|
|
||||||
background-color: #e6EEEE;
|
|
||||||
}
|
|
||||||
|
|
||||||
table.tablesorter thead tr .header {
|
|
||||||
background-image: url("../images/bg.gif");
|
|
||||||
background-repeat: no-repeat;
|
|
||||||
background-position: center right;
|
|
||||||
cursor: pointer;
|
|
||||||
border-bottom: 1px solid #005A92;
|
|
||||||
}
|
|
||||||
table.tablesorter thead tr .headerSortUp {
|
|
||||||
background-image: url("../images/asc.gif");
|
|
||||||
}
|
|
||||||
table.tablesorter thead tr .headerSortDown {
|
|
||||||
background-image: url("../images/desc.gif");
|
|
||||||
}
|
|
||||||
|
|
||||||
table.tablesorter tbody td {
|
|
||||||
color: #005A92;
|
|
||||||
padding: 4px;
|
|
||||||
|
|
||||||
vertical-align: top;
|
|
||||||
}
|
|
||||||
table.tablesorter tbody tr{
|
|
||||||
background-color: #FFF;
|
|
||||||
}
|
|
||||||
table.tablesorter tbody tr.odd {
|
|
||||||
background-color:#EFF2F3;
|
|
||||||
}
|
|
||||||
table.tablesorter tbody tr:hover {
|
|
||||||
background-color: #D8E1E8;
|
|
||||||
}
|
|
||||||
table.tablesorter tbody a{
|
|
||||||
color: #444;color: #005A92;
|
|
||||||
text-decoration: none;
|
|
||||||
}
|
|
||||||
table.tablesorter tbody a:hover{
|
|
||||||
text-decoration: underline;
|
|
||||||
}
|
|
||||||
table.tablesorter tbody tr:hover td,
|
|
||||||
table.tablesorter tbody tr:hover a{
|
|
||||||
color: #000;
|
|
||||||
}
|
|
||||||
#Footer{
|
|
||||||
margin: 50px 0 10px;
|
|
||||||
text-align: right;
|
|
||||||
font-size: 0.8em;
|
|
||||||
}
|
}
|
||||||
|
@ -1,23 +1,23 @@
|
|||||||
# Google Sitemaps Module
|
# Google Sitemaps Module
|
||||||
|
|
||||||
SilverStripe provides support for the Google Sitemaps XML system, enabling
|
Silverstripe CMS 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 pages on your site. This helps
|
||||||
your SilverStripe website rank well in search engines, and to encourage the
|
your Silverstripe CMS website rank well in search engines, and to encourage the
|
||||||
information on your site to be discovered by Google quickly.
|
information on your site to be discovered by Google quickly.
|
||||||
|
|
||||||
Therefore, all Silverstripe websites contain a special controller which can be
|
Therefore, all Silverstripe CMS websites contain a special controller which can
|
||||||
visited: http://yoursite.com/sitemap.xml. This is not a file directly, but
|
be visited: http://yoursite.com/sitemap.xml. This is not a file directly, but
|
||||||
rather a custom route which points to the GoogleSitemap controller.
|
rather a custom route which points to the GoogleSitemap controller.
|
||||||
|
|
||||||
See http://en.wikipedia.org/wiki/Sitemaps for info on the Google Sitemap
|
See http://en.wikipedia.org/wiki/Sitemaps for info on the Google Sitemap
|
||||||
format.
|
format.
|
||||||
|
|
||||||
Whenever you publish a new or republish an existing page, SilverStripe can
|
Whenever you publish a new or republish an existing page, Silverstripe CMS can
|
||||||
automatically inform Google of the change, encouraging a Google to take notice.
|
automatically inform Google of the change, encouraging a Google to take notice.
|
||||||
If you install the SilverStripe Google Analytics module, you can see if Google
|
If you install the Silverstripe CMS Google Analytics module, you can see if Google
|
||||||
has updated your page as a result.
|
has updated your page as a result.
|
||||||
|
|
||||||
By default, SilverStripe informs Google that the importance of a page depends
|
By default, Silverstripe CMS informs Google that the importance of a page depends
|
||||||
on its position of in the sitemap. "Top level" pages are most important, and
|
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,
|
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).
|
Importance drops from 1.0, to 0.9, to 0.8, and so on, until 0.1 is reached).
|
||||||
@ -27,33 +27,32 @@ manually, including requesting to have the page excluded from the sitemap.
|
|||||||
|
|
||||||
## Configuration
|
## Configuration
|
||||||
|
|
||||||
Most module configuration is done via the SilverStripe Config API. Create a new
|
Most module configuration is done via the Silverstripe CMS Config API. Create a
|
||||||
config file `mysite/_config/googlesitemaps.yml` with the following outline:
|
new config file `mysite/_config/googlesitemaps.yml` with the following outline:
|
||||||
|
|
||||||
|
```yml
|
||||||
---
|
---
|
||||||
Name: customgooglesitemaps
|
Name: customgooglesitemaps
|
||||||
After: googlesitemaps
|
After: googlesitemaps
|
||||||
---
|
---
|
||||||
GoogleSitemap:
|
Wilr\GoogleSitemaps\GoogleSitemap:
|
||||||
enabled: true
|
enabled: true
|
||||||
objects_per_sitemap: 1000
|
objects_per_sitemap: 1000
|
||||||
google_notification_enabled: false
|
|
||||||
use_show_in_search: true
|
use_show_in_search: true
|
||||||
|
```
|
||||||
|
|
||||||
You can now alter any of those properties to set your needs. A popular option
|
You can now alter any of those properties to set your needs.
|
||||||
is to turn on automatic pinging so that Google is notified of any updates to
|
|
||||||
your page. You can set this in the file we created in the last paragraph by
|
|
||||||
editing the `google_notification_enabled` option to true
|
|
||||||
|
|
||||||
|
```yml
|
||||||
---
|
---
|
||||||
Name: customgooglesitemaps
|
Name: customgooglesitemaps
|
||||||
After: googlesitemaps
|
After: googlesitemaps
|
||||||
---
|
---
|
||||||
GoogleSitemap:
|
Wilr\GoogleSitemaps\GoogleSitemap:
|
||||||
enabled: true
|
enabled: true
|
||||||
objects_per_sitemap: 1000
|
objects_per_sitemap: 1000
|
||||||
google_notification_enabled: true
|
|
||||||
use_show_in_search: true
|
use_show_in_search: true
|
||||||
|
```
|
||||||
|
|
||||||
### Including DataObjects
|
### Including DataObjects
|
||||||
|
|
||||||
@ -64,8 +63,8 @@ database as DataObject subclasses.
|
|||||||
To include a DataObject instance in the Sitemap it requires that your subclass
|
To include a DataObject instance in the Sitemap it requires that your subclass
|
||||||
defines two functions:
|
defines two functions:
|
||||||
|
|
||||||
* AbsoluteLink() function which returns the URL for this DataObject
|
- AbsoluteLink() function which returns the URL for this DataObject
|
||||||
* canView() function which returns a boolean value.
|
- canView() function which returns a boolean value.
|
||||||
|
|
||||||
The following is a barebones example of a DataObject called 'MyDataObject'. It
|
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
|
assumes that you have a controller called 'MyController' which has a show method
|
||||||
@ -73,6 +72,9 @@ to show the DataObject by its ID.
|
|||||||
|
|
||||||
<?php
|
<?php
|
||||||
|
|
||||||
|
use SilverStripe\ORM\DataObject;
|
||||||
|
use SilverStripe\Control\Director;
|
||||||
|
|
||||||
class MyDataObject extends DataObject {
|
class MyDataObject extends DataObject {
|
||||||
|
|
||||||
function canView($member = null) {
|
function canView($member = null) {
|
||||||
@ -88,10 +90,11 @@ to show the DataObject by its ID.
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
After those methods have been defined on your DataObject you now need to tell
|
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
|
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;
|
||||||
|
|
||||||
GoogleSitemap::register_dataobject('MyDataObject');
|
GoogleSitemap::register_dataobject('MyDataObject');
|
||||||
|
|
||||||
@ -99,6 +102,8 @@ 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_dataobject(), So
|
frequency (daily, weekly, monthly) as a second parameter to register_dataobject(), So
|
||||||
instead of the previous code you would write:
|
instead of the previous code you would write:
|
||||||
|
|
||||||
|
use Wilr\GoogleSitemaps\GoogleSitemap;
|
||||||
|
|
||||||
GoogleSitemap::register_dataobject('MyDataObject', 'daily');
|
GoogleSitemap::register_dataobject('MyDataObject', 'daily');
|
||||||
|
|
||||||
See the following blog post for more information:
|
See the following blog post for more information:
|
||||||
@ -107,13 +112,33 @@ http://www.silvercart.org/blog/dataobjects-and-googlesitemaps/
|
|||||||
|
|
||||||
### Including custom routes
|
### Including custom routes
|
||||||
|
|
||||||
Occasionally you may have a need to include custom url's in your sitemap for
|
Occasionally you may have a need to include custom URLs in your sitemap for
|
||||||
your Controllers and other pages which don't exist in the database. To update
|
your Controllers and other pages which don't exist in the database. To update
|
||||||
the sitemap to include those links call register_routes() with your array of
|
the sitemap to include those links call register_routes() with your array of
|
||||||
urls to include.
|
URLs to include.
|
||||||
|
|
||||||
|
use Wilr\GoogleSitemaps\GoogleSitemap;
|
||||||
|
|
||||||
GoogleSitemap::register_routes(array(
|
GoogleSitemap::register_routes(array(
|
||||||
'/my-custom-controller/',
|
'/my-custom-controller/',
|
||||||
'/Security/',
|
'/Security/',
|
||||||
'/Security/login/'
|
'/Security/login/'
|
||||||
));
|
));
|
||||||
|
|
||||||
|
### Sitemapable
|
||||||
|
|
||||||
|
For automatic registration of a DataObject subclass, implement the `Sitemapable`
|
||||||
|
extension.
|
||||||
|
|
||||||
|
```
|
||||||
|
<?php
|
||||||
|
|
||||||
|
|
||||||
|
class MyDataObject extends DataObject implements Sitemapable
|
||||||
|
{
|
||||||
|
public function AbsoluteLink()
|
||||||
|
{
|
||||||
|
// ..
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
BIN
images/asc.gif
BIN
images/asc.gif
Binary file not shown.
Before Width: | Height: | Size: 54 B |
BIN
images/bg.gif
BIN
images/bg.gif
Binary file not shown.
Before Width: | Height: | Size: 64 B |
4
images/cms_logo.svg
Normal file
4
images/cms_logo.svg
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
<svg width="32" height="19" viewBox="0 0 32 19" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path d="M15.832 1.85439L9.09495 6.56978C8.16307 7.22084 7.93658 8.50206 8.58764 9.43478C9.2387 10.3667 10.5199 10.5931 11.4526 9.94209L18.1897 5.2267C20.9787 3.27352 24.8307 3.95216 26.7772 6.74111C28.7304 9.53005 28.0518 13.3821 25.2628 15.3286L23.145 16.8087C24.8993 19.7899 28.8675 18.5354 30.6702 15.3353C32.5549 11.9905 32.4863 7.72146 30.1428 4.37673C26.8926 -0.263432 20.4714 -1.39422 15.832 1.85439Z" fill="#005AE1"/>
|
||||||
|
<path d="M16.1671 16.6776L22.9042 11.9622C23.836 11.3112 24.0625 10.03 23.4115 9.09724C22.7604 8.16536 21.4792 7.93887 20.5465 8.58993L13.8094 13.3053C11.0204 15.2585 7.1684 14.5799 5.22191 11.7909C3.26873 9.00196 3.94737 5.14992 6.73631 3.20343L8.85414 1.72329C7.1007 -1.25705 3.1325 -0.00256691 1.32975 3.19758C-0.554895 6.54231 -0.486362 10.8114 1.85712 14.1561C5.10574 18.7955 11.5269 19.9338 16.1671 16.6785V16.6776Z" fill="#005AE1"/>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 970 B |
BIN
images/desc.gif
BIN
images/desc.gif
Binary file not shown.
Before Width: | Height: | Size: 54 B |
Binary file not shown.
Before Width: | Height: | Size: 2.1 KiB |
4
javascript/jquery.tablesorter.min.js
vendored
4
javascript/jquery.tablesorter.min.js
vendored
@ -1,4 +0,0 @@
|
|||||||
|
|
||||||
(function($){$.extend({tablesorter:new
|
|
||||||
function(){var parsers=[],widgets=[];this.defaults={cssHeader:"header",cssAsc:"headerSortUp",cssDesc:"headerSortDown",cssChildRow:"expand-child",sortInitialOrder:"asc",sortMultiSortKey:"shiftKey",sortForce:null,sortAppend:null,sortLocaleCompare:true,textExtraction:"simple",parsers:{},widgets:[],widgetZebra:{css:["even","odd"]},headers:{},widthFixed:false,cancelSelection:true,sortList:[],headerList:[],dateFormat:"us",decimal:'/\.|\,/g',onRenderHeader:null,selectorHeaders:'thead th',debug:false};function benchmark(s,d){log(s+","+(new Date().getTime()-d.getTime())+"ms");}this.benchmark=benchmark;function log(s){if(typeof console!="undefined"&&typeof console.debug!="undefined"){console.log(s);}else{alert(s);}}function buildParserCache(table,$headers){if(table.config.debug){var parsersDebug="";}if(table.tBodies.length==0)return;var rows=table.tBodies[0].rows;if(rows[0]){var list=[],cells=rows[0].cells,l=cells.length;for(var i=0;i<l;i++){var p=false;if($.metadata&&($($headers[i]).metadata()&&$($headers[i]).metadata().sorter)){p=getParserById($($headers[i]).metadata().sorter);}else if((table.config.headers[i]&&table.config.headers[i].sorter)){p=getParserById(table.config.headers[i].sorter);}if(!p){p=detectParserForColumn(table,rows,-1,i);}if(table.config.debug){parsersDebug+="column:"+i+" parser:"+p.id+"\n";}list.push(p);}}if(table.config.debug){log(parsersDebug);}return list;};function detectParserForColumn(table,rows,rowIndex,cellIndex){var l=parsers.length,node=false,nodeValue=false,keepLooking=true;while(nodeValue==''&&keepLooking){rowIndex++;if(rows[rowIndex]){node=getNodeFromRowAndCellIndex(rows,rowIndex,cellIndex);nodeValue=trimAndGetNodeText(table.config,node);if(table.config.debug){log('Checking if value was empty on row:'+rowIndex);}}else{keepLooking=false;}}for(var i=1;i<l;i++){if(parsers[i].is(nodeValue,table,node)){return parsers[i];}}return parsers[0];}function getNodeFromRowAndCellIndex(rows,rowIndex,cellIndex){return rows[rowIndex].cells[cellIndex];}function trimAndGetNodeText(config,node){return $.trim(getElementText(config,node));}function getParserById(name){var l=parsers.length;for(var i=0;i<l;i++){if(parsers[i].id.toLowerCase()==name.toLowerCase()){return parsers[i];}}return false;}function buildCache(table){if(table.config.debug){var cacheTime=new Date();}var totalRows=(table.tBodies[0]&&table.tBodies[0].rows.length)||0,totalCells=(table.tBodies[0].rows[0]&&table.tBodies[0].rows[0].cells.length)||0,parsers=table.config.parsers,cache={row:[],normalized:[]};for(var i=0;i<totalRows;++i){var c=$(table.tBodies[0].rows[i]),cols=[];if(c.hasClass(table.config.cssChildRow)){cache.row[cache.row.length-1]=cache.row[cache.row.length-1].add(c);continue;}cache.row.push(c);for(var j=0;j<totalCells;++j){cols.push(parsers[j].format(getElementText(table.config,c[0].cells[j]),table,c[0].cells[j]));}cols.push(cache.normalized.length);cache.normalized.push(cols);cols=null;};if(table.config.debug){benchmark("Building cache for "+totalRows+" rows:",cacheTime);}return cache;};function getElementText(config,node){var text="";if(!node)return"";if(!config.supportsTextContent)config.supportsTextContent=node.textContent||false;if(config.textExtraction=="simple"){if(config.supportsTextContent){text=node.textContent;}else{if(node.childNodes[0]&&node.childNodes[0].hasChildNodes()){text=node.childNodes[0].innerHTML;}else{text=node.innerHTML;}}}else{if(typeof(config.textExtraction)=="function"){text=config.textExtraction(node);}else{text=$(node).text();}}return text;}function appendToTable(table,cache){if(table.config.debug){var appendTime=new Date()}var c=cache,r=c.row,n=c.normalized,totalRows=n.length,checkCell=(n[0].length-1),tableBody=$(table.tBodies[0]),rows=[];for(var i=0;i<totalRows;i++){var pos=n[i][checkCell];rows.push(r[pos]);if(!table.config.appender){var l=r[pos].length;for(var j=0;j<l;j++){tableBody[0].appendChild(r[pos][j]);}}}if(table.config.appender){table.config.appender(table,rows);}rows=null;if(table.config.debug){benchmark("Rebuilt table:",appendTime);}applyWidget(table);setTimeout(function(){$(table).trigger("sortEnd");},0);};function buildHeaders(table){if(table.config.debug){var time=new Date();}var meta=($.metadata)?true:false;var header_index=computeTableHeaderCellIndexes(table);$tableHeaders=$(table.config.selectorHeaders,table).each(function(index){this.column=header_index[this.parentNode.rowIndex+"-"+this.cellIndex];this.order=formatSortingOrder(table.config.sortInitialOrder);this.count=this.order;if(checkHeaderMetadata(this)||checkHeaderOptions(table,index))this.sortDisabled=true;if(checkHeaderOptionsSortingLocked(table,index))this.order=this.lockedOrder=checkHeaderOptionsSortingLocked(table,index);if(!this.sortDisabled){var $th=$(this).addClass(table.config.cssHeader);if(table.config.onRenderHeader)table.config.onRenderHeader.apply($th);}table.config.headerList[index]=this;});if(table.config.debug){benchmark("Built headers:",time);log($tableHeaders);}return $tableHeaders;};function computeTableHeaderCellIndexes(t){var matrix=[];var lookup={};var thead=t.getElementsByTagName('THEAD')[0];var trs=thead.getElementsByTagName('TR');for(var i=0;i<trs.length;i++){var cells=trs[i].cells;for(var j=0;j<cells.length;j++){var c=cells[j];var rowIndex=c.parentNode.rowIndex;var cellId=rowIndex+"-"+c.cellIndex;var rowSpan=c.rowSpan||1;var colSpan=c.colSpan||1
|
|
||||||
var firstAvailCol;if(typeof(matrix[rowIndex])=="undefined"){matrix[rowIndex]=[];}for(var k=0;k<matrix[rowIndex].length+1;k++){if(typeof(matrix[rowIndex][k])=="undefined"){firstAvailCol=k;break;}}lookup[cellId]=firstAvailCol;for(var k=rowIndex;k<rowIndex+rowSpan;k++){if(typeof(matrix[k])=="undefined"){matrix[k]=[];}var matrixrow=matrix[k];for(var l=firstAvailCol;l<firstAvailCol+colSpan;l++){matrixrow[l]="x";}}}}return lookup;}function checkCellColSpan(table,rows,row){var arr=[],r=table.tHead.rows,c=r[row].cells;for(var i=0;i<c.length;i++){var cell=c[i];if(cell.colSpan>1){arr=arr.concat(checkCellColSpan(table,headerArr,row++));}else{if(table.tHead.length==1||(cell.rowSpan>1||!r[row+1])){arr.push(cell);}}}return arr;};function checkHeaderMetadata(cell){if(($.metadata)&&($(cell).metadata().sorter===false)){return true;};return false;}function checkHeaderOptions(table,i){if((table.config.headers[i])&&(table.config.headers[i].sorter===false)){return true;};return false;}function checkHeaderOptionsSortingLocked(table,i){if((table.config.headers[i])&&(table.config.headers[i].lockedOrder))return table.config.headers[i].lockedOrder;return false;}function applyWidget(table){var c=table.config.widgets;var l=c.length;for(var i=0;i<l;i++){getWidgetById(c[i]).format(table);}}function getWidgetById(name){var l=widgets.length;for(var i=0;i<l;i++){if(widgets[i].id.toLowerCase()==name.toLowerCase()){return widgets[i];}}};function formatSortingOrder(v){if(typeof(v)!="Number"){return(v.toLowerCase()=="desc")?1:0;}else{return(v==1)?1:0;}}function isValueInArray(v,a){var l=a.length;for(var i=0;i<l;i++){if(a[i][0]==v){return true;}}return false;}function setHeadersCss(table,$headers,list,css){$headers.removeClass(css[0]).removeClass(css[1]);var h=[];$headers.each(function(offset){if(!this.sortDisabled){h[this.column]=$(this);}});var l=list.length;for(var i=0;i<l;i++){h[list[i][0]].addClass(css[list[i][1]]);}}function fixColumnWidth(table,$headers){var c=table.config;if(c.widthFixed){var colgroup=$('<colgroup>');$("tr:first td",table.tBodies[0]).each(function(){colgroup.append($('<col>').css('width',$(this).width()));});$(table).prepend(colgroup);};}function updateHeaderSortCount(table,sortList){var c=table.config,l=sortList.length;for(var i=0;i<l;i++){var s=sortList[i],o=c.headerList[s[0]];o.count=s[1];o.count++;}}function multisort(table,sortList,cache){if(table.config.debug){var sortTime=new Date();}var dynamicExp="var sortWrapper = function(a,b) {",l=sortList.length;for(var i=0;i<l;i++){var c=sortList[i][0];var order=sortList[i][1];var s=(table.config.parsers[c].type=="text")?((order==0)?makeSortFunction("text","asc",c):makeSortFunction("text","desc",c)):((order==0)?makeSortFunction("numeric","asc",c):makeSortFunction("numeric","desc",c));var e="e"+i;dynamicExp+="var "+e+" = "+s;dynamicExp+="if("+e+") { return "+e+"; } ";dynamicExp+="else { ";}var orgOrderCol=cache.normalized[0].length-1;dynamicExp+="return a["+orgOrderCol+"]-b["+orgOrderCol+"];";for(var i=0;i<l;i++){dynamicExp+="}; ";}dynamicExp+="return 0; ";dynamicExp+="}; ";if(table.config.debug){benchmark("Evaling expression:"+dynamicExp,new Date());}eval(dynamicExp);cache.normalized.sort(sortWrapper);if(table.config.debug){benchmark("Sorting on "+sortList.toString()+" and dir "+order+" time:",sortTime);}return cache;};function makeSortFunction(type,direction,index){var a="a["+index+"]",b="b["+index+"]";if(type=='text'&&direction=='asc'){return"("+a+" == "+b+" ? 0 : ("+a+" === null ? Number.POSITIVE_INFINITY : ("+b+" === null ? Number.NEGATIVE_INFINITY : ("+a+" < "+b+") ? -1 : 1 )));";}else if(type=='text'&&direction=='desc'){return"("+a+" == "+b+" ? 0 : ("+a+" === null ? Number.POSITIVE_INFINITY : ("+b+" === null ? Number.NEGATIVE_INFINITY : ("+b+" < "+a+") ? -1 : 1 )));";}else if(type=='numeric'&&direction=='asc'){return"("+a+" === null && "+b+" === null) ? 0 :("+a+" === null ? Number.POSITIVE_INFINITY : ("+b+" === null ? Number.NEGATIVE_INFINITY : "+a+" - "+b+"));";}else if(type=='numeric'&&direction=='desc'){return"("+a+" === null && "+b+" === null) ? 0 :("+a+" === null ? Number.POSITIVE_INFINITY : ("+b+" === null ? Number.NEGATIVE_INFINITY : "+b+" - "+a+"));";}};function makeSortText(i){return"((a["+i+"] < b["+i+"]) ? -1 : ((a["+i+"] > b["+i+"]) ? 1 : 0));";};function makeSortTextDesc(i){return"((b["+i+"] < a["+i+"]) ? -1 : ((b["+i+"] > a["+i+"]) ? 1 : 0));";};function makeSortNumeric(i){return"a["+i+"]-b["+i+"];";};function makeSortNumericDesc(i){return"b["+i+"]-a["+i+"];";};function sortText(a,b){if(table.config.sortLocaleCompare)return a.localeCompare(b);return((a<b)?-1:((a>b)?1:0));};function sortTextDesc(a,b){if(table.config.sortLocaleCompare)return b.localeCompare(a);return((b<a)?-1:((b>a)?1:0));};function sortNumeric(a,b){return a-b;};function sortNumericDesc(a,b){return b-a;};function getCachedSortType(parsers,i){return parsers[i].type;};this.construct=function(settings){return this.each(function(){if(!this.tHead||!this.tBodies)return;var $this,$document,$headers,cache,config,shiftDown=0,sortOrder;this.config={};config=$.extend(this.config,$.tablesorter.defaults,settings);$this=$(this);$.data(this,"tablesorter",config);$headers=buildHeaders(this);this.config.parsers=buildParserCache(this,$headers);cache=buildCache(this);var sortCSS=[config.cssDesc,config.cssAsc];fixColumnWidth(this);$headers.click(function(e){var totalRows=($this[0].tBodies[0]&&$this[0].tBodies[0].rows.length)||0;if(!this.sortDisabled&&totalRows>0){$this.trigger("sortStart");var $cell=$(this);var i=this.column;this.order=this.count++%2;if(this.lockedOrder)this.order=this.lockedOrder;if(!e[config.sortMultiSortKey]){config.sortList=[];if(config.sortForce!=null){var a=config.sortForce;for(var j=0;j<a.length;j++){if(a[j][0]!=i){config.sortList.push(a[j]);}}}config.sortList.push([i,this.order]);}else{if(isValueInArray(i,config.sortList)){for(var j=0;j<config.sortList.length;j++){var s=config.sortList[j],o=config.headerList[s[0]];if(s[0]==i){o.count=s[1];o.count++;s[1]=o.count%2;}}}else{config.sortList.push([i,this.order]);}};setTimeout(function(){setHeadersCss($this[0],$headers,config.sortList,sortCSS);appendToTable($this[0],multisort($this[0],config.sortList,cache));},1);return false;}}).mousedown(function(){if(config.cancelSelection){this.onselectstart=function(){return false};return false;}});$this.bind("update",function(){var me=this;setTimeout(function(){me.config.parsers=buildParserCache(me,$headers);cache=buildCache(me);},1);}).bind("updateCell",function(e,cell){var config=this.config;var pos=[(cell.parentNode.rowIndex-1),cell.cellIndex];cache.normalized[pos[0]][pos[1]]=config.parsers[pos[1]].format(getElementText(config,cell),cell);}).bind("sorton",function(e,list){$(this).trigger("sortStart");config.sortList=list;var sortList=config.sortList;updateHeaderSortCount(this,sortList);setHeadersCss(this,$headers,sortList,sortCSS);appendToTable(this,multisort(this,sortList,cache));}).bind("appendCache",function(){appendToTable(this,cache);}).bind("applyWidgetId",function(e,id){getWidgetById(id).format(this);}).bind("applyWidgets",function(){applyWidget(this);});if($.metadata&&($(this).metadata()&&$(this).metadata().sortlist)){config.sortList=$(this).metadata().sortlist;}if(config.sortList.length>0){$this.trigger("sorton",[config.sortList]);}applyWidget(this);});};this.addParser=function(parser){var l=parsers.length,a=true;for(var i=0;i<l;i++){if(parsers[i].id.toLowerCase()==parser.id.toLowerCase()){a=false;}}if(a){parsers.push(parser);};};this.addWidget=function(widget){widgets.push(widget);};this.formatFloat=function(s){var i=parseFloat(s);return(isNaN(i))?0:i;};this.formatInt=function(s){var i=parseInt(s);return(isNaN(i))?0:i;};this.isDigit=function(s,config){return/^[-+]?\d*$/.test($.trim(s.replace(/[,.']/g,'')));};this.clearTableBody=function(table){if($.browser.msie){function empty(){while(this.firstChild)this.removeChild(this.firstChild);}empty.apply(table.tBodies[0]);}else{table.tBodies[0].innerHTML="";}};}});$.fn.extend({tablesorter:$.tablesorter.construct});var ts=$.tablesorter;ts.addParser({id:"text",is:function(s){return true;},format:function(s){return $.trim(s.toLocaleLowerCase());},type:"text"});ts.addParser({id:"digit",is:function(s,table){var c=table.config;return $.tablesorter.isDigit(s,c);},format:function(s){return $.tablesorter.formatFloat(s);},type:"numeric"});ts.addParser({id:"currency",is:function(s){return/^[£$€?.]/.test(s);},format:function(s){return $.tablesorter.formatFloat(s.replace(new RegExp(/[£$€]/g),""));},type:"numeric"});ts.addParser({id:"ipAddress",is:function(s){return/^\d{2,3}[\.]\d{2,3}[\.]\d{2,3}[\.]\d{2,3}$/.test(s);},format:function(s){var a=s.split("."),r="",l=a.length;for(var i=0;i<l;i++){var item=a[i];if(item.length==2){r+="0"+item;}else{r+=item;}}return $.tablesorter.formatFloat(r);},type:"numeric"});ts.addParser({id:"url",is:function(s){return/^(https?|ftp|file):\/\/$/.test(s);},format:function(s){return jQuery.trim(s.replace(new RegExp(/(https?|ftp|file):\/\//),''));},type:"text"});ts.addParser({id:"isoDate",is:function(s){return/^\d{4}[\/-]\d{1,2}[\/-]\d{1,2}$/.test(s);},format:function(s){return $.tablesorter.formatFloat((s!="")?new Date(s.replace(new RegExp(/-/g),"/")).getTime():"0");},type:"numeric"});ts.addParser({id:"percent",is:function(s){return/\%$/.test($.trim(s));},format:function(s){return $.tablesorter.formatFloat(s.replace(new RegExp(/%/g),""));},type:"numeric"});ts.addParser({id:"usLongDate",is:function(s){return s.match(new RegExp(/^[A-Za-z]{3,10}\.? [0-9]{1,2}, ([0-9]{4}|'?[0-9]{2}) (([0-2]?[0-9]:[0-5][0-9])|([0-1]?[0-9]:[0-5][0-9]\s(AM|PM)))$/));},format:function(s){return $.tablesorter.formatFloat(new Date(s).getTime());},type:"numeric"});ts.addParser({id:"shortDate",is:function(s){return/\d{1,2}[\/\-]\d{1,2}[\/\-]\d{2,4}/.test(s);},format:function(s,table){var c=table.config;s=s.replace(/\-/g,"/");if(c.dateFormat=="us"){s=s.replace(/(\d{1,2})[\/\-](\d{1,2})[\/\-](\d{4})/,"$3/$1/$2");}else if(c.dateFormat=="uk"){s=s.replace(/(\d{1,2})[\/\-](\d{1,2})[\/\-](\d{4})/,"$3/$2/$1");}else if(c.dateFormat=="dd/mm/yy"||c.dateFormat=="dd-mm-yy"){s=s.replace(/(\d{1,2})[\/\-](\d{1,2})[\/\-](\d{2})/,"$1/$2/$3");}return $.tablesorter.formatFloat(new Date(s).getTime());},type:"numeric"});ts.addParser({id:"time",is:function(s){return/^(([0-2]?[0-9]:[0-5][0-9])|([0-1]?[0-9]:[0-5][0-9]\s(am|pm)))$/.test(s);},format:function(s){return $.tablesorter.formatFloat(new Date("2000/01/01 "+s).getTime());},type:"numeric"});ts.addParser({id:"metadata",is:function(s){return false;},format:function(s,table,cell){var c=table.config,p=(!c.parserMetadataName)?'sortValue':c.parserMetadataName;return $(cell).metadata()[p];},type:"numeric"});ts.addWidget({id:"zebra",format:function(table){if(table.config.debug){var time=new Date();}var $tr,row=-1,odd;$("tr:visible",table.tBodies[0]).each(function(i){$tr=$(this);if(!$tr.hasClass(table.config.cssChildRow))row++;odd=(row%2==0);$tr.removeClass(table.config.widgetZebra.css[odd?0:1]).addClass(table.config.widgetZebra.css[odd?1:0])});if(table.config.debug){$.tablesorter.benchmark("Applying Zebra widget",time);}}});})(jQuery);
|
|
9
lang/de.yml
Normal file
9
lang/de.yml
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
de:
|
||||||
|
GoogleSitemaps:
|
||||||
|
METANOTEPRIORITY: 'Manuelle Google Sitemaps-Priorität für diese Seite (%s)'
|
||||||
|
METAPAGEPRIO: 'Page Priorität'
|
||||||
|
PRIORITYAUTOSET: 'Automatisch basierend auf Verschachtelung im Seitenbaum'
|
||||||
|
PRIORITYLEASTIMPORTANT: 'Am wenigsten wichtig'
|
||||||
|
PRIORITYMOSTIMPORTANT: 'Am wichtigsten'
|
||||||
|
PRIORITYNOTINDEXED: 'Nicht indexiert'
|
||||||
|
TABGOOGLESITEMAP: 'Google Sitemap'
|
9
lang/fi.yml
Normal file
9
lang/fi.yml
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
fi:
|
||||||
|
GoogleSitemaps:
|
||||||
|
METANOTEPRIORITY: 'Määritä Google Sitemaps -prioriteetti tälle sivulle (%s)'
|
||||||
|
METAPAGEPRIO: 'Sivun prioriteetti'
|
||||||
|
PRIORITYAUTOSET: 'Päättele automaattisesti valikkorakenteen mukaan'
|
||||||
|
PRIORITYLEASTIMPORTANT: 'Vähiten tärkein'
|
||||||
|
PRIORITYMOSTIMPORTANT: 'Kaikista tärkein'
|
||||||
|
PRIORITYNOTINDEXED: 'Pyydä olemaan näyttämättä hakukoneissa'
|
||||||
|
TABGOOGLESITEMAP: 'Google Sitemap'
|
9
lang/it.yml
Normal file
9
lang/it.yml
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
it:
|
||||||
|
GoogleSitemaps:
|
||||||
|
METANOTEPRIORITY: 'Specifica manualmente una priorità Sitemaps di Google per questa pagina (%s)'
|
||||||
|
METAPAGEPRIO: 'Priorità Pagina'
|
||||||
|
PRIORITYAUTOSET: 'Imposta automaticamente in base alla profondità della pagina'
|
||||||
|
PRIORITYLEASTIMPORTANT: 'Meno importante'
|
||||||
|
PRIORITYMOSTIMPORTANT: 'Più importante'
|
||||||
|
PRIORITYNOTINDEXED: 'Non indicizzato'
|
||||||
|
TABGOOGLESITEMAP: 'Sitemap di Google'
|
9
lang/lt.yml
Normal file
9
lang/lt.yml
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
lt:
|
||||||
|
GoogleSitemaps:
|
||||||
|
METANOTEPRIORITY: 'Šio puslapio Google svetainės medžio prioriteto nustatymas (%s)'
|
||||||
|
METAPAGEPRIO: 'Puslapio prioritetas'
|
||||||
|
PRIORITYAUTOSET: 'Automatiškai nustatyti prioritetą pagal puslapio gylį'
|
||||||
|
PRIORITYLEASTIMPORTANT: 'Mažiausiai svarbus'
|
||||||
|
PRIORITYMOSTIMPORTANT: 'Labiausiai svarbus'
|
||||||
|
PRIORITYNOTINDEXED: 'Neindeksuojamas'
|
||||||
|
TABGOOGLESITEMAP: 'Google svetainės medis'
|
10
lang/ru.yml
Normal file
10
lang/ru.yml
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
|
||||||
|
ru:
|
||||||
|
GoogleSitemaps:
|
||||||
|
METANOTEPRIORITY: 'Установить вручную Google Sitemaps приоритет для этой страшицы (%s)'
|
||||||
|
METAPAGEPRIO: 'Приоритет страницы'
|
||||||
|
PRIORITYAUTOSET: 'Автоматичисеки на основании глубины просмотра'
|
||||||
|
PRIORITYLEASTIMPORTANT: 'Наименее важная'
|
||||||
|
PRIORITYMOSTIMPORTANT: 'Наиболее Важная'
|
||||||
|
PRIORITYNOTINDEXED: 'Не индексировать'
|
||||||
|
TABGOOGLESITEMAP: 'Google Sitemap'
|
9
lang/sl.yml
Normal file
9
lang/sl.yml
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
sl:
|
||||||
|
GoogleSitemaps:
|
||||||
|
METANOTEPRIORITY: 'Ročno nastavite (Google Sitemaps priority) pomembnost trenutne strani (%s)'
|
||||||
|
METAPAGEPRIO: 'Pomembnost strani'
|
||||||
|
PRIORITYAUTOSET: 'Samodejno glede na nivo v strukturi'
|
||||||
|
PRIORITYLEASTIMPORTANT: 'Manj pomembna'
|
||||||
|
PRIORITYMOSTIMPORTANT: 'Bolj pomembna'
|
||||||
|
PRIORITYNOTINDEXED: 'Ne indeksiraj'
|
||||||
|
TABGOOGLESITEMAP: 'Google Sitemap'
|
11
phpcs.xml.dist
Normal file
11
phpcs.xml.dist
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
<?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 -->
|
||||||
|
<exclude name="PSR1.Methods.CamelCapsMethodName" />
|
||||||
|
</rule>
|
||||||
|
</ruleset>
|
14
phpunit.xml.dist
Normal file
14
phpunit.xml.dist
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
<?xml version="1.0"?>
|
||||||
|
<phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" bootstrap="vendor/silverstripe/framework/tests/bootstrap.php" colors="true" xsi:noNamespaceSchemaLocation="https://schema.phpunit.de/9.3/phpunit.xsd">
|
||||||
|
<coverage includeUncoveredFiles="true">
|
||||||
|
<include>
|
||||||
|
<directory suffix=".php">src/</directory>
|
||||||
|
</include>
|
||||||
|
<exclude>
|
||||||
|
<directory suffix=".php">tests/</directory>
|
||||||
|
</exclude>
|
||||||
|
</coverage>
|
||||||
|
<testsuite name="Default">
|
||||||
|
<directory>tests</directory>
|
||||||
|
</testsuite>
|
||||||
|
</phpunit>
|
137
src/Control/GoogleSitemapController.php
Normal file
137
src/Control/GoogleSitemapController.php
Normal file
@ -0,0 +1,137 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Wilr\GoogleSitemaps\Control;
|
||||||
|
|
||||||
|
use SilverStripe\CMS\Model\SiteTree;
|
||||||
|
use SilverStripe\Control\Controller;
|
||||||
|
use SilverStripe\Control\Director;
|
||||||
|
use SilverStripe\Control\HTTPResponse;
|
||||||
|
use Wilr\GoogleSitemaps\GoogleSitemap;
|
||||||
|
use SilverStripe\View\ArrayData;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Controller for displaying the sitemap.xml. The module displays an index
|
||||||
|
* sitemap at the sitemap.xml level, then outputs the individual objects
|
||||||
|
* at a second level.
|
||||||
|
*
|
||||||
|
* <code>
|
||||||
|
* http://site.com/sitemap.xml/
|
||||||
|
* http://site.com/sitemap.xml/sitemap/$ClassName-$Page.xml
|
||||||
|
* </code>
|
||||||
|
*
|
||||||
|
* @package googlesitemaps
|
||||||
|
*/
|
||||||
|
class GoogleSitemapController extends Controller
|
||||||
|
{
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var array
|
||||||
|
*/
|
||||||
|
private static $allowed_actions = [
|
||||||
|
'index',
|
||||||
|
'sitemap',
|
||||||
|
'styleSheetIndex',
|
||||||
|
'styleSheet'
|
||||||
|
];
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Default controller action for the sitemap.xml file. Renders a index
|
||||||
|
* file containing a list of links to sub sitemaps containing the data.
|
||||||
|
*
|
||||||
|
* @return mixed
|
||||||
|
*/
|
||||||
|
public function index($url)
|
||||||
|
{
|
||||||
|
if (GoogleSitemap::enabled()) {
|
||||||
|
$this->getResponse()->addHeader('Content-Type', 'application/xml; charset="utf-8"');
|
||||||
|
$this->getResponse()->addHeader('X-Robots-Tag', 'noindex');
|
||||||
|
|
||||||
|
$sitemaps = GoogleSitemap::inst()->getSitemaps();
|
||||||
|
$this->extend('updateGoogleSitemaps', $sitemaps);
|
||||||
|
|
||||||
|
return $this->customise(new ArrayData([
|
||||||
|
'Sitemaps' => $sitemaps,
|
||||||
|
]))->renderWith(__CLASS__);
|
||||||
|
} else {
|
||||||
|
return new HTTPResponse('Page not found', 404);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Specific controller action for displaying a particular list of links
|
||||||
|
* for a class
|
||||||
|
*
|
||||||
|
* @return mixed
|
||||||
|
*/
|
||||||
|
public function sitemap()
|
||||||
|
{
|
||||||
|
$class = $this->unsanitiseClassName($this->request->param('ID'));
|
||||||
|
$page = intval($this->request->param('OtherID'));
|
||||||
|
|
||||||
|
if ($page) {
|
||||||
|
if (!is_numeric($page)) {
|
||||||
|
return new HTTPResponse('Page not found', 404);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (GoogleSitemap::enabled()
|
||||||
|
&& $class
|
||||||
|
&& ($page > 0)
|
||||||
|
&& ($class == SiteTree::class || $class == 'GoogleSitemapRoute' || GoogleSitemap::is_registered($class))
|
||||||
|
) {
|
||||||
|
$this->getResponse()->addHeader('Content-Type', 'application/xml; charset="utf-8"');
|
||||||
|
$this->getResponse()->addHeader('X-Robots-Tag', 'noindex');
|
||||||
|
|
||||||
|
$items = GoogleSitemap::inst()->getItems($class, $page);
|
||||||
|
$this->extend('updateGoogleSitemapItems', $items, $class, $page);
|
||||||
|
|
||||||
|
return array(
|
||||||
|
'Items' => $items
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return new HTTPResponse('Page not found', 404);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Unsanitise a namespaced class' name from a URL param
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
protected function unsanitiseClassName($class)
|
||||||
|
{
|
||||||
|
return str_replace('-', '\\', (string) $class);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Render the stylesheet for the sitemap index
|
||||||
|
*
|
||||||
|
* @return DBHTMLText
|
||||||
|
*/
|
||||||
|
public function styleSheetIndex()
|
||||||
|
{
|
||||||
|
$html = $this->renderWith('xml-sitemapindex');
|
||||||
|
$this->getResponse()->addHeader('Content-Type', 'text/xsl; charset="utf-8"');
|
||||||
|
|
||||||
|
return $html;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Render the stylesheet for the sitemap
|
||||||
|
*
|
||||||
|
* @return DBHTMLText
|
||||||
|
*/
|
||||||
|
public function styleSheet()
|
||||||
|
{
|
||||||
|
$html = $this->renderWith('xml-sitemap');
|
||||||
|
$this->getResponse()->addHeader('Content-Type', 'text/xsl; charset="utf-8"');
|
||||||
|
|
||||||
|
return $html;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public function AbsoluteLink($action = null)
|
||||||
|
{
|
||||||
|
return rtrim(Controller::join_links(Director::absoluteBaseURL(), 'sitemap.xml', $action), '/');
|
||||||
|
}
|
||||||
|
}
|
146
src/Extensions/GoogleSitemapExtension.php
Normal file
146
src/Extensions/GoogleSitemapExtension.php
Normal file
@ -0,0 +1,146 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Wilr\GoogleSitemaps\Extensions;
|
||||||
|
|
||||||
|
use SilverStripe\Control\Director;
|
||||||
|
use SilverStripe\ORM\DataExtension;
|
||||||
|
use SilverStripe\ORM\FieldType\DBDatetime;
|
||||||
|
use SilverStripe\Subsites\Model\Subsite;
|
||||||
|
use Wilr\GoogleSitemaps\GoogleSitemap;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Decorate the page object to provide google sitemaps with additional options
|
||||||
|
* and configuration.
|
||||||
|
*/
|
||||||
|
class GoogleSitemapExtension extends DataExtension
|
||||||
|
{
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return boolean
|
||||||
|
*/
|
||||||
|
public function canIncludeInGoogleSitemap()
|
||||||
|
{
|
||||||
|
$can = true;
|
||||||
|
|
||||||
|
if ($this->owner->hasMethod('AbsoluteLink')) {
|
||||||
|
$hostHttp = parse_url(Director::protocolAndHost(), PHP_URL_HOST);
|
||||||
|
|
||||||
|
// Subsite support
|
||||||
|
if (class_exists(Subsite::class)) {
|
||||||
|
// Subsite will have a different domain from Director::protocolAndHost
|
||||||
|
if ($subsite = Subsite::currentSubsite()) {
|
||||||
|
$hostHttp = parse_url(Director::protocol() . $subsite->getPrimaryDomain(), PHP_URL_HOST);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$objHttp = parse_url($this->owner->AbsoluteLink() ?? '', PHP_URL_HOST);
|
||||||
|
|
||||||
|
if ($objHttp != $hostHttp) {
|
||||||
|
$can = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($can) {
|
||||||
|
$can = $this->owner->canView();
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($can) {
|
||||||
|
$can = ($this->owner->getGooglePriority() !== false);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($can === false) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Allow override. invokeWithExtensions will either return a single result (true|false) if defined on the object
|
||||||
|
// or an array if on extensions.
|
||||||
|
$override = $this->owner->invokeWithExtensions('alterCanIncludeInGoogleSitemap', $can);
|
||||||
|
|
||||||
|
if ($override !== null) {
|
||||||
|
if (is_array($override)) {
|
||||||
|
if (!empty($override)) {
|
||||||
|
$can = min($override, $can);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
$can = $override;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (is_array($can) && isset($can[0])) {
|
||||||
|
return $can[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
return $can;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The default value of the priority field depends on the depth of the page in
|
||||||
|
* the site tree, so it must be calculated dynamically.
|
||||||
|
*
|
||||||
|
* @return mixed
|
||||||
|
*/
|
||||||
|
public function getGooglePriority()
|
||||||
|
{
|
||||||
|
$field = $this->owner->hasField('Priority');
|
||||||
|
|
||||||
|
if ($field) {
|
||||||
|
$priority = $this->owner->getField('Priority');
|
||||||
|
|
||||||
|
return ($priority < 0) ? false : $priority;
|
||||||
|
}
|
||||||
|
|
||||||
|
return GoogleSitemap::get_priority_for_class($this->owner->ClassName);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a pages change frequency calculated by pages age and number of
|
||||||
|
* versions. Google expects always, hourly, daily, weekly, monthly, yearly
|
||||||
|
* or never as values.
|
||||||
|
*
|
||||||
|
* @see http://support.google.com/webmasters/bin/answer.py?hl=en&answer=183668&topic=8476&ctx=topic
|
||||||
|
*
|
||||||
|
* @return DBDatetime
|
||||||
|
*/
|
||||||
|
public function getChangeFrequency()
|
||||||
|
{
|
||||||
|
if ($freq = GoogleSitemap::get_frequency_for_class($this->owner->ClassName)) {
|
||||||
|
return $freq;
|
||||||
|
}
|
||||||
|
|
||||||
|
$date = date('Y-m-d H:i:s');
|
||||||
|
|
||||||
|
$created = new DBDatetime();
|
||||||
|
$created->value = ($this->owner->Created) ? $this->owner->Created : $date;
|
||||||
|
|
||||||
|
$now = new DBDatetime();
|
||||||
|
$now->value = $date;
|
||||||
|
|
||||||
|
$versions = ($this->owner->Version) ? $this->owner->Version : 1;
|
||||||
|
|
||||||
|
if ($now && $created) {
|
||||||
|
$timediff = $now->getTimestamp() - $created->getTimestamp();
|
||||||
|
|
||||||
|
// 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);
|
||||||
|
} else {
|
||||||
|
$period = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($period > 60 * 60 * 24 * 365) {
|
||||||
|
$freq = 'yearly';
|
||||||
|
} elseif ($period > 60 * 60 * 24 * 30) {
|
||||||
|
$freq = 'monthly';
|
||||||
|
} elseif ($period > 60 * 60 * 24 * 7) {
|
||||||
|
$freq = 'weekly';
|
||||||
|
} elseif ($period > 60 * 60 * 24) {
|
||||||
|
$freq = 'daily';
|
||||||
|
} elseif ($period > 60 * 60) {
|
||||||
|
$freq = 'hourly';
|
||||||
|
} else {
|
||||||
|
$freq = 'always';
|
||||||
|
}
|
||||||
|
|
||||||
|
return $freq;
|
||||||
|
}
|
||||||
|
}
|
218
src/Extensions/GoogleSitemapSiteTreeExtension.php
Normal file
218
src/Extensions/GoogleSitemapSiteTreeExtension.php
Normal file
@ -0,0 +1,218 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Wilr\GoogleSitemaps\Extensions;
|
||||||
|
|
||||||
|
use SilverStripe\Assets\Image;
|
||||||
|
use SilverStripe\ErrorPage\ErrorPage;
|
||||||
|
use SilverStripe\Forms\DropdownField;
|
||||||
|
use SilverStripe\Forms\LiteralField;
|
||||||
|
use SilverStripe\Forms\Tab;
|
||||||
|
use SilverStripe\ORM\ArrayList;
|
||||||
|
use Throwable;
|
||||||
|
|
||||||
|
class GoogleSitemapSiteTreeExtension extends GoogleSitemapExtension
|
||||||
|
{
|
||||||
|
private static $db = [
|
||||||
|
"Priority" => "Varchar(5)"
|
||||||
|
];
|
||||||
|
|
||||||
|
public function updateSettingsFields(&$fields)
|
||||||
|
{
|
||||||
|
$prorities = array(
|
||||||
|
'-1' => _t('GoogleSitemaps.PRIORITYNOTINDEXED', "Not indexed"),
|
||||||
|
'1.0' => '1 - ' . _t('GoogleSitemaps.PRIORITYMOSTIMPORTANT', "Most important"),
|
||||||
|
'0.9' => '2',
|
||||||
|
'0.8' => '3',
|
||||||
|
'0.7' => '4',
|
||||||
|
'0.6' => '5',
|
||||||
|
'0.5' => '6',
|
||||||
|
'0.4' => '7',
|
||||||
|
'0.3' => '8',
|
||||||
|
'0.2' => '9',
|
||||||
|
'0.1' => '10 - ' . _t('GoogleSitemaps.PRIORITYLEASTIMPORTANT', "Least important")
|
||||||
|
);
|
||||||
|
|
||||||
|
$tabset = $fields->findOrMakeTab('Root.Settings');
|
||||||
|
|
||||||
|
$message = "<p>";
|
||||||
|
$message .= sprintf(
|
||||||
|
_t(
|
||||||
|
'GoogleSitemaps.METANOTEPRIORITY',
|
||||||
|
"Manually specify a Google Sitemaps priority for this page (%s)"
|
||||||
|
),
|
||||||
|
'<a href="http://www.google.com/support/webmasters/bin/answer.py?hl=en&answer=71936#prioritize" '
|
||||||
|
. 'target="_blank">?</a>'
|
||||||
|
);
|
||||||
|
$message .= "</p>";
|
||||||
|
|
||||||
|
$tabset->push(new Tab(
|
||||||
|
'GoogleSitemap',
|
||||||
|
_t('GoogleSitemaps.TABGOOGLESITEMAP', 'Google Sitemap'),
|
||||||
|
LiteralField::create("GoogleSitemapIntro", $message),
|
||||||
|
$priority = DropdownField::create(
|
||||||
|
"Priority",
|
||||||
|
$this->owner->fieldLabel('Priority'),
|
||||||
|
$prorities,
|
||||||
|
$this->owner->Priority
|
||||||
|
)
|
||||||
|
));
|
||||||
|
|
||||||
|
$priority->setEmptyString(_t('GoogleSitemaps.PRIORITYAUTOSET', 'Auto-set based on page depth'));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function updateFieldLabels(&$labels)
|
||||||
|
{
|
||||||
|
parent::updateFieldLabels($labels);
|
||||||
|
|
||||||
|
$labels['Priority'] = _t('GoogleSitemaps.METAPAGEPRIO', "Page Priority");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Ensure that all parent pages of this page (if any) are published
|
||||||
|
*
|
||||||
|
* @return boolean
|
||||||
|
*/
|
||||||
|
public function hasPublishedParent()
|
||||||
|
{
|
||||||
|
|
||||||
|
// Skip root pages
|
||||||
|
if (empty($this->owner->ParentID)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ensure direct parent exists
|
||||||
|
$parent = $this->owner->Parent();
|
||||||
|
if (empty($parent) || !$parent->exists()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check ancestry
|
||||||
|
return $parent->hasPublishedParent();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return boolean
|
||||||
|
*/
|
||||||
|
public function canIncludeInGoogleSitemap()
|
||||||
|
{
|
||||||
|
|
||||||
|
// Check that parent page is published
|
||||||
|
if (!$this->owner->hasPublishedParent()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
$result = parent::canIncludeInGoogleSitemap();
|
||||||
|
$result = ($this->owner instanceof ErrorPage) ? false : $result;
|
||||||
|
|
||||||
|
if (is_array($result) && isset($result[0])) {
|
||||||
|
return $result[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
return $result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return mixed
|
||||||
|
*/
|
||||||
|
public function getGooglePriority()
|
||||||
|
{
|
||||||
|
setlocale(LC_ALL, "en_US.UTF8");
|
||||||
|
$priority = $this->owner->getField('Priority');
|
||||||
|
|
||||||
|
if (!$priority) {
|
||||||
|
$parentStack = $this->owner->getAncestors();
|
||||||
|
$numParents = $parentStack->count();
|
||||||
|
|
||||||
|
$num = max(0.1, 1.0 - ($numParents / 10));
|
||||||
|
$result = str_replace(",", ".", $num);
|
||||||
|
|
||||||
|
return $result;
|
||||||
|
} elseif ($priority == -1) {
|
||||||
|
return false;
|
||||||
|
} else {
|
||||||
|
return (is_numeric($priority) && $priority <= 1.0) ? $priority : 0.5;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function ImagesForSitemap()
|
||||||
|
{
|
||||||
|
$list = new ArrayList();
|
||||||
|
$cachedImages = [];
|
||||||
|
|
||||||
|
foreach ($this->owner->hasOne() as $field => $type) {
|
||||||
|
if (strpos($type, '.') !== false) {
|
||||||
|
$type = explode('.', $type)[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (singleton($type) instanceof Image) {
|
||||||
|
$image = $this->owner->getComponent($field);
|
||||||
|
|
||||||
|
try {
|
||||||
|
if ($image && $image->exists() && !isset($cachedImages[$image->ID])) {
|
||||||
|
$cachedImages[$image->ID] = true;
|
||||||
|
|
||||||
|
$list->push($image);
|
||||||
|
}
|
||||||
|
} catch (Throwable $e) {
|
||||||
|
//
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach ($this->owner->hasMany() as $field => $type) {
|
||||||
|
if (singleton($type) instanceof Image) {
|
||||||
|
$images = $this->owner->getComponents($field);
|
||||||
|
|
||||||
|
foreach ($images as $image) {
|
||||||
|
try {
|
||||||
|
if ($image && $image->exists() && !isset($cachedImages[$image->ID])) {
|
||||||
|
$cachedImages[$image->ID] = true;
|
||||||
|
|
||||||
|
$list->push($image);
|
||||||
|
}
|
||||||
|
} catch (Throwable $e) {
|
||||||
|
//
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach ($this->owner->manyMany() as $field => $type) {
|
||||||
|
$image = false;
|
||||||
|
|
||||||
|
if (is_array($type) && isset($type['through'])) {
|
||||||
|
if (singleton($type['through']) instanceof Image) {
|
||||||
|
$image = true;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (strpos($type, '.') !== false) {
|
||||||
|
$type = explode('.', $type)[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (singleton($type) instanceof Image) {
|
||||||
|
$image = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($image) {
|
||||||
|
$images = $this->owner->$field();
|
||||||
|
|
||||||
|
foreach ($images as $image) {
|
||||||
|
try {
|
||||||
|
if ($image && $image->exists() && !isset($cachedImages[$image->ID])) {
|
||||||
|
$cachedImages[$image->ID] = true;
|
||||||
|
|
||||||
|
$list->push($image);
|
||||||
|
}
|
||||||
|
} catch (Throwable $e) {
|
||||||
|
//
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->owner->extend('updateImagesForSitemap', $list);
|
||||||
|
|
||||||
|
return $list;
|
||||||
|
}
|
||||||
|
}
|
466
src/GoogleSitemap.php
Normal file
466
src/GoogleSitemap.php
Normal file
@ -0,0 +1,466 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Wilr\GoogleSitemaps;
|
||||||
|
|
||||||
|
use SilverStripe\Control\Director;
|
||||||
|
use SilverStripe\Control\Controller;
|
||||||
|
use SilverStripe\Core\ClassInfo;
|
||||||
|
use SilverStripe\Core\Config\Config;
|
||||||
|
use SilverStripe\ORM\ArrayList;
|
||||||
|
use SilverStripe\ORM\DataList;
|
||||||
|
use SilverStripe\Versioned\Versioned;
|
||||||
|
use SilverStripe\View\ArrayData;
|
||||||
|
use SilverStripe\Core\Extensible;
|
||||||
|
use SilverStripe\Core\Injector\Injectable;
|
||||||
|
use SilverStripe\Core\Config\Configurable;
|
||||||
|
use Wilr\GoogleSitemaps\Extensions\GoogleSitemapExtension;
|
||||||
|
use ReflectionClass;
|
||||||
|
use ReflectionException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sitemaps are a way to tell Google about pages on your site that they might
|
||||||
|
* not otherwise discover. In its simplest terms, a XML Sitemap usually called
|
||||||
|
* a Sitemap, with a capital S—is a list of the pages on your website.
|
||||||
|
*
|
||||||
|
* Creating and submitting a Sitemap helps make sure that Google knows about
|
||||||
|
* all the pages on your site, including URLs that may not be discoverable by
|
||||||
|
* Google's normal crawling process.
|
||||||
|
*
|
||||||
|
* The GoogleSitemap handle requests to 'sitemap.xml' the other two classes are
|
||||||
|
* used to render the sitemap.
|
||||||
|
*
|
||||||
|
* The config file is usually located in the _config folder of your project folder.
|
||||||
|
* e.g. app/_config/googlesitemaps.yml
|
||||||
|
*
|
||||||
|
* <example>
|
||||||
|
* ---
|
||||||
|
* Name: customgooglesitemaps
|
||||||
|
* After: googlesitemaps
|
||||||
|
* ---
|
||||||
|
* Wilr\GoogleSitemaps\GoogleSitemap:
|
||||||
|
* enabled: true
|
||||||
|
* objects_per_sitemap: 1000
|
||||||
|
* use_show_in_search: true
|
||||||
|
* </example>
|
||||||
|
*
|
||||||
|
* @see http://www.google.com/support/webmasters/bin/answer.py?hl=en&answer=34609
|
||||||
|
*/
|
||||||
|
class GoogleSitemap
|
||||||
|
{
|
||||||
|
use Extensible;
|
||||||
|
use Injectable;
|
||||||
|
use Configurable;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* List of {@link DataObject} class names to include. As well as the change
|
||||||
|
* frequency and priority of each class.
|
||||||
|
*
|
||||||
|
* @var array
|
||||||
|
*/
|
||||||
|
private static $dataobjects = [];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* List of custom routes to include in the sitemap (such as controller
|
||||||
|
* subclasses) as well as the change frequency and priority.
|
||||||
|
*
|
||||||
|
* @var array
|
||||||
|
*/
|
||||||
|
private static $routes = [];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @config
|
||||||
|
*
|
||||||
|
* @var boolean
|
||||||
|
*/
|
||||||
|
private static $exclude_redirector_pages = true;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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 how often is this DataObject updated?
|
||||||
|
* Possible values:
|
||||||
|
* always, hourly, daily, weekly, monthly, yearly, never
|
||||||
|
* @param string $priority How important is this DataObject in comparison to other urls?
|
||||||
|
* Possible values: 0.1, 0.2 ... , 0.9, 1.0
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public static function register_dataobject($className, $changeFreq = 'monthly', $priority = '0.6')
|
||||||
|
{
|
||||||
|
if (!self::is_registered($className)) {
|
||||||
|
$className::add_extension(GoogleSitemapExtension::class);
|
||||||
|
|
||||||
|
self::$dataobjects[$className] = [
|
||||||
|
'frequency' => ($changeFreq) ? $changeFreq : 'monthly',
|
||||||
|
'priority' => ($priority) ? $priority : '0.6'
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Registers multiple dataobjects in a single line. See {@link register_dataobject}
|
||||||
|
* for the heavy lifting
|
||||||
|
*
|
||||||
|
* @param array $dataobjects array of class names of DataObject to register
|
||||||
|
* @param string $changeFreq how often is this DataObject updated?
|
||||||
|
* Possible values:
|
||||||
|
* always, hourly, daily, weekly, monthly, yearly, never
|
||||||
|
* @param string $priority How important is this DataObject in comparison to other urls?
|
||||||
|
* Possible values: 0.1, 0.2 ... , 0.9, 1.0
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public static function register_dataobjects($dataobjects, $changeFreq = 'monthly', $priority = '0.6')
|
||||||
|
{
|
||||||
|
foreach ($dataobjects as $obj) {
|
||||||
|
self::register_dataobject($obj, $changeFreq, $priority);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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)
|
||||||
|
{
|
||||||
|
if (!isset(self::$dataobjects[$className])) {
|
||||||
|
$lowerKeys = array_change_key_case(self::$dataobjects);
|
||||||
|
|
||||||
|
return isset($lowerKeys[$className]);
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Unregisters a class from the sitemap. Mostly used for the test suite
|
||||||
|
*
|
||||||
|
* @param string
|
||||||
|
*/
|
||||||
|
public static function unregister_dataobject($className)
|
||||||
|
{
|
||||||
|
unset(self::$dataobjects[$className]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Clears registered {@link DataObjects}. Useful for unit tests.
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public static function clear_registered_dataobjects()
|
||||||
|
{
|
||||||
|
self::$dataobjects = array();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Register a given route to the sitemap list
|
||||||
|
*
|
||||||
|
* @param string
|
||||||
|
* @param string
|
||||||
|
* @param string
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public static function register_route($route, $changeFreq = 'monthly', $priority = '0.6')
|
||||||
|
{
|
||||||
|
self::$routes = array_merge(self::$routes, array(
|
||||||
|
$route => array(
|
||||||
|
'frequency' => ($changeFreq) ? $changeFreq : 'monthly',
|
||||||
|
'priority' => ($priority) ? $priority : '0.6'
|
||||||
|
)
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Registers a given list of relative urls. Will be merged with the current
|
||||||
|
* registered routes. If you want to replace them, please call {@link clear_routes}
|
||||||
|
*
|
||||||
|
* @param array
|
||||||
|
* @param string
|
||||||
|
* @param string
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public static function register_routes($routes, $changeFreq = 'monthly', $priority = '0.6')
|
||||||
|
{
|
||||||
|
foreach ($routes as $route) {
|
||||||
|
self::register_route($route, $changeFreq, $priority);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Clears registered routes
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public static function clear_registered_routes()
|
||||||
|
{
|
||||||
|
self::$routes = array();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructs the list of data to include in the rendered sitemap. Links
|
||||||
|
* can include pages from the website, dataobjects (such as forum posts)
|
||||||
|
* as well as custom registered paths.
|
||||||
|
*
|
||||||
|
* @param string
|
||||||
|
* @param int
|
||||||
|
*
|
||||||
|
* @return ArrayList
|
||||||
|
*/
|
||||||
|
public function getItems($class, $page = 1)
|
||||||
|
{
|
||||||
|
$page = (int) $page;
|
||||||
|
|
||||||
|
try {
|
||||||
|
$reflectionClass = new ReflectionClass($class);
|
||||||
|
$class = $reflectionClass->getName();
|
||||||
|
} catch (ReflectionException $e) {
|
||||||
|
// this can happen when $class is GoogleSitemapRoute
|
||||||
|
//we should try to carry on
|
||||||
|
}
|
||||||
|
|
||||||
|
$output = new ArrayList();
|
||||||
|
$count = (int) Config::inst()->get(__CLASS__, 'objects_per_sitemap');
|
||||||
|
$filter = Config::inst()->get(__CLASS__, 'use_show_in_search');
|
||||||
|
$redirector = Config::inst()->get(__CLASS__, 'exclude_redirector_pages');
|
||||||
|
|
||||||
|
// todo migrate to extension hook or DI point for other modules to
|
||||||
|
// modify state filters
|
||||||
|
if (class_exists('Translatable')) {
|
||||||
|
Translatable::disable_locale_filter();
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($class == 'SilverStripe\CMS\Model\SiteTree') {
|
||||||
|
$instances = Versioned::get_by_stage('SilverStripe\CMS\Model\SiteTree', 'Live');
|
||||||
|
|
||||||
|
if ($filter) {
|
||||||
|
$instances = $instances->filter('ShowInSearch', 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($redirector) {
|
||||||
|
foreach (ClassInfo::subclassesFor('SilverStripe\\CMS\\Model\\RedirectorPage') as $redirectorClass) {
|
||||||
|
$instances = $instances->exclude('ClassName', $redirectorClass);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} elseif ($class == "GoogleSitemapRoute") {
|
||||||
|
$instances = array_slice(self::$routes, ($page - 1) * $count, $count);
|
||||||
|
$output = new ArrayList();
|
||||||
|
|
||||||
|
if ($instances) {
|
||||||
|
foreach ($instances as $route => $config) {
|
||||||
|
$output->push(new ArrayData(array(
|
||||||
|
'AbsoluteLink' => Director::absoluteURL($route),
|
||||||
|
'ChangeFrequency' => $config['frequency'],
|
||||||
|
'GooglePriority' => $config['priority']
|
||||||
|
)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $output;
|
||||||
|
} else {
|
||||||
|
$instances = new DataList($class);
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->extend("alterDataList", $instances, $class);
|
||||||
|
|
||||||
|
$instances = $instances->limit(
|
||||||
|
$count,
|
||||||
|
($page - 1) * $count
|
||||||
|
);
|
||||||
|
|
||||||
|
if ($instances) {
|
||||||
|
foreach ($instances as $obj) {
|
||||||
|
if ($obj->canIncludeInGoogleSitemap()) {
|
||||||
|
$output->push($obj);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $output;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Static interface to instance level ->getItems() for backward compatibility.
|
||||||
|
*
|
||||||
|
* @param string
|
||||||
|
* @param int
|
||||||
|
*
|
||||||
|
* @return ArrayList
|
||||||
|
* @deprecated Please create an instance and call ->getSitemaps() instead.
|
||||||
|
*/
|
||||||
|
public static function get_items($class, $page = 1)
|
||||||
|
{
|
||||||
|
return static::inst()->getItems($class, $page);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the string frequency of edits for a particular dataobject class.
|
||||||
|
*
|
||||||
|
* Frequency for {@link SiteTree} objects can be determined from the version
|
||||||
|
* history.
|
||||||
|
*
|
||||||
|
* @param string
|
||||||
|
*
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public static function get_frequency_for_class($class)
|
||||||
|
{
|
||||||
|
foreach (self::$dataobjects as $type => $config) {
|
||||||
|
if ($class == $type) {
|
||||||
|
return $config['frequency'];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the default priority of edits for a particular dataobject class.
|
||||||
|
*
|
||||||
|
* @param string
|
||||||
|
*
|
||||||
|
* @return float
|
||||||
|
*/
|
||||||
|
public static function get_priority_for_class($class)
|
||||||
|
{
|
||||||
|
foreach (self::$dataobjects as $type => $config) {
|
||||||
|
if ($class == $type) {
|
||||||
|
return $config['priority'];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0.5;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The google site map is broken down into multiple smaller files to
|
||||||
|
* prevent overbearing a server. By default separate {@link DataObject}
|
||||||
|
* records are keep in separate files and broken down into chunks.
|
||||||
|
*
|
||||||
|
* @return ArrayList
|
||||||
|
*/
|
||||||
|
public function getSitemaps()
|
||||||
|
{
|
||||||
|
$countPerFile = Config::inst()->get(__CLASS__, 'objects_per_sitemap');
|
||||||
|
$sitemaps = new ArrayList();
|
||||||
|
$filter = Config::inst()->get(__CLASS__, 'use_show_in_search');
|
||||||
|
|
||||||
|
if (class_exists('SilverStripe\CMS\Model\SiteTree')) {
|
||||||
|
// move to extension hook. At the moment moduleexists config hook
|
||||||
|
// does not work.
|
||||||
|
if (class_exists('Translatable')) {
|
||||||
|
Translatable::disable_locale_filter();
|
||||||
|
}
|
||||||
|
|
||||||
|
$filter = ($filter) ? "\"ShowInSearch\" = 1" : "";
|
||||||
|
$class = 'SilverStripe\CMS\Model\SiteTree';
|
||||||
|
$instances = Versioned::get_by_stage($class, 'Live', $filter);
|
||||||
|
$this->extend("alterDataList", $instances, $class);
|
||||||
|
$count = $instances->count();
|
||||||
|
|
||||||
|
$neededForPage = ceil($count / $countPerFile);
|
||||||
|
|
||||||
|
for ($i = 1; $i <= $neededForPage; $i++) {
|
||||||
|
$lastEdited = $instances
|
||||||
|
->limit($countPerFile, ($i - 1) * $countPerFile)
|
||||||
|
->sort(null)
|
||||||
|
->max('LastEdited');
|
||||||
|
|
||||||
|
$lastModified = ($lastEdited) ? date('Y-m-d', strtotime($lastEdited)) : date('Y-m-d');
|
||||||
|
|
||||||
|
$sitemaps->push(new ArrayData(array(
|
||||||
|
'ClassName' => $this->sanitiseClassName('SilverStripe\CMS\Model\SiteTree'),
|
||||||
|
'LastModified' => $lastModified,
|
||||||
|
'Page' => $i
|
||||||
|
)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (count(self::$dataobjects) > 0) {
|
||||||
|
foreach (self::$dataobjects as $class => $config) {
|
||||||
|
$list = new DataList($class);
|
||||||
|
$list = $list->sort('LastEdited ASC');
|
||||||
|
$this->extend("alterDataList", $list, $class);
|
||||||
|
$neededForClass = ceil($list->count() / $countPerFile);
|
||||||
|
|
||||||
|
for ($i = 1; $i <= $neededForClass; $i++) {
|
||||||
|
// determine the last modified date for this slice
|
||||||
|
$sliced = $list
|
||||||
|
->limit($countPerFile, ($i - 1) * $countPerFile)
|
||||||
|
->last();
|
||||||
|
|
||||||
|
$lastModified = ($sliced) ? date('Y-m-d', strtotime($sliced->LastEdited)) : date('Y-m-d');
|
||||||
|
|
||||||
|
$sitemaps->push(new ArrayData(array(
|
||||||
|
'ClassName' => $this->sanitiseClassName($class),
|
||||||
|
'Page' => $i,
|
||||||
|
'LastModified' => $lastModified
|
||||||
|
)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (count(self::$routes) > 0) {
|
||||||
|
$needed = ceil(count(self::$routes) / $countPerFile);
|
||||||
|
|
||||||
|
for ($i = 1; $i <= $needed; $i++) {
|
||||||
|
$sitemaps->push(new ArrayData(array(
|
||||||
|
'ClassName' => 'GoogleSitemapRoute',
|
||||||
|
'Page' => $i
|
||||||
|
)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $sitemaps;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Static interface to instance level ->getSitemaps() for backward compatibility.
|
||||||
|
*
|
||||||
|
* @return ArrayList
|
||||||
|
* @deprecated Please create an instance and call ->getSitemaps() instead.
|
||||||
|
*/
|
||||||
|
public static function get_sitemaps()
|
||||||
|
{
|
||||||
|
return static::inst()->getSitemaps();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Is GoogleSitemap enabled?
|
||||||
|
*
|
||||||
|
* @return boolean
|
||||||
|
*/
|
||||||
|
public static function enabled()
|
||||||
|
{
|
||||||
|
return (Config::inst()->get(__CLASS__, 'enabled'));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Convenience method for manufacturing an instance for hew instance-level
|
||||||
|
* methods (and for easier type definition).
|
||||||
|
*
|
||||||
|
* @return GoogleSitemap
|
||||||
|
*/
|
||||||
|
public static function inst()
|
||||||
|
{
|
||||||
|
return GoogleSitemap::create();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sanitise a namespaced class' name for inclusion in a link
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
protected function sanitiseClassName($class)
|
||||||
|
{
|
||||||
|
return str_replace('\\', '-', (string) $class);
|
||||||
|
}
|
||||||
|
}
|
14
src/Sitemapable.php
Normal file
14
src/Sitemapable.php
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
|
||||||
|
namespace Wilr\GoogleSitemaps;
|
||||||
|
|
||||||
|
interface Sitemapable
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Return the absolute URL for this object
|
||||||
|
*
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function AbsoluteLink();
|
||||||
|
}
|
@ -1,7 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<sitemapindex xmlns="http://www.sitemaps.org/schemas/sitemap/0.9"><% loop Sitemaps %>
|
|
||||||
<sitemap>
|
|
||||||
<loc>{$BaseHref}sitemap.xml/sitemap/$ClassName/$Page.xml</loc>
|
|
||||||
<% if LastModified %><lastmod>$LastModified</lastmod><% end_if %>
|
|
||||||
</sitemap><% end_loop %>
|
|
||||||
</sitemapindex>
|
|
@ -1,12 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<?xml-stylesheet type='text/xsl' href='{$BaseHref}googlesitemaps/templates/xml-sitemap.xsl'?>
|
|
||||||
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
|
|
||||||
<% loop $Items %>
|
|
||||||
<url>
|
|
||||||
<loc>$AbsoluteLink</loc>
|
|
||||||
<% if $LastEdited %><lastmod>$LastEdited.Format(c)</lastmod><% end_if %>
|
|
||||||
<% if $ChangeFrequency %><changefreq>$ChangeFrequency</changefreq><% end_if %>
|
|
||||||
<% if $GooglePriority %><priority>$GooglePriority</priority><% end_if %>
|
|
||||||
</url>
|
|
||||||
<% end_loop %>
|
|
||||||
</urlset>
|
|
@ -0,0 +1,8 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<?xml-stylesheet type='text/xsl' href='{$AbsoluteLink('styleSheetIndex')}'?>
|
||||||
|
<sitemapindex xmlns="http://www.sitemaps.org/schemas/sitemap/0.9"><% loop Sitemaps %>
|
||||||
|
<sitemap>
|
||||||
|
<loc>{$Up.AbsoluteLink('sitemap')}/{$ClassName}/{$Page.xml}</loc>
|
||||||
|
<% if $LastModified %><lastmod>{$LastModified}</lastmod><% end_if %>
|
||||||
|
</sitemap><% end_loop %>
|
||||||
|
</sitemapindex>
|
@ -0,0 +1,18 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<?xml-stylesheet type='text/xsl' href='{$AbsoluteLink('styleSheet')}'?>
|
||||||
|
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9" xmlns:xhtml="http://www.w3.org/1999/xhtml" xmlns:image="http://www.google.com/schemas/sitemap-image/1.1">
|
||||||
|
<% loop $Items %>
|
||||||
|
<url>
|
||||||
|
<loc>{$AbsoluteLink}</loc>
|
||||||
|
<% if $LastEdited %><lastmod>{$LastEdited.Rfc3339()}</lastmod><% end_if %>
|
||||||
|
<% if $ChangeFrequency %><changefreq>$ChangeFrequency</changefreq><% end_if %>
|
||||||
|
<% if $GooglePriority %><priority>$GooglePriority</priority><% end_if %>
|
||||||
|
<% if $ImagesForSitemap %><% loop $ImagesForSitemap %>
|
||||||
|
<image:image>
|
||||||
|
<image:loc>{$AbsoluteLink}</image:loc>
|
||||||
|
<image:title>{$Title}</image:title>
|
||||||
|
</image:image>
|
||||||
|
<% end_loop %><% end_if %>
|
||||||
|
</url>
|
||||||
|
<% end_loop %>
|
||||||
|
</urlset>
|
91
templates/xml-sitemap.ss
Normal file
91
templates/xml-sitemap.ss
Normal file
@ -0,0 +1,91 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<xsl:stylesheet version="2.0" xmlns:html="http://www.w3.org/TR/REC-html40" xmlns:image="http://www.google.com/schemas/sitemap-image/1.1" xmlns:sitemap="http://www.sitemaps.org/schemas/sitemap/0.9" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
|
||||||
|
<xsl:output method="html" version="1.0" encoding="UTF-8" indent="yes"/>
|
||||||
|
<xsl:template match="/">
|
||||||
|
<html xmlns="http://www.w3.org/1999/xhtml">
|
||||||
|
<head>
|
||||||
|
<title>XML Sitemap</title>
|
||||||
|
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
|
||||||
|
<link rel="stylesheet" href="$resourceURL(wilr/silverstripe-googlesitemaps:css/style.css)" />
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div class="content">
|
||||||
|
<h1 class="content__title">
|
||||||
|
<a href="https://www.silverstripe.org" target="_blank" rel="noopener noreferrer">XML Sitemap</a>
|
||||||
|
</h1>
|
||||||
|
<p class="content__text">
|
||||||
|
This sitemap contains <xsl:value-of select="count(sitemap:urlset/sitemap:url)"/> URLs.
|
||||||
|
</p>
|
||||||
|
<div class="table-wrapper">
|
||||||
|
<table id="sitemap" class="table">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th class="table__cell table__cell--w-65">URL</th>
|
||||||
|
<th class="table__cell table__cell--w-10">Priority</th>
|
||||||
|
<th class="table__cell table__cell--w-10">Change Freq.</th>
|
||||||
|
<th class="table__cell table__cell--w-15">Last Change</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<xsl:variable name="lower" select="'abcdefghijklmnopqrstuvwxyz'"/>
|
||||||
|
<xsl:variable name="upper" select="'ABCDEFGHIJKLMNOPQRSTUVWXYZ'"/>
|
||||||
|
<xsl:for-each select="sitemap:urlset/sitemap:url">
|
||||||
|
<tr>
|
||||||
|
<td class="table__cell">
|
||||||
|
<xsl:variable name="itemURL">
|
||||||
|
<xsl:value-of select="sitemap:loc"/>
|
||||||
|
</xsl:variable>
|
||||||
|
<xsl:variable name="imagesCount" select="count(image:image)"/>
|
||||||
|
<a href="{\$itemURL}">
|
||||||
|
<xsl:value-of select="sitemap:loc"/>
|
||||||
|
</a>
|
||||||
|
<xsl:if test="\$imagesCount > 0">
|
||||||
|
<table class="image-table">
|
||||||
|
<tr>
|
||||||
|
<th class="image-table__cell">Images</th>
|
||||||
|
</tr>
|
||||||
|
<xsl:for-each select="image:image">
|
||||||
|
<xsl:variable name="imageURL">
|
||||||
|
<xsl:value-of select="image:loc"/>
|
||||||
|
</xsl:variable>
|
||||||
|
<tr>
|
||||||
|
<td class="image-table__cell image-table__cell--image">
|
||||||
|
<img class="image-table__image" src="{\$imageURL}" width="60" height="40" />
|
||||||
|
</td>
|
||||||
|
<td class="image-table__cell image-table__cell--text">
|
||||||
|
<span class="image-table__title"><xsl:value-of select="image:title"/></span>
|
||||||
|
<a href="{\$imageURL}">
|
||||||
|
<xsl:value-of select="image:loc"/>
|
||||||
|
</a>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</xsl:for-each>
|
||||||
|
</table>
|
||||||
|
</xsl:if>
|
||||||
|
</td>
|
||||||
|
<td class="table__cell">
|
||||||
|
<xsl:value-of select="concat(sitemap:priority*100,'%')"/>
|
||||||
|
</td>
|
||||||
|
<td class="table__cell">
|
||||||
|
<xsl:value-of select="concat(translate(substring(sitemap:changefreq, 1, 1),concat(\$lower, \$upper),concat(\$upper, \$lower)),substring(sitemap:changefreq, 2))"/>
|
||||||
|
</td>
|
||||||
|
<td class="table__cell">
|
||||||
|
<xsl:value-of select="concat(substring(sitemap:lastmod,0,11),concat(' ', substring(sitemap:lastmod,12,5)))"/>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</xsl:for-each>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
<p class="content__text">
|
||||||
|
Generated by the Silverstripe CMS
|
||||||
|
<a href="https://github.com/wilr/silverstripe-googlesitemaps" target="_blank" rel="noopener noreferrer" title="Silverstripe CMS Google Sitemaps module on Github">Google Sitemaps Module</a>
|
||||||
|
</p>
|
||||||
|
<p class="content__text">
|
||||||
|
More information about XML sitemaps on <a href="https://sitemaps.org" target="_blank" rel="noopener noreferrer">sitemaps.org</a>
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
</xsl:template>
|
||||||
|
</xsl:stylesheet>
|
@ -1,72 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<xsl:stylesheet version="2.0" xmlns:html="http://www.w3.org/TR/REC-html40" xmlns:image="http://www.google.com/schemas/sitemap-image/1.1" xmlns:sitemap="http://www.sitemaps.org/schemas/sitemap/0.9" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
|
|
||||||
<xsl:output method="html" version="1.0" encoding="UTF-8" indent="yes"/>
|
|
||||||
<xsl:template match="/">
|
|
||||||
<html xmlns="http://www.w3.org/1999/xhtml">
|
|
||||||
<head>
|
|
||||||
<title>XML Sitemap</title>
|
|
||||||
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
|
|
||||||
<link rel="stylesheet" href="../../../googlesitemaps/css/style.css" />
|
|
||||||
<script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.7.2/jquery.min.js"></script>
|
|
||||||
<script type="text/javascript" src="../../../googlesitemaps/javascript/jquery.tablesorter.min.js"></script>
|
|
||||||
<script type="text/javascript"><![CDATA[
|
|
||||||
$(document).ready(function() {
|
|
||||||
$("#sitemap").tablesorter( { sortList: [[0,0]],widgets: ['zebra'] } );
|
|
||||||
});
|
|
||||||
]]></script>
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
<div id="content">
|
|
||||||
<h1>
|
|
||||||
<a href="http://silverstripe.org" target="_blank">XML Sitemap
|
|
||||||
<span class="ss_link">→ silverstripe.org</span>
|
|
||||||
</a>
|
|
||||||
</h1>
|
|
||||||
|
|
||||||
<p class="expl">
|
|
||||||
This sitemap contains <xsl:value-of select="count(sitemap:urlset/sitemap:url)"/> URLs.
|
|
||||||
</p>
|
|
||||||
<table id="sitemap" cellpadding="3" class="tablesorter">
|
|
||||||
<thead>
|
|
||||||
<tr>
|
|
||||||
<th width="76%">URL</th>
|
|
||||||
<th width="7%">Priority</th>
|
|
||||||
<th width="7%">Change Freq.</th>
|
|
||||||
<th width="10%">Last Change</th>
|
|
||||||
</tr>
|
|
||||||
</thead>
|
|
||||||
<tbody>
|
|
||||||
<xsl:variable name="lower" select="'abcdefghijklmnopqrstuvwxyz'"/>
|
|
||||||
<xsl:variable name="upper" select="'ABCDEFGHIJKLMNOPQRSTUVWXYZ'"/>
|
|
||||||
<xsl:for-each select="sitemap:urlset/sitemap:url">
|
|
||||||
<tr>
|
|
||||||
<td>
|
|
||||||
<xsl:variable name="itemURL">
|
|
||||||
<xsl:value-of select="sitemap:loc"/>
|
|
||||||
</xsl:variable>
|
|
||||||
<a href="{$itemURL}">
|
|
||||||
<xsl:value-of select="sitemap:loc"/>
|
|
||||||
</a>
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
<xsl:value-of select="concat(sitemap:priority*100,'%')"/>
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
<xsl:value-of select="concat(translate(substring(sitemap:changefreq, 1, 1),concat($lower, $upper),concat($upper, $lower)),substring(sitemap:changefreq, 2))"/>
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
<xsl:value-of select="concat(substring(sitemap:lastmod,0,11),concat(' ', substring(sitemap:lastmod,12,5)))"/>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
</xsl:for-each>
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
<p id="Footer" class="expl">Generated by the SilverStripe
|
|
||||||
<a href="https://github.com/silverstripe-labs/silverstripe-googlesitemaps" target="_blank" title="SilverStripe Google Sitemaps module on Github">Google Sitemaps Module</a>
|
|
||||||
<br />More information about XML sitemaps on <a href="http://sitemaps.org" target="_blank">sitemaps.org</a>.
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
||||||
</xsl:template>
|
|
||||||
</xsl:stylesheet>
|
|
59
templates/xml-sitemapindex.ss
Normal file
59
templates/xml-sitemapindex.ss
Normal file
@ -0,0 +1,59 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<xsl:stylesheet version="2.0" xmlns:html="http://www.w3.org/TR/REC-html40" xmlns:image="http://www.google.com/schemas/sitemap-image/1.1" xmlns:sitemap="http://www.sitemaps.org/schemas/sitemap/0.9" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
|
||||||
|
<xsl:output method="html" version="1.0" encoding="UTF-8" indent="yes"/>
|
||||||
|
<xsl:template match="/">
|
||||||
|
<html xmlns="http://www.w3.org/1999/xhtml">
|
||||||
|
<head>
|
||||||
|
<title>XML Sitemap</title>
|
||||||
|
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
|
||||||
|
<link rel="stylesheet" href="$resourceURL(wilr/silverstripe-googlesitemaps:css/style.css)" />
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div class="content">
|
||||||
|
<h1 class="content__title">
|
||||||
|
<a href="https://www.silverstripe.org" target="_blank" rel="noopener noreferrer">XML Sitemap</a>
|
||||||
|
</h1>
|
||||||
|
<p class="content__text">
|
||||||
|
This sitemap consists of <xsl:value-of select="count(sitemap:sitemapindex/sitemap:sitemap)"/> part(s).
|
||||||
|
</p>
|
||||||
|
<div class="table-wrapper">
|
||||||
|
<table id="sitemapindex" class="table">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th class="table__cell table__cell--w-85">URL</th>
|
||||||
|
<th class="table__cell table__cell--w-15">Last Change</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<xsl:variable name="lower" select="'abcdefghijklmnopqrstuvwxyz'"/>
|
||||||
|
<xsl:variable name="upper" select="'ABCDEFGHIJKLMNOPQRSTUVWXYZ'"/>
|
||||||
|
<xsl:for-each select="sitemap:sitemapindex/sitemap:sitemap">
|
||||||
|
<tr>
|
||||||
|
<td class="table__cell">
|
||||||
|
<xsl:variable name="itemURL">
|
||||||
|
<xsl:value-of select="sitemap:loc"/>
|
||||||
|
</xsl:variable>
|
||||||
|
<a href="{\$itemURL}">
|
||||||
|
<xsl:value-of select="sitemap:loc"/>
|
||||||
|
</a>
|
||||||
|
</td>
|
||||||
|
<td class="table__cell">
|
||||||
|
<xsl:value-of select="concat(substring(sitemap:lastmod,0,11),concat(' ', substring(sitemap:lastmod,12,5)))"/>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</xsl:for-each>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
<p class="content__text">
|
||||||
|
Generated by the Silverstripe CMS
|
||||||
|
<a href="https://github.com/wilr/silverstripe-googlesitemaps" target="_blank" rel="noopener noreferrer" title="SilverStripe Google Sitemaps module on Github">Google Sitemaps Module</a>
|
||||||
|
</p>
|
||||||
|
<p class="content__text">
|
||||||
|
More information about XML sitemaps on <a href="https://sitemaps.org" target="_blank" rel="noopener noreferrer">sitemaps.org</a>
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
</xsl:template>
|
||||||
|
</xsl:stylesheet>
|
@ -1,133 +1,206 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
/**
|
namespace Wilr\GoogleSitemaps\Tests;
|
||||||
* @package googlesitemaps
|
|
||||||
* @subpackage tests
|
|
||||||
*/
|
|
||||||
class GoogleSitemapTest extends FunctionalTest {
|
|
||||||
|
|
||||||
public static $fixture_file = 'googlesitemaps/tests/GoogleSitemapTest.yml';
|
use Exception;
|
||||||
|
use SilverStripe\CMS\Model\SiteTree;
|
||||||
|
use SilverStripe\Core\Config\Config;
|
||||||
|
use SilverStripe\Dev\FunctionalTest;
|
||||||
|
use SilverStripe\Forms\DropdownField;
|
||||||
|
use SilverStripe\Forms\LiteralField;
|
||||||
|
use SilverStripe\Forms\Tab;
|
||||||
|
use SilverStripe\ORM\DB;
|
||||||
|
use SilverStripe\Versioned\Versioned;
|
||||||
|
use Wilr\GoogleSitemaps\Extensions\GoogleSitemapExtension;
|
||||||
|
use Wilr\GoogleSitemaps\GoogleSitemap;
|
||||||
|
use Wilr\GoogleSitemaps\Tests\Model\OtherDataObject;
|
||||||
|
use Wilr\GoogleSitemaps\Tests\Model\TestDataObject;
|
||||||
|
use Wilr\GoogleSitemaps\Tests\Model\UnviewableDataObject;
|
||||||
|
|
||||||
protected $extraDataObjects = array(
|
class GoogleSitemapTest extends FunctionalTest
|
||||||
'GoogleSitemapTest_DataObject',
|
{
|
||||||
'GoogleSitemapTest_OtherDataObject',
|
protected static $fixture_file = [
|
||||||
'GoogleSitemapTest_UnviewableDataObject'
|
'GoogleSitemapTest.yml'
|
||||||
);
|
];
|
||||||
|
|
||||||
public function setUp() {
|
protected $usesDatabase = true;
|
||||||
parent::setUp();
|
|
||||||
|
public static function get_fixture_file()
|
||||||
|
{
|
||||||
|
$files = [__DIR__ . '/GoogleSitemapTest.yml'];
|
||||||
|
|
||||||
if (class_exists('Page')) {
|
if (class_exists('Page')) {
|
||||||
$this->loadFixture('googlesitemaps/tests/GoogleSitemapPageTest.yml');
|
$files[] = __DIR__ . '/GoogleSitemapPageTest.yml';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return $files;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected static $extra_dataobjects = [
|
||||||
|
TestDataObject::class,
|
||||||
|
OtherDataObject::class,
|
||||||
|
UnviewableDataObject::class
|
||||||
|
];
|
||||||
|
|
||||||
|
protected static $extra_extensions = [
|
||||||
|
GoogleSitemapExtension::class
|
||||||
|
];
|
||||||
|
|
||||||
|
protected function setUp(): void
|
||||||
|
{
|
||||||
|
parent::setUp();
|
||||||
|
|
||||||
GoogleSitemap::clear_registered_dataobjects();
|
GoogleSitemap::clear_registered_dataobjects();
|
||||||
GoogleSitemap::clear_registered_routes();
|
GoogleSitemap::clear_registered_routes();
|
||||||
}
|
}
|
||||||
|
|
||||||
public function tearDown() {
|
protected function tearDown(): void
|
||||||
|
{
|
||||||
parent::tearDown();
|
parent::tearDown();
|
||||||
|
|
||||||
GoogleSitemap::clear_registered_dataobjects();
|
GoogleSitemap::clear_registered_dataobjects();
|
||||||
GoogleSitemap::clear_registered_routes();
|
GoogleSitemap::clear_registered_routes();
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testIndexFileWithCustomRoute() {
|
public function testCanIncludeInGoogleSitemap(): void
|
||||||
|
{
|
||||||
|
GoogleSitemap::register_dataobject(TestDataObject::class, '');
|
||||||
|
$used = $this->objFromFixture(TestDataObject::class, 'DataObjectTest2');
|
||||||
|
|
||||||
|
$this->assertTrue($used->canIncludeInGoogleSitemap());
|
||||||
|
|
||||||
|
$used->setPrivate();
|
||||||
|
|
||||||
|
$this->assertFalse($used->canIncludeInGoogleSitemap());
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testIndexFileWithCustomRoute(): void
|
||||||
|
{
|
||||||
GoogleSitemap::register_route('/test/');
|
GoogleSitemap::register_route('/test/');
|
||||||
|
|
||||||
$response = $this->get('sitemap.xml');
|
$response = $this->get('sitemap.xml');
|
||||||
$body = $response->getBody();
|
$body = $response->getBody();
|
||||||
|
|
||||||
$expected = "<loc>". Director::absoluteURL("sitemap.xml/sitemap/GoogleSitemapRoute/1") ."</loc>";
|
$this->assertXmlStringEqualsXmlFile(
|
||||||
$this->assertEquals(1, substr_count($body, $expected) , 'A link to the custom routes exists');
|
__DIR__ . '/xml/' . __FUNCTION__ . '.xml',
|
||||||
|
$body,
|
||||||
|
'A link to the custom routes exists'
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function testGetItems(): void
|
||||||
|
{
|
||||||
|
GoogleSitemap::register_dataobject(TestDataObject::class, '');
|
||||||
|
$google = new GoogleSitemap();
|
||||||
|
|
||||||
public function testGetItems() {
|
$items = $google->getItems(TestDataObject::class, 1);
|
||||||
GoogleSitemap::register_dataobject("GoogleSitemapTest_DataObject", '');
|
|
||||||
|
|
||||||
$items = GoogleSitemap::get_items('GoogleSitemapTest_DataObject', 1);
|
$this->assertEquals(3, $items->count());
|
||||||
$this->assertEquals(2, $items->count());
|
|
||||||
|
|
||||||
$this->assertDOSEquals(array(
|
GoogleSitemap::register_dataobject(OtherDataObject::class);
|
||||||
array("Priority" => "0.2"),
|
$this->assertEquals(1, $google->getItems(OtherDataObject::class, 1)->count());
|
||||||
array("Priority" => "0.4")
|
|
||||||
), $items);
|
|
||||||
|
|
||||||
GoogleSitemap::register_dataobject("GoogleSitemapTest_OtherDataObject");
|
GoogleSitemap::register_dataobject(UnviewableDataObject::class);
|
||||||
$this->assertEquals(1, GoogleSitemap::get_items('GoogleSitemapTest_OtherDataObject', 1)->count());
|
$this->assertEquals(0, $google->getItems(UnviewableDataObject::class, 1)->count());
|
||||||
|
|
||||||
GoogleSitemap::register_dataobject("GoogleSitemapTest_UnviewableDataObject");
|
|
||||||
$this->assertEquals(0, GoogleSitemap::get_items('GoogleSitemapTest_UnviewableDataObject', 1)->count());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testGetItemsWithCustomRoutes() {
|
public function testGetItemsWithCustomRoutes(): void
|
||||||
GoogleSitemap::register_routes(array(
|
{
|
||||||
|
GoogleSitemap::register_routes([
|
||||||
'/test-route/',
|
'/test-route/',
|
||||||
'/someother-route/',
|
'/someother-route/',
|
||||||
'/fake-sitemap-route/'
|
'/fake-sitemap-route/'
|
||||||
));
|
]);
|
||||||
|
|
||||||
$items = GoogleSitemap::get_items('GoogleSitemapRoute', 1);
|
$google = new GoogleSitemap();
|
||||||
|
$items = $google->getItems('GoogleSitemapRoute', 1);
|
||||||
$this->assertEquals(3, $items->count());
|
$this->assertEquals(3, $items->count());
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testAccessingSitemapRootXMLFile() {
|
public function testAccessingSitemapRootXMLFile(): void
|
||||||
GoogleSitemap::register_dataobject("GoogleSitemapTest_DataObject");
|
{
|
||||||
GoogleSitemap::register_dataobject("GoogleSitemapTest_OtherDataObject");
|
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');
|
$response = $this->get('sitemap.xml');
|
||||||
$body = $response->getBody();
|
$body = $response->getBody();
|
||||||
|
|
||||||
// the sitemap should contain <loc> to both those files and not the other
|
$this->assertXmlStringEqualsXmlFile(__DIR__ . '/xml/' . __FUNCTION__ . '.xml', $body);
|
||||||
// dataobject as it hasn't been registered
|
|
||||||
$expected = "<loc>". Director::absoluteURL("sitemap.xml/sitemap/GoogleSitemapTest_DataObject/1") ."</loc>";
|
|
||||||
$this->assertEquals(1, substr_count($body, $expected) , 'A link to GoogleSitemapTest_DataObject exists');
|
|
||||||
|
|
||||||
$expected = "<loc>". Director::absoluteURL("sitemap.xml/sitemap/GoogleSitemapTest_OtherDataObject/1") ."</loc>";
|
|
||||||
$this->assertEquals(1, substr_count($body, $expected) , 'A link to GoogleSitemapTest_OtherDataObject exists');
|
|
||||||
|
|
||||||
$expected = "<loc>". Director::absoluteURL("sitemap.xml/sitemap/GoogleSitemapTest_UnviewableDataObject/2") ."</loc>";
|
|
||||||
$this->assertEquals(0, substr_count($body, $expected) , 'A link to a GoogleSitemapTest_UnviewableDataObject does not exist');
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testLastModifiedDateOnRootXML() {
|
public function testLastModifiedDateOnRootXML(): void
|
||||||
GoogleSitemap::register_dataobject("GoogleSitemapTest_DataObject");
|
{
|
||||||
|
Config::inst()->set(GoogleSitemap::class, 'enabled', true);
|
||||||
|
|
||||||
DB::query("
|
if (!class_exists('Page')) {
|
||||||
UPDATE GoogleSitemapTest_DataObject SET LastEdited = '2012-01-14'"
|
$this->markTestIncomplete('No cms module installed, page related test skipped');
|
||||||
|
}
|
||||||
|
|
||||||
|
$page = $this->objFromFixture('Page', 'Page1');
|
||||||
|
$page->publishSingle();
|
||||||
|
$page->flushCache();
|
||||||
|
|
||||||
|
$p2 = $this->objFromFixture('Page', 'Page2');
|
||||||
|
$p2->publishSingle();
|
||||||
|
$p2->flushCache();
|
||||||
|
|
||||||
|
DB::query("UPDATE \"SiteTree_Live\" SET \"LastEdited\"='2014-03-14 00:00:00' WHERE \"ID\"='" . $page->ID . "'");
|
||||||
|
DB::query("UPDATE \"SiteTree_Live\" SET \"LastEdited\"='2014-01-01 00:00:00' WHERE \"ID\"='" . $p2->ID . "'");
|
||||||
|
|
||||||
|
$response = $this->get('sitemap.xml');
|
||||||
|
$body = $response->getBody();
|
||||||
|
|
||||||
|
$expected = '<lastmod>2014-03-14</lastmod>';
|
||||||
|
|
||||||
|
$this->assertEquals(
|
||||||
|
1,
|
||||||
|
substr_count($body, $expected),
|
||||||
|
'The last mod date should use most recent LastEdited date'
|
||||||
);
|
);
|
||||||
|
|
||||||
$response = $this->get('sitemap.xml');
|
|
||||||
$body = $response->getBody();
|
|
||||||
|
|
||||||
$expected = "<lastmod>2012-01-14</lastmod>";
|
|
||||||
$this->assertEquals(1, substr_count($body, $expected));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testIndexFilePaginatedSitemapFiles() {
|
public function testIndexFilePaginatedSitemapFiles(): void
|
||||||
|
{
|
||||||
$original = Config::inst()->get('GoogleSitemap', 'objects_per_sitemap');
|
$original = Config::inst()->get('GoogleSitemap', 'objects_per_sitemap');
|
||||||
Config::inst()->update('GoogleSitemap', 'objects_per_sitemap', 1);
|
Config::inst()->set(GoogleSitemap::class, 'objects_per_sitemap', 1);
|
||||||
GoogleSitemap::register_dataobject("GoogleSitemapTest_DataObject");
|
GoogleSitemap::register_dataobject(TestDataObject::class);
|
||||||
|
|
||||||
|
|
||||||
|
$obj = $this->objFromFixture(TestDataObject::class, 'DataObjectTest1');
|
||||||
|
$obj1 = $this->objFromFixture(TestDataObject::class, 'DataObjectTest2');
|
||||||
|
$obj2 = $this->objFromFixture(TestDataObject::class, 'UnindexedDataObject');
|
||||||
|
|
||||||
|
$t = $obj->baseTable();
|
||||||
|
DB::query("UPDATE \"" . $t . "\" SET \"LastEdited\"='2023-02-13 00:00:00' WHERE \"ID\"='" . $obj->ID . "'");
|
||||||
|
DB::query("UPDATE \"" . $t . "\" SET \"LastEdited\"='2023-02-13 00:00:00' WHERE \"ID\"='" . $obj1->ID . "'");
|
||||||
|
DB::query("UPDATE \"" . $t . "\" SET \"LastEdited\"='2023-02-13 00:00:00' WHERE \"ID\"='" . $obj2->ID . "'");
|
||||||
|
|
||||||
|
|
||||||
$response = $this->get('sitemap.xml');
|
$response = $this->get('sitemap.xml');
|
||||||
$body = $response->getBody();
|
$body = $response->getBody();
|
||||||
$expected = "<loc>". Director::absoluteURL("sitemap.xml/sitemap/GoogleSitemapTest_DataObject/1") ."</loc>";
|
$this->assertXmlStringEqualsXmlFile(__DIR__ . '/xml/' . __FUNCTION__ . '.xml', $body);
|
||||||
$this->assertEquals(1, substr_count($body, $expected) , 'A link to the first page of GoogleSitemapTest_DataObject exists');
|
|
||||||
|
|
||||||
$expected = "<loc>". Director::absoluteURL("sitemap.xml/sitemap/GoogleSitemapTest_DataObject/2") ."</loc>";
|
Config::inst()->set(GoogleSitemap::class, 'objects_per_sitemap', $original);
|
||||||
$this->assertEquals(1, substr_count($body, $expected) , 'A link to the second page GoogleSitemapTest_DataObject exists');
|
|
||||||
|
|
||||||
Config::inst()->update('GoogleSitemap', 'objects_per_sitemap', $original);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testRegisterRoutesIncludesAllRoutes() {
|
public function testRegisterRoutesIncludesAllRoutes(): void
|
||||||
|
{
|
||||||
GoogleSitemap::register_route('/test/');
|
GoogleSitemap::register_route('/test/');
|
||||||
GoogleSitemap::register_routes(array(
|
GoogleSitemap::register_routes([
|
||||||
'/test/', // duplication should be replaced
|
'/test/', // duplication should be replaced
|
||||||
'/unittests/',
|
'/unittests/',
|
||||||
'/anotherlink/'
|
'/anotherlink/'
|
||||||
), 'weekly');
|
], 'weekly');
|
||||||
|
|
||||||
$response = $this->get('sitemap.xml/sitemap/GoogleSitemapRoute/1');
|
$response = $this->get('sitemap.xml/sitemap/GoogleSitemapRoute/1');
|
||||||
$body = $response->getBody();
|
$body = $response->getBody();
|
||||||
@ -136,73 +209,76 @@ class GoogleSitemapTest extends FunctionalTest {
|
|||||||
$this->assertEquals(3, substr_count($body, "<loc>"));
|
$this->assertEquals(3, substr_count($body, "<loc>"));
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testAccessingNestedSiteMap() {
|
public function testAccessingNestedSiteMap(): void
|
||||||
|
{
|
||||||
$original = Config::inst()->get('GoogleSitemap', 'objects_per_sitemap');
|
$original = Config::inst()->get('GoogleSitemap', 'objects_per_sitemap');
|
||||||
Config::inst()->update('GoogleSitemap', 'objects_per_sitemap', 1);
|
Config::inst()->set(GoogleSitemap::class, 'objects_per_sitemap', 1);
|
||||||
GoogleSitemap::register_dataobject("GoogleSitemapTest_DataObject");
|
GoogleSitemap::register_dataobject(TestDataObject::class);
|
||||||
|
|
||||||
$response = $this->get('sitemap.xml/sitemap/GoogleSitemapTest_DataObject/1');
|
$response = $this->get('sitemap.xml/sitemap/Wilr-GoogleSitemaps-Tests-Model-TestDataObject/1');
|
||||||
$body = $response->getBody();
|
$body = $response->getBody();
|
||||||
|
|
||||||
$this->assertEquals(200, $response->getStatusCode(), 'successful loaded nested sitemap');
|
$this->assertEquals(200, $response->getStatusCode(), 'successful loaded nested sitemap');
|
||||||
|
|
||||||
Config::inst()->update('GoogleSitemap', 'objects_per_sitemap', $original);
|
Config::inst()->set(GoogleSitemap::class, 'objects_per_sitemap', $original);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testGetItemsWithPages() {
|
public function testGetItemsWithPages(): void
|
||||||
if(!class_exists('Page')) {
|
{
|
||||||
|
if (!class_exists(SiteTree::class)) {
|
||||||
$this->markTestIncomplete('No cms module installed, page related test skipped');
|
$this->markTestIncomplete('No cms module installed, page related test skipped');
|
||||||
}
|
}
|
||||||
|
|
||||||
$page = $this->objFromFixture('Page', 'Page1');
|
$page = $this->objFromFixture('Page', 'Page1');
|
||||||
$page->publish('Stage', 'Live');
|
$page->publishSingle();
|
||||||
$page->flushCache();
|
$page->flushCache();
|
||||||
|
|
||||||
$page2 = $this->objFromFixture('Page', 'Page2');
|
$page2 = $this->objFromFixture('Page', 'Page2');
|
||||||
$page2->publish('Stage', 'Live');
|
$page2->publishSingle();
|
||||||
$page2->flushCache();
|
$page2->flushCache();
|
||||||
|
|
||||||
$this->assertDOSContains(array(
|
$this->assertListContains([
|
||||||
array('Title' => 'Testpage1'),
|
['Title' => 'Testpage1'],
|
||||||
array('Title' => 'Testpage2')
|
['Title' => 'Testpage2']
|
||||||
), GoogleSitemap::get_items('SiteTree'), "There should be 2 pages in the sitemap after publishing");
|
], 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
|
// check if we make a page readonly that it is hidden
|
||||||
$page2->CanViewType = 'LoggedInUsers';
|
$page2->CanViewType = 'LoggedInUsers';
|
||||||
$page2->write();
|
$page2->write();
|
||||||
$page2->publish('Stage', 'Live');
|
$page2->publishSingle();
|
||||||
|
|
||||||
$this->session()->inst_set('loggedInAs', null);
|
$this->logOut();
|
||||||
|
|
||||||
$this->assertDOSEquals(array(
|
$this->assertListEquals([
|
||||||
array('Title' => 'Testpage1')
|
['Title' => 'Testpage1']
|
||||||
), GoogleSitemap::get_items('SiteTree'), "There should be only 1 page, other is logged in only");
|
], GoogleSitemap::inst()->getItems(SiteTree::class), "There should be only 1 page, other is logged in only");
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testAccess() {
|
public function testAccess(): void
|
||||||
Config::inst()->update('GoogleSitemap', 'enabled', true);
|
{
|
||||||
|
Config::inst()->set(GoogleSitemap::class, 'enabled', true);
|
||||||
|
|
||||||
$response = $this->get('sitemap.xml');
|
$response = $this->get('sitemap.xml');
|
||||||
|
|
||||||
$this->assertEquals(200, $response->getStatusCode(), 'Sitemap returns a 200 success when enabled');
|
$this->assertEquals(200, $response->getStatusCode(), 'Sitemap returns a 200 success when enabled');
|
||||||
$this->assertEquals('application/xml; charset="utf-8"', $response->getHeader('Content-Type'));
|
$this->assertEquals('application/xml; charset="utf-8"', $response->getHeader('Content-Type'));
|
||||||
|
|
||||||
GoogleSitemap::register_dataobject("GoogleSitemapTest_DataObject");
|
GoogleSitemap::register_dataobject(TestDataObject::class);
|
||||||
$response = $this->get('sitemap.xml/sitemap/GoogleSitemapTest_DataObject/1');
|
$response = $this->get('sitemap.xml/sitemap/Wilr-GoogleSitemaps-Tests-Model-TestDataObject/1');
|
||||||
$this->assertEquals(200, $response->getStatusCode(), 'Sitemap returns a 200 success when enabled');
|
$this->assertEquals(200, $response->getStatusCode(), 'Sitemap returns a 200 success when enabled');
|
||||||
$this->assertEquals('application/xml; charset="utf-8"', $response->getHeader('Content-Type'));
|
$this->assertEquals('application/xml; charset="utf-8"', $response->getHeader('Content-Type'));
|
||||||
|
|
||||||
Config::inst()->remove('GoogleSitemap', 'enabled');
|
Config::inst()->set(GoogleSitemap::class, 'enabled', false);
|
||||||
Config::inst()->update('GoogleSitemap', 'enabled', false);
|
|
||||||
|
|
||||||
$response = $this->get('sitemap.xml');
|
$response = $this->get('sitemap.xml');
|
||||||
$this->assertEquals(404, $response->getStatusCode(), 'Sitemap index returns a 404 when disabled');
|
$this->assertEquals(404, $response->getStatusCode(), 'Sitemap index returns a 404 when disabled');
|
||||||
|
|
||||||
$response = $this->get('sitemap.xml/sitemap/GoogleSitemapTest_DataObject/1');
|
$response = $this->get('sitemap.xml/sitemap/Wilr-GoogleSitemaps-Tests-Model-TestDataObject/1');
|
||||||
$this->assertEquals(404, $response->getStatusCode(), 'Sitemap file returns a 404 when disabled');
|
$this->assertEquals(404, $response->getStatusCode(), 'Sitemap file returns a 404 when disabled');
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testDecoratorAddsFields() {
|
public function testDecoratorAddsFields(): void
|
||||||
|
{
|
||||||
if (!class_exists("Page")) {
|
if (!class_exists("Page")) {
|
||||||
$this->markTestIncomplete('No cms module installed, page related test skipped');
|
$this->markTestIncomplete('No cms module installed, page related test skipped');
|
||||||
}
|
}
|
||||||
@ -212,12 +288,13 @@ class GoogleSitemapTest extends FunctionalTest {
|
|||||||
$fields = $page->getSettingsFields();
|
$fields = $page->getSettingsFields();
|
||||||
$tab = $fields->fieldByName('Root')->fieldByName('Settings')->fieldByName('GoogleSitemap');
|
$tab = $fields->fieldByName('Root')->fieldByName('Settings')->fieldByName('GoogleSitemap');
|
||||||
|
|
||||||
$this->assertInstanceOf('Tab', $tab);
|
$this->assertInstanceOf(Tab::class, $tab);
|
||||||
$this->assertInstanceOf('DropdownField', $tab->fieldByName('Priority'));
|
$this->assertInstanceOf(DropdownField::class, $tab->fieldByName('Priority'));
|
||||||
$this->assertInstanceOf('LiteralField', $tab->fieldByName('GoogleSitemapIntro'));
|
$this->assertInstanceOf(LiteralField::class, $tab->fieldByName('GoogleSitemapIntro'));
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testGetPriority() {
|
public function testGetPriority(): void
|
||||||
|
{
|
||||||
if (!class_exists("Page")) {
|
if (!class_exists("Page")) {
|
||||||
$this->markTestIncomplete('No cms module installed, page related test skipped');
|
$this->markTestIncomplete('No cms module installed, page related test skipped');
|
||||||
}
|
}
|
||||||
@ -228,65 +305,44 @@ class GoogleSitemapTest extends FunctionalTest {
|
|||||||
$page->Priority = 'foo';
|
$page->Priority = 'foo';
|
||||||
$this->assertEquals(0.5, $page->getGooglePriority());
|
$this->assertEquals(0.5, $page->getGooglePriority());
|
||||||
|
|
||||||
|
// custom value (set as string as db field is varchar)
|
||||||
|
$page->Priority = '0.2';
|
||||||
|
$this->assertEquals(0.2, $page->getGooglePriority());
|
||||||
|
|
||||||
// -1 indicates that we should not index this
|
// -1 indicates that we should not index this
|
||||||
$page->Priority = -1;
|
$page->Priority = -1;
|
||||||
$this->assertFalse($page->getGooglePriority());
|
$this->assertFalse($page->getGooglePriority());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function testUnpublishedPage(): void
|
||||||
|
{
|
||||||
|
if (!class_exists('SilverStripe\CMS\Model\SiteTree')) {
|
||||||
|
$this->markTestSkipped('Test skipped; CMS module required for testUnpublishedPage');
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
$orphanedPage = new \SilverStripe\CMS\Model\SiteTree();
|
||||||
* @package googlesitemaps
|
$orphanedPage->ParentID = 999999; // missing parent id
|
||||||
* @subpackage tests
|
$orphanedPage->write();
|
||||||
*/
|
$orphanedPage->publishSingle();
|
||||||
class GoogleSitemapTest_DataObject extends DataObject implements TestOnly {
|
|
||||||
|
|
||||||
public static $db = array(
|
$rootPage = new \SilverStripe\CMS\Model\SiteTree();
|
||||||
'Priority' => 'Varchar(10)'
|
$rootPage->ParentID = 0;
|
||||||
);
|
$rootPage->write();
|
||||||
|
$rootPage->publishSingle();
|
||||||
|
|
||||||
public function canView($member = null) {
|
$oldMode = Versioned::get_reading_mode();
|
||||||
return true;
|
Versioned::set_reading_mode('Live');
|
||||||
}
|
|
||||||
|
|
||||||
public function AbsoluteLink() {
|
try {
|
||||||
return Director::absoluteBaseURL();
|
$this->assertEmpty($orphanedPage->hasPublishedParent());
|
||||||
}
|
$this->assertEmpty($orphanedPage->canIncludeInGoogleSitemap());
|
||||||
}
|
$this->assertNotEmpty($rootPage->hasPublishedParent());
|
||||||
|
$this->assertNotEmpty($rootPage->canIncludeInGoogleSitemap());
|
||||||
/**
|
} catch (Exception $ex) {
|
||||||
* @package googlesitemaps
|
Versioned::set_reading_mode($oldMode);
|
||||||
* @subpackage tests
|
throw $ex;
|
||||||
*/
|
} // finally {
|
||||||
class GoogleSitemapTest_OtherDataObject extends DataObject implements TestOnly {
|
Versioned::set_reading_mode($oldMode);
|
||||||
|
// }
|
||||||
public static $db = array(
|
|
||||||
'Priority' => 'Varchar(10)'
|
|
||||||
);
|
|
||||||
|
|
||||||
public function canView($member = null) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function AbsoluteLink() {
|
|
||||||
return Director::absoluteBaseURL();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @package googlesitemaps
|
|
||||||
* @subpackage tests
|
|
||||||
*/
|
|
||||||
class GoogleSitemapTest_UnviewableDataObject extends DataObject implements TestOnly {
|
|
||||||
|
|
||||||
public static $db = array(
|
|
||||||
'Priority' => 'Varchar(10)'
|
|
||||||
);
|
|
||||||
|
|
||||||
public function canView($member = null) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function AbsoluteLink() {
|
|
||||||
return Director::absoluteBaseURL();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
GoogleSitemapTest_DataObject:
|
Wilr\GoogleSitemaps\Tests\Model\TestDataObject:
|
||||||
DataObjectTest1:
|
DataObjectTest1:
|
||||||
Priority: 0.4
|
Priority: 0.4
|
||||||
DataObjectTest2:
|
DataObjectTest2:
|
||||||
@ -6,10 +6,10 @@ GoogleSitemapTest_DataObject:
|
|||||||
UnindexedDataObject:
|
UnindexedDataObject:
|
||||||
Priority: -1
|
Priority: -1
|
||||||
|
|
||||||
GoogleSitemapTest_OtherDataObject:
|
Wilr\GoogleSitemaps\Tests\Model\OtherDataObject:
|
||||||
OtherDataObjectTest2:
|
OtherDataObjectTest2:
|
||||||
Priority: 0.3
|
Priority: 0.3
|
||||||
|
|
||||||
GoogleSitemapTest_UnviewableDataObject:
|
Wilr\GoogleSitemaps\Tests\Model\UnviewableDataObject:
|
||||||
Unviewable1:
|
Unviewable1:
|
||||||
Priority: 0.4
|
Priority: 0.4
|
24
tests/Model/OtherDataObject.php
Normal file
24
tests/Model/OtherDataObject.php
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Wilr\GoogleSitemaps\Tests\Model;
|
||||||
|
|
||||||
|
use SilverStripe\ORM\DataObject;
|
||||||
|
use SilverStripe\Dev\TestOnly;
|
||||||
|
use SilverStripe\Control\Director;
|
||||||
|
|
||||||
|
class OtherDataObject extends DataObject implements TestOnly
|
||||||
|
{
|
||||||
|
private static $db = array(
|
||||||
|
'Priority' => 'Varchar(10)'
|
||||||
|
);
|
||||||
|
|
||||||
|
public function canView($member = null)
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function AbsoluteLink()
|
||||||
|
{
|
||||||
|
return Director::absoluteBaseURL();
|
||||||
|
}
|
||||||
|
}
|
36
tests/Model/TestDataObject.php
Normal file
36
tests/Model/TestDataObject.php
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Wilr\GoogleSitemaps\Tests\Model;
|
||||||
|
|
||||||
|
use SilverStripe\ORM\DataObject;
|
||||||
|
use SilverStripe\Dev\TestOnly;
|
||||||
|
use SilverStripe\Control\Director;
|
||||||
|
|
||||||
|
class TestDataObject extends DataObject implements TestOnly
|
||||||
|
{
|
||||||
|
protected $private = false;
|
||||||
|
|
||||||
|
private static $db = array(
|
||||||
|
'Priority' => 'Varchar(10)'
|
||||||
|
);
|
||||||
|
|
||||||
|
public function canView($member = null)
|
||||||
|
{
|
||||||
|
if ($this->private) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public function setPrivate()
|
||||||
|
{
|
||||||
|
$this->private = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function AbsoluteLink()
|
||||||
|
{
|
||||||
|
return Director::absoluteBaseURL();
|
||||||
|
}
|
||||||
|
}
|
25
tests/Model/UnviewableDataObject.php
Normal file
25
tests/Model/UnviewableDataObject.php
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Wilr\GoogleSitemaps\Tests\Model;
|
||||||
|
|
||||||
|
use SilverStripe\ORM\DataObject;
|
||||||
|
use SilverStripe\Dev\TestOnly;
|
||||||
|
use SilverStripe\Control\Director;
|
||||||
|
|
||||||
|
class UnviewableDataObject extends DataObject implements TestOnly
|
||||||
|
{
|
||||||
|
|
||||||
|
private static $db = array(
|
||||||
|
'Priority' => 'Varchar(10)'
|
||||||
|
);
|
||||||
|
|
||||||
|
public function canView($member = null)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function AbsoluteLink()
|
||||||
|
{
|
||||||
|
return Director::absoluteBaseURL();
|
||||||
|
}
|
||||||
|
}
|
12
tests/xml/testAccessingSitemapRootXMLFile.xml
Normal file
12
tests/xml/testAccessingSitemapRootXMLFile.xml
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<?xml-stylesheet type='text/xsl' href='http://localhost/sitemap.xml/styleSheetIndex'?>
|
||||||
|
<sitemapindex xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
|
||||||
|
<sitemap>
|
||||||
|
<loc>http://localhost/sitemap.xml/sitemap/Wilr-GoogleSitemaps-Tests-Model-TestDataObject/1</loc>
|
||||||
|
<lastmod>2023-02-13</lastmod>
|
||||||
|
</sitemap>
|
||||||
|
<sitemap>
|
||||||
|
<loc>http://localhost/sitemap.xml/sitemap/Wilr-GoogleSitemaps-Tests-Model-OtherDataObject/1</loc>
|
||||||
|
<lastmod>2023-02-13</lastmod>
|
||||||
|
</sitemap>
|
||||||
|
</sitemapindex>
|
16
tests/xml/testIndexFilePaginatedSitemapFiles.xml
Normal file
16
tests/xml/testIndexFilePaginatedSitemapFiles.xml
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<?xml-stylesheet type='text/xsl' href='http://localhost/sitemap.xml/styleSheetIndex'?>
|
||||||
|
<sitemapindex xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
|
||||||
|
<sitemap>
|
||||||
|
<loc>http://localhost/sitemap.xml/sitemap/Wilr-GoogleSitemaps-Tests-Model-TestDataObject/1</loc>
|
||||||
|
<lastmod>2023-02-13</lastmod>
|
||||||
|
</sitemap>
|
||||||
|
<sitemap>
|
||||||
|
<loc>http://localhost/sitemap.xml/sitemap/Wilr-GoogleSitemaps-Tests-Model-TestDataObject/2</loc>
|
||||||
|
<lastmod>2023-02-13</lastmod>
|
||||||
|
</sitemap>
|
||||||
|
<sitemap>
|
||||||
|
<loc>http://localhost/sitemap.xml/sitemap/Wilr-GoogleSitemaps-Tests-Model-TestDataObject/3</loc>
|
||||||
|
<lastmod>2023-02-13</lastmod>
|
||||||
|
</sitemap>
|
||||||
|
</sitemapindex>
|
7
tests/xml/testIndexFileWithCustomRoute.xml
Normal file
7
tests/xml/testIndexFileWithCustomRoute.xml
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<?xml-stylesheet type='text/xsl' href='http://localhost/sitemap.xml/styleSheetIndex'?>
|
||||||
|
<sitemapindex xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
|
||||||
|
<sitemap>
|
||||||
|
<loc>http://localhost/sitemap.xml/sitemap/GoogleSitemapRoute/1</loc>
|
||||||
|
</sitemap>
|
||||||
|
</sitemapindex>
|
Loading…
Reference in New Issue
Block a user