Compare commits

..

50 Commits
2.1.6 ... main

Author SHA1 Message Date
Will Rossiter
794778ae20
Merge pull request #198 from somardigital/main
BUG - Pass empty string instead of null when no image exists
2024-04-08 16:04:08 +12:00
Sukhwinder Singh
05e6963f04 BUG - Pass empty string instead of null when no image exists 2024-04-08 15:08:56 +12:00
Will Rossiter
3f75c06a77
Merge pull request #197 from silverstripeltd/feature/removal-of-ping
Removal of ping for google site maps
2024-03-26 16:04:23 +13:00
jareddreyerss
a13d3dd9de Removal of ping from google site maps
See https://developers.google.com/search/blog/2023/06/sitemaps-lastmod-ping for context.

Accordingly removed all references to notification config and/or ping.
2024-03-26 14:20:14 +13:00
Will Rossiter
59acd419ef
Merge pull request #194 from sunnysideup/patch-1 2024-02-22 07:24:41 +13:00
Sunny Side Up
aeeb960566
DOC: fixing yml annotation 2024-02-21 20:50:47 +13:00
Will Rossiter
c7f260bb33
Merge pull request #193 from BrookeNZ/main 2024-01-03 07:13:52 +13:00
Brooke Lord
c2f5162e97 Additional styling tweaks 2023-12-29 20:29:05 +13:00
Brooke Lord
59e56c3b69 Update xml-sitemap.ss 2023-12-29 20:20:32 +13:00
Brooke Lord
d9da059a83 Update Silverstripe CMS wording 2023-12-29 17:13:43 +13:00
Brooke Lord
54ddc6ccf7 UI refresh 2023-12-29 17:13:43 +13:00
Will Rossiter
cbccf88a62
chore: lint 2023-10-29 13:53:26 +13:00
Will Rossiter
8a7c8aa9cc
feat: implement Sitemapable (#181) 2023-10-29 13:30:08 +13:00
Will Rossiter
def6be347c
fix: cast variables (#192) 2023-10-28 07:59:55 +13:00
Will Rossiter
523d196d12
Merge pull request #191 from satrun77/pulls/fix-trailing-slash
Ensure trailing slash removed from AbsoluteLink
2023-08-29 16:36:46 +12:00
Mohamed Alsharaf
f064330acd Ensure trailing slash removed from AbsoluteLink
This is to resolve duplicated trailing slash when add_trailing_slash
property is set to true in YML config.

Also, relates to #188
2023-08-29 15:57:24 +12:00
Will Rossiter
f1bddb626d
Merge pull request #190 from visualmetricsio/fix/absolute-link-scope
Fix absolute link scope
2023-08-29 06:16:25 +12:00
Luke Fromhold
77a366871e Fix absolute link scope 2023-08-28 20:33:51 +10:00
Will Rossiter
81b8d870a4
fix: absolute link (188) 2023-07-19 21:41:47 +12:00
Will Rossiter
9b0e0f1064
Merge pull request #179 from td204/2.2.0-fixAlterCanInclude
response was array, need to check boolean value
2023-07-19 21:16:34 +12:00
Will Rossiter
7370c85dae
Merge pull request #189 from tiller1010/hotfix/2.2.2/other-id-type-check 2023-07-14 16:32:29 +12:00
Tyler
bf6156c103 fix: added type check for other id 2023-07-13 15:38:30 -04:00
Will Rossiter
a29de80408
chore: update alias 2023-05-18 19:18:54 +12:00
Will Rossiter
0fbfec9f66
chore: migrate to gh actions from travis 2023-02-13 16:27:48 +13:00
Will Rossiter
54e5043622
Merge pull request #186 from satrun77/pulls/silverstripe-5
Add support for silverstripe 5
2023-02-13 16:23:37 +13:00
Mohamed Alsharaf
286115174d Add support for silverstripe 5 2023-02-13 14:06:48 +13:00
Will Rossiter
cfcb002cf3
Merge pull request #184 from Zazama/main 2022-07-29 05:56:34 +12:00
Jan Metzger
0e965ecd46 Convert in GoogleSitemapController to int, avoid 500 errors on non-nomeric values 2022-07-28 12:28:58 +02:00
Will Rossiter
cb56ac11d9
fix: gracefully handle missing images 2022-07-05 13:27:32 +12:00
Will Rossiter
6adb11b5bf
Merge pull request #181 from evanshunt/main 2021-12-16 07:42:33 +13:00
sinan-evanshunt
8d321bd0a1
Update GoogleSitemapSiteTreeExtension.php 2021-12-15 11:16:30 -07:00
terry
35665402cb response was array, need to check boolean value 2021-08-20 23:07:09 +02:00
Will Rossiter
7326028c7c
MNT dev-main 2021-08-17 14:44:22 +12:00
Will Rossiter
401f723e7e
Merge pull request #177 from lerni/german-translation
add german translation
2021-01-12 07:11:20 +13:00
Will Rossiter
71e62966d0
Merge pull request #176 from lerni/Title-for-Images
Add Title for Images
2021-01-12 07:10:34 +13:00
Lukas Erni
1915f9ab59 add german translation 2021-01-11 14:22:24 +01:00
Lukas Erni
1d535ade02 Add Title for Images 2021-01-11 14:14:44 +01:00
Will Rossiter
56b25a424f
Merge pull request #174 from wilr/pr/172
Handle cases such as 'Class.RelationName'
2020-11-20 09:53:35 +13:00
Will Rossiter
67e8d601d5
Update GoogleSitemapSiteTreeExtension.php 2020-11-20 09:46:56 +13:00
Will Rossiter
67c8ad044e
Handle cases such as 'Class.RelationName' 2020-11-20 09:01:40 +13:00
Will Rossiter
a333b56711
Rename CHANGELOG to CHANGELOG.md 2020-11-19 12:00:42 +13:00
Will Rossiter
745de7a7af
Update CHANGELOG (#171) 2020-11-19 12:00:28 +13:00
Will Rossiter
b5f322646f
Merge pull request #170 from xini/fix-priority-calculations
fix hierarchy based priority calculations
2020-11-19 11:34:29 +13:00
Will Rossiter
312b78f88b
Index manyMany images (Fixes #172) 2020-11-19 11:33:25 +13:00
Florian Thoma
f026001f8b fix number of ancestors (getAncestors by default doesn't return the current page) 2020-09-19 16:22:17 +10:00
Florian Thoma
ced2213be4 fix hierarchy beased priority calculations 2020-09-19 16:16:26 +10:00
Will Rossiter
17b21683a2
Merge pull request #169 from tomaszpirc/patch-1
Create sl.yml
2020-09-11 06:23:40 +12:00
Tomasz Pirc
7668ba8f48
Create sl.yml
Translation to Slovenian
2020-09-10 16:04:01 +02:00
Will Rossiter
4214c091e5
Merge pull request #168 from christopherdarling/patch-1
add .upgrade.yml for SilverStripe upgrader
2020-06-05 15:28:44 +12:00
Christopher Darling
799f669da2
add .upgrade.yml for SilverStripe upgrader 2020-06-03 15:58:13 +01:00
36 changed files with 685 additions and 676 deletions

11
.github/workflows/ci.yml vendored Normal file
View 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
View File

@ -1 +1,4 @@
.DS_Store
/vendor
/public
/composer.lock

View File

@ -1,69 +0,0 @@
inherit: true
checks:
php:
verify_property_names: true
verify_argument_usable_as_reference: true
verify_access_scope_valid: true
useless_calls: true
use_statement_alias_conflict: true
variable_existence: true
unused_variables: true
unused_properties: true
unused_parameters: true
unused_methods: true
unreachable_code: true
too_many_arguments: true
sql_injection_vulnerabilities: true
simplify_boolean_return: true
side_effects_or_types: true
security_vulnerabilities: true
return_doc_comments: true
return_doc_comment_if_not_inferrable: true
require_scope_for_properties: true
require_scope_for_methods: true
require_php_tag_first: true
psr2_switch_declaration: true
psr2_class_declaration: true
property_assignments: true
prefer_while_loop_over_for_loop: true
precedence_mistakes: true
precedence_in_conditions: true
phpunit_assertions: true
php5_style_constructor: true
parse_doc_comments: true
parameter_non_unique: true
parameter_doc_comments: true
param_doc_comment_if_not_inferrable: true
optional_parameters_at_the_end: true
one_class_per_file: true
no_unnecessary_if: true
no_trailing_whitespace: true
no_property_on_interface: true
no_non_implemented_abstract_methods: true
no_error_suppression: true
no_duplicate_arguments: true
no_commented_out_code: true
newline_at_end_of_file: true
missing_arguments: true
method_calls_on_non_object: true
instanceof_class_exists: true
foreach_traversable: true
fix_line_ending: true
fix_doc_comments: true
duplication: true
deprecated_code_usage: true
deadlock_detection_in_loops: true
code_rating: true
closure_use_not_conflicting: true
catch_class_exists: true
blank_line_after_namespace_declaration: false
avoid_multiple_statements_on_same_line: true
avoid_duplicate_types: true
avoid_conflicting_incrementers: true
avoid_closing_tag: true
assignment_of_null_return: true
argument_type_checks: true
filter:
paths: [code/*, tests/*]

View File

@ -1,32 +0,0 @@
language: php
dist: trusty
env:
global:
- COMPOSER_ROOT_VERSION=2.x-dev
matrix:
include:
- php: 7.2
env: DB=MYSQL PHPUNIT_TEST=1 PHPCS_TEST=1
- php: 7.3
env: DB=MYSQL PHPUNIT_COVERAGE_TEST=1
before_script:
# Init PHP
- phpenv rehash
- phpenv config-rm xdebug.ini
- composer validate
- composer require silverstripe/recipe-cms 4.5.x-dev --no-update
- if [[ $DB == PGSQL ]]; then composer require silverstripe/postgresql:2.0.x-dev --no-update; fi
- if [[ $PHPCS_TEST ]]; then composer global require squizlabs/php_codesniffer:^3 --prefer-dist --no-interaction --no-progress --no-suggest -o; fi
- composer install --prefer-dist --no-interaction --no-progress --no-suggest --optimize-autoloader --verbose --profile
script:
- if [[ $PHPUNIT_TEST ]]; then vendor/bin/phpunit tests/; fi
- if [[ $PHPUNIT_COVERAGE_TEST ]]; then phpdbg -qrr vendor/bin/phpunit --coverage-clover=coverage.xml; fi
- if [[ $PHPCS_TEST ]]; then vendor/bin/phpcs src/ tests/ ; fi
after_success:
- if [[ $PHPUNIT_COVERAGE_TEST ]]; then bash <(curl -s https://codecov.io/bash) -f coverage.xml; fi

2
.upgrade.yml Normal file
View File

@ -0,0 +1,2 @@
mappings:
GoogleSitemap: Wilr\GoogleSitemaps\GoogleSitemap

View File

@ -1,5 +0,0 @@
0.1
- Initial Build
0.2
- Moved Decorator to a separate file

View File

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

10
_config.php Normal file
View File

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

View File

@ -4,7 +4,6 @@ Name: googlesitemaps
Wilr\GoogleSitemaps\GoogleSitemap:
enabled: true
objects_per_sitemap: 1000
google_notification_enabled: false
use_show_in_search: true
---
Only:

View File

@ -1,27 +1,34 @@
{
"name": "wilr/silverstripe-googlesitemaps",
"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-vendormodule",
"keywords": ["silverstripe", "googlesitemaps", "seo"],
"homepage": "https://github.com/wilr/silverstripe-googlesitemaps",
"license": "BSD-3-Clause",
"authors": [{
"name": "Will Rossiter",
"email": "will@fullscreen.io"
}],
"require": {
"silverstripe/framework": "^4"
},
"name": "wilr/silverstripe-googlesitemaps",
"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-vendormodule",
"keywords": [
"silverstripe",
"googlesitemaps",
"seo"
],
"homepage": "https://github.com/wilr/silverstripe-googlesitemaps",
"license": "BSD-3-Clause",
"authors": [
{
"name": "Will Rossiter",
"email": "will@fullscreen.io"
}
],
"require": {
"php": "^8.1",
"silverstripe/framework": "^5"
},
"require-dev": {
"phpunit/phpunit": "^5.7",
"phpunit/phpunit": "^9.5",
"squizlabs/php_codesniffer": "^3"
},
"replace": {
"silverstripe/googlesitemaps":"*"
"silverstripe/googlesitemaps": "*"
},
"extra": {
"extra": {
"branch-alias": {
"dev-master": "2.x-dev"
"dev-main": "3.x-dev"
},
"expose": [
"images",
@ -35,5 +42,12 @@
}
},
"prefer-stable": true,
"minimum-stability": "dev"
"minimum-stability": "dev",
"config": {
"allow-plugins": {
"composer/installers": true,
"silverstripe/vendor-plugin": true,
"silverstripe/recipe-plugin": true
}
}
}

View File

@ -1,144 +1,145 @@
*{
margin: 0;
padding: 0;
: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;
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 {
font-family: Helvetica, Arial, sans-serif;
font-size: 14px;
color: #545353;
color: #005A92;
background: #B0BEC7;
padding: 20px;
}
#content {
margin: 0 auto;
max-width: 1200px;
background: #fff;
padding: 20px 30px;
-webkit-box-shadow: 2px 2px 5px 1px rgba(0, 0, 0, 0.2);
box-shadow: 2px 2px 5px 1px rgba(0, 0, 0, 0.2);
-webkit-border-radius: 5px;
border-radius: 5px;
}
h1{
font-size: 20px;
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;
text-decoration: none;
}
h1 .ss_link{
visibility: hidden;
font-size: 9px;
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;
background: var(--color-white);
color: var(--color-grey-dark);
}
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 {
border-collapse: collapse;
color: var(--color-black);
text-align: left;
width: 100%;
}
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");
a {
color: inherit;
text-decoration: none;
}
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;
}
table.imagestable,
table.imagestable a {
font-size: 8pt;
}
table.imagestable {
margin-top: 15px;
}
table.imagestable tr.odd {
background: transparent;
}
table.imagestable tr.even {
background: transparent;
.content {
margin: 0 auto;
max-width: 122rem;
padding: 2rem;
}
#Footer{
margin: 50px 0 10px;
text-align: right;
font-size: 0.8em;
.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;
}

View File

@ -1,23 +1,23 @@
# 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
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.
Therefore, all Silverstripe websites contain a special controller which can be
visited: http://yoursite.com/sitemap.xml. This is not a file directly, but
Therefore, all Silverstripe CMS websites contain a special controller which can
be visited: http://yoursite.com/sitemap.xml. This is not a file directly, but
rather a custom route which points to the GoogleSitemap controller.
See http://en.wikipedia.org/wiki/Sitemaps for info on the Google Sitemap
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.
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.
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
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).
@ -27,45 +27,32 @@ manually, including requesting to have the page excluded from the sitemap.
## Configuration
Most module configuration is done via the SilverStripe Config API. Create a new
config file `mysite/_config/googlesitemaps.yml` with the following outline:
Most module configuration is done via the Silverstripe CMS Config API. Create a
new config file `mysite/_config/googlesitemaps.yml` with the following outline:
---
Name: customgooglesitemaps
After: googlesitemaps
---
Wilr\GoogleSitemaps\GoogleSitemap:
enabled: true
objects_per_sitemap: 1000
google_notification_enabled: false
use_show_in_search: true
```yml
---
Name: customgooglesitemaps
After: googlesitemaps
---
Wilr\GoogleSitemaps\GoogleSitemap:
enabled: true
objects_per_sitemap: 1000
use_show_in_search: true
```
You can now alter any of those properties to set your needs. A popular option
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
You can now alter any of those properties to set your needs.
---
Name: customgooglesitemaps
After: googlesitemaps
---
Wilr\GoogleSitemaps\GoogleSitemap:
enabled: true
objects_per_sitemap: 1000
google_notification_enabled: true
use_show_in_search: true
### Bing Ping Support
To ping Bing whenever your sitemap is updated, set `bing_notification_enabled`
---
Name: customgooglesitemaps
After: googlesitemaps
---
Wilr\GoogleSitemaps\GoogleSitemap:
enabled: true
bing_notification_enabled: true
```yml
---
Name: customgooglesitemaps
After: googlesitemaps
---
Wilr\GoogleSitemaps\GoogleSitemap:
enabled: true
objects_per_sitemap: 1000
use_show_in_search: true
```
### Including DataObjects
@ -76,41 +63,40 @@ database as DataObject subclasses.
To include a DataObject instance in the Sitemap it requires that your subclass
defines two functions:
* AbsoluteLink() function which returns the URL for this DataObject
* canView() function which returns a boolean value.
- AbsoluteLink() function which returns the URL for this DataObject
- canView() function which returns a boolean value.
The following is a barebones example of a DataObject called 'MyDataObject'. It
assumes that you have a controller called 'MyController' which has a show method
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) {
return true;
}
function canView($member = null) {
return true;
}
function AbsoluteLink() {
return Director::absoluteURL($this->Link());
}
function Link() {
return 'MyController/show/'. $this->ID;
}
}
function AbsoluteLink() {
return Director::absoluteURL($this->Link());
}
function Link() {
return 'MyController/show/'. $this->ID;
}
}
After those methods have been defined on your DataObject you now need to tell
the Google Sitemaps module that it should be listed in the sitemap.xml file. To
do that, include the following in your _config.php file.
do that, include the following in your \_config.php file.
use Wilr\GoogleSitemaps\GoogleSitemap;
GoogleSitemap::register_dataobject('MyDataObject');
GoogleSitemap::register_dataobject('MyDataObject');
If you need to change the frequency of the indexing, you can pass the change
frequency (daily, weekly, monthly) as a second parameter to register_dataobject(), So
@ -118,7 +104,7 @@ 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:
@ -126,15 +112,33 @@ http://www.silvercart.org/blog/dataobjects-and-googlesitemaps/
### 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
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(
'/my-custom-controller/',
'/Security/',
'/Security/login/'
));
GoogleSitemap::register_routes(array(
'/my-custom-controller/',
'/Security/',
'/Security/login/'
));
### Sitemapable
For automatic registration of a DataObject subclass, implement the `Sitemapable`
extension.
```
<?php
class MyDataObject extends DataObject implements Sitemapable
{
public function AbsoluteLink()
{
// ..
}
}
```

Binary file not shown.

Before

Width:  |  Height:  |  Size: 54 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 64 B

4
images/cms_logo.svg Normal file
View 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

Binary file not shown.

Before

Width:  |  Height:  |  Size: 54 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.1 KiB

9
lang/de.yml Normal file
View 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/sl.yml Normal file
View 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'

View File

@ -1,9 +1,10 @@
<?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" >
<rule ref="PSR2">
<!-- Current exclusions -->
<exclude name="PSR1.Methods.CamelCapsMethodName" />
</rule>

View File

@ -1,14 +1,14 @@
<phpunit bootstrap="vendor/silverstripe/framework/tests/bootstrap.php" colors="true">
<testsuite name="Default">
<directory>tests</directory>
</testsuite>
<filter>
<whitelist addUncoveredFilesFromWhitelist="true">
<directory suffix=".php">src/</directory>
<exclude>
<directory suffix=".php">tests/</directory>
</exclude>
</whitelist>
</filter>
<?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>

View File

@ -4,9 +4,8 @@ namespace Wilr\GoogleSitemaps\Control;
use SilverStripe\CMS\Model\SiteTree;
use SilverStripe\Control\Controller;
use SilverStripe\Core\Config\Config;
use SilverStripe\Control\Director;
use SilverStripe\Control\HTTPResponse;
use SilverStripe\Core\Manifest\ModuleResourceLoader;
use Wilr\GoogleSitemaps\GoogleSitemap;
use SilverStripe\View\ArrayData;
@ -52,7 +51,7 @@ class GoogleSitemapController extends Controller
$this->extend('updateGoogleSitemaps', $sitemaps);
return $this->customise(new ArrayData([
'Sitemaps' => $sitemaps
'Sitemaps' => $sitemaps,
]))->renderWith(__CLASS__);
} else {
return new HTTPResponse('Page not found', 404);
@ -68,11 +67,17 @@ class GoogleSitemapController extends Controller
public function sitemap()
{
$class = $this->unsanitiseClassName($this->request->param('ID'));
$page = $this->request->param('OtherID');
$page = intval($this->request->param('OtherID'));
if ($page) {
if (!is_numeric($page)) {
return new HTTPResponse('Page not found', 404);
}
}
if (GoogleSitemap::enabled()
&& $class
&& $page
&& ($page > 0)
&& ($class == SiteTree::class || $class == 'GoogleSitemapRoute' || GoogleSitemap::is_registered($class))
) {
$this->getResponse()->addHeader('Content-Type', 'application/xml; charset="utf-8"');
@ -95,7 +100,7 @@ class GoogleSitemapController extends Controller
*/
protected function unsanitiseClassName($class)
{
return str_replace('-', '\\', $class);
return str_replace('-', '\\', (string) $class);
}
/**
@ -123,4 +128,10 @@ class GoogleSitemapController extends Controller
return $html;
}
public function AbsoluteLink($action = null)
{
return rtrim(Controller::join_links(Director::absoluteBaseURL(), 'sitemap.xml', $action), '/');
}
}

View File

@ -33,7 +33,7 @@ class GoogleSitemapExtension extends DataExtension
}
}
$objHttp = parse_url($this->owner->AbsoluteLink(), PHP_URL_HOST);
$objHttp = parse_url($this->owner->AbsoluteLink() ?? '', PHP_URL_HOST);
if ($objHttp != $hostHttp) {
$can = false;
@ -66,27 +66,13 @@ class GoogleSitemapExtension extends DataExtension
}
}
if (is_array($can) && isset($can[0])) {
return $can[0];
}
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.

View File

@ -8,6 +8,7 @@ use SilverStripe\Forms\DropdownField;
use SilverStripe\Forms\LiteralField;
use SilverStripe\Forms\Tab;
use SilverStripe\ORM\ArrayList;
use Throwable;
class GoogleSitemapSiteTreeExtension extends GoogleSitemapExtension
{
@ -103,6 +104,10 @@ class GoogleSitemapSiteTreeExtension extends GoogleSitemapExtension
$result = parent::canIncludeInGoogleSitemap();
$result = ($this->owner instanceof ErrorPage) ? false : $result;
if (is_array($result) && isset($result[0])) {
return $result[0];
}
return $result;
}
@ -116,7 +121,7 @@ class GoogleSitemapSiteTreeExtension extends GoogleSitemapExtension
if (!$priority) {
$parentStack = $this->owner->getAncestors();
$numParents = is_array($parentStack) ? count($parentStack) - 1 : 0;
$numParents = $parentStack->count();
$num = max(0.1, 1.0 - ($numParents / 10));
$result = str_replace(",", ".", $num);
@ -135,13 +140,21 @@ class GoogleSitemapSiteTreeExtension extends GoogleSitemapExtension
$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);
if ($image && $image->exists() && !isset($cachedImages[$image->ID])) {
$cachedImages[$image->ID] = true;
try {
if ($image && $image->exists() && !isset($cachedImages[$image->ID])) {
$cachedImages[$image->ID] = true;
$list->push($image);
$list->push($image);
}
} catch (Throwable $e) {
//
}
}
}
@ -151,10 +164,48 @@ class GoogleSitemapSiteTreeExtension extends GoogleSitemapExtension
$images = $this->owner->getComponents($field);
foreach ($images as $image) {
if ($image && $image->exists() && !isset($cachedImages[$image->ID])) {
$cachedImages[$image->ID] = true;
try {
if ($image && $image->exists() && !isset($cachedImages[$image->ID])) {
$cachedImages[$image->ID] = true;
$list->push($image);
$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) {
//
}
}
}

View File

@ -29,17 +29,8 @@ use ReflectionException;
* 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.
*
* To Enable notification of Google after every publish set google_notification_enabled
* to true in the googlesitemaps.yml config file.
*
* This file is usually located in the _config folder of your project folder.
* e.g mysite/_config/googlesitemaps.yml
* The config file is usually located in the _config folder of your project folder.
* e.g. app/_config/googlesitemaps.yml
*
* <example>
* ---
@ -49,7 +40,6 @@ use ReflectionException;
* Wilr\GoogleSitemaps\GoogleSitemap:
* enabled: true
* objects_per_sitemap: 1000
* google_notification_enabled: true
* use_show_in_search: true
* </example>
*
@ -84,16 +74,6 @@ class GoogleSitemap
*/
private static $exclude_redirector_pages = true;
/**
* @config
*
* @var array
*/
private static $search_indexes = [
'google' => 'http://www.google.com/webmasters/sitemaps/ping?sitemap=',
'bing' => 'http://www.bing.com/ping?sitemap=',
];
/**
* Decorates the given DataObject with {@link GoogleSitemapDecorator}
* and pushes the class name to the registered DataObjects.
@ -236,7 +216,8 @@ class GoogleSitemap
*/
public function getItems($class, $page = 1)
{
//normalise the class name
$page = (int) $page;
try {
$reflectionClass = new ReflectionClass($class);
$class = $reflectionClass->getName();
@ -246,9 +227,9 @@ class GoogleSitemap
}
$output = new ArrayList();
$count = Config::inst()->get(__CLASS__, 'objects_per_sitemap');
$filter = Config::inst()->get(__CLASS__, 'use_show_in_search');
$redirector = Config::inst()->get(__CLASS__, 'exclude_redirector_pages');
$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
@ -390,7 +371,7 @@ class GoogleSitemap
for ($i = 1; $i <= $neededForPage; $i++) {
$lastEdited = $instances
->limit($countPerFile, ($i - 1) * $countPerFile)
->sort(array())
->sort(null)
->max('LastEdited');
$lastModified = ($lastEdited) ? date('Y-m-d', strtotime($lastEdited)) : date('Y-m-d');
@ -416,7 +397,7 @@ class GoogleSitemap
->limit($countPerFile, ($i - 1) * $countPerFile)
->last();
$lastModified = ($sliced) ? date('Y-m-d', strtotime($sliced->LastEdited)): date('Y-m-d');
$lastModified = ($sliced) ? date('Y-m-d', strtotime($sliced->LastEdited)) : date('Y-m-d');
$sitemaps->push(new ArrayData(array(
'ClassName' => $this->sanitiseClassName($class),
@ -452,41 +433,6 @@ class GoogleSitemap
return static::inst()->getSitemaps();
}
/**
* Notifies search indexes 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 enabled search indexes of the update.
*
* If the site is in development mode no ping will be sent.
*
* @return boolean
*/
public static function ping()
{
if (!self::enabled() || Director::isDev()) {
return false;
}
$location = urlencode(Controller::join_links(
Director::absoluteBaseURL(),
'sitemap.xml'
));
$response = true;
foreach (self::config()->search_indexes as $name => $url) {
$configName = $name . '_notification_enabled';
if (self::config()->$configName) {
$response = $response && file_get_contents($url . $location);
}
}
return $response;
}
/**
* Is GoogleSitemap enabled?
*
@ -515,6 +461,6 @@ class GoogleSitemap
*/
protected function sanitiseClassName($class)
{
return str_replace('\\', '-', $class);
return str_replace('\\', '-', (string) $class);
}
}

14
src/Sitemapable.php Normal file
View File

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

View File

@ -1,8 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet type='text/xsl' href='{$BaseHref}sitemap.xml/styleSheetIndex'?>
<?xml-stylesheet type='text/xsl' href='{$AbsoluteLink('styleSheetIndex')}'?>
<sitemapindex xmlns="http://www.sitemaps.org/schemas/sitemap/0.9"><% loop Sitemaps %>
<sitemap>
<loc>{$AbsoluteBaseURL}sitemap.xml/sitemap/$ClassName/$Page.xml</loc>
<% if $LastModified %><lastmod>$LastModified</lastmod><% end_if %>
</sitemap><% end_loop %>
<sitemap>
<loc>{$Up.AbsoluteLink('sitemap')}/{$ClassName}/{$Page.xml}</loc>
<% if $LastModified %><lastmod>{$LastModified}</lastmod><% end_if %>
</sitemap><% end_loop %>
</sitemapindex>

View File

@ -1,15 +1,16 @@
<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet type='text/xsl' href='{$BaseHref}sitemap.xml/styleSheet'?>
<?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 %>
<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>

View File

@ -9,81 +9,80 @@
<link rel="stylesheet" href="$resourceURL(wilr/silverstripe-googlesitemaps:css/style.css)" />
</head>
<body>
<div id="content">
<h1>
<a href="http://silverstripe.org" target="_blank">XML Sitemap
<span class="ss_link">&#8594; silverstripe.org</span>
</a>
<div class="content">
<h1 class="content__title">
<a href="https://www.silverstripe.org" target="_blank" rel="noopener noreferrer">XML Sitemap</a>
</h1>
<p class="expl">
<p class="content__text">
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">
<div class="table-wrapper">
<table id="sitemap" class="table">
<thead>
<tr>
<td>
<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 &gt; 0">
<table class="imagestable" cellpadding="0" cellspacing="0">
<tr>
<th>Images</th>
</tr>
<xsl:for-each select="image:image">
<xsl:variable name="imageURL">
<xsl:value-of select="image:loc"/>
</xsl:variable>
<tr>
<td>
<img src="{\$imageURL}" width="40px"/>
</td>
<td>
<a href="{\$imageURL}">
<xsl:value-of select="image:loc"/>
</a>
</td>
</tr>
</xsl:for-each>
</table>
</xsl:if>
</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>
<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>
</xsl:for-each>
</tbody>
</table>
<p id="Footer" class="expl">Generated by the SilverStripe
<a href="https://github.com/wilr/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>.
</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 &gt; 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>

View File

@ -9,46 +9,48 @@
<link rel="stylesheet" href="$resourceURL(wilr/silverstripe-googlesitemaps:css/style.css)" />
</head>
<body>
<div id="content">
<h1>
<a href="http://silverstripe.org" target="_blank">XML Sitemap
<span class="ss_link">&#8594; silverstripe.org</span>
</a>
<div class="content">
<h1 class="content__title">
<a href="https://www.silverstripe.org" target="_blank" rel="noopener noreferrer">XML Sitemap</a>
</h1>
<p class="expl">
<p class="content__text">
This sitemap consists of <xsl:value-of select="count(sitemap:sitemapindex/sitemap:sitemap)"/> part(s).
</p>
<table id="sitemapindex" cellpadding="3" class="tablesorter">
<thead>
<tr>
<th width="90%">URL</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:sitemapindex/sitemap:sitemap">
<div class="table-wrapper">
<table id="sitemapindex" class="table">
<thead>
<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(substring(sitemap:lastmod,0,11),concat(' ', substring(sitemap:lastmod,12,5)))"/>
</td>
<th class="table__cell table__cell--w-85">URL</th>
<th class="table__cell table__cell--w-15">Last Change</th>
</tr>
</xsl:for-each>
</tbody>
</table>
<p id="Footer" class="expl">Generated by the SilverStripe
<a href="https://github.com/wilr/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>.
</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>

View File

@ -4,7 +4,6 @@ namespace Wilr\GoogleSitemaps\Tests;
use Exception;
use SilverStripe\CMS\Model\SiteTree;
use SilverStripe\Control\Director;
use SilverStripe\Core\Config\Config;
use SilverStripe\Dev\FunctionalTest;
use SilverStripe\Forms\DropdownField;
@ -20,7 +19,22 @@ use Wilr\GoogleSitemaps\Tests\Model\UnviewableDataObject;
class GoogleSitemapTest extends FunctionalTest
{
protected static $fixture_file = 'GoogleSitemapTest.yml';
protected static $fixture_file = [
'GoogleSitemapTest.yml'
];
protected $usesDatabase = true;
public static function get_fixture_file()
{
$files = [__DIR__ . '/GoogleSitemapTest.yml'];
if (class_exists('Page')) {
$files[] = __DIR__ . '/GoogleSitemapPageTest.yml';
}
return $files;
}
protected static $extra_dataobjects = [
TestDataObject::class,
@ -32,19 +46,15 @@ class GoogleSitemapTest extends FunctionalTest
GoogleSitemapExtension::class
];
public function setUp()
protected function setUp(): void
{
parent::setUp();
if (class_exists('Page')) {
$this->loadFixture($this->resolveFixturePath('GoogleSitemapPageTest.yml'));
}
GoogleSitemap::clear_registered_dataobjects();
GoogleSitemap::clear_registered_routes();
}
public function tearDown()
protected function tearDown(): void
{
parent::tearDown();
@ -52,117 +62,100 @@ class GoogleSitemapTest extends FunctionalTest
GoogleSitemap::clear_registered_routes();
}
public function testCanIncludeInGoogleSitemap()
public function testCanIncludeInGoogleSitemap(): void
{
GoogleSitemap::register_dataobject(TestDataObject::class, '');
$unused = $this->objFromFixture(TestDataObject::class, 'UnindexedDataObject');
$this->assertFalse($unused->canIncludeInGoogleSitemap());
$used = $this->objFromFixture(TestDataObject::class, 'DataObjectTest2');
$this->assertTrue($used->canIncludeInGoogleSitemap());
$used->setPrivate();
$this->assertFalse($used->canIncludeInGoogleSitemap());
}
public function testIndexFileWithCustomRoute()
public function testIndexFileWithCustomRoute(): void
{
GoogleSitemap::register_route('/test/');
$response = $this->get('sitemap.xml');
$body = $response->getBody();
$expected = "<loc>". Director::absoluteURL("sitemap.xml/sitemap/GoogleSitemapRoute/1") ."</loc>";
$this->assertEquals(1, substr_count($body, $expected), 'A link to the custom routes exists');
$this->assertXmlStringEqualsXmlFile(
__DIR__ . '/xml/' . __FUNCTION__ . '.xml',
$body,
'A link to the custom routes exists'
);
}
public function testGetItems()
public function testGetItems(): void
{
GoogleSitemap::register_dataobject(TestDataObject::class, '');
$google = new GoogleSitemap();
$items = GoogleSitemap::get_items(TestDataObject::class, 1);
$this->assertEquals(2, $items->count());
$items = $google->getItems(TestDataObject::class, 1);
$this->assertDOSEquals(array(
array("Priority" => "0.2"),
array("Priority" => "0.4")
), $items);
$this->assertEquals(3, $items->count());
GoogleSitemap::register_dataobject(OtherDataObject::class);
$this->assertEquals(1, GoogleSitemap::get_items(OtherDataObject::class, 1)->count());
$this->assertEquals(1, $google->getItems(OtherDataObject::class, 1)->count());
GoogleSitemap::register_dataobject(UnviewableDataObject::class);
$this->assertEquals(0, GoogleSitemap::get_items(UnviewableDataObject::class, 1)->count());
$this->assertEquals(0, $google->getItems(UnviewableDataObject::class, 1)->count());
}
public function testGetItemsWithCustomRoutes()
public function testGetItemsWithCustomRoutes(): void
{
GoogleSitemap::register_routes(array(
GoogleSitemap::register_routes([
'/test-route/',
'/someother-route/',
'/fake-sitemap-route/'
));
]);
$items = GoogleSitemap::get_items('GoogleSitemapRoute', 1);
$google = new GoogleSitemap();
$items = $google->getItems('GoogleSitemapRoute', 1);
$this->assertEquals(3, $items->count());
}
public function testAccessingSitemapRootXMLFile()
public function testAccessingSitemapRootXMLFile(): void
{
GoogleSitemap::register_dataobject(TestDataObject::class);
GoogleSitemap::register_dataobject(OtherDataObject::class);
$obj = $this->objFromFixture(TestDataObject::class, 'DataObjectTest1');
$table = $obj->baseTable();
DB::query("UPDATE \"" . $table . "\" SET \"LastEdited\"='2023-02-13 00:00:00'");
$obj2 = $this->objFromFixture(OtherDataObject::class, 'OtherDataObjectTest2');
$table = $obj2->baseTable();
DB::query("UPDATE \"" . $table . "\" SET \"LastEdited\"='2023-02-13 00:00:00'");
$response = $this->get('sitemap.xml');
$body = $response->getBody();
// the sitemap should contain <loc> to both those files and not the other
// dataobject as it hasn't been registered
$expected = "<loc>". Director::absoluteURL(
"sitemap.xml/sitemap/Wilr-GoogleSitemaps-Tests-Model-TestDataObject/1"
) ."</loc>";
$this->assertEquals(
1,
substr_count($body, $expected),
'A link to GoogleSitemapTest_DataObject exists'
);
$expected = "<loc>". Director::absoluteURL(
"sitemap.xml/sitemap/Wilr-GoogleSitemaps-Tests-Model-OtherDataObject/1"
) ."</loc>";
$this->assertEquals(
1,
substr_count($body, $expected),
'A link to GoogleSitemapTest_OtherDataObject exists'
);
$expected = "<loc>". Director::absoluteURL(
"sitemap.xml/sitemap/Wilr-GoogleSitemaps-Tests-Model-UnviewableDataObject/2"
) ."</loc>";
$this->assertEquals(
0,
substr_count($body, $expected),
'A link to a GoogleSitemapTest_UnviewableDataObject does not exist'
);
$this->assertXmlStringEqualsXmlFile(__DIR__ . '/xml/' . __FUNCTION__ . '.xml', $body);
}
public function testLastModifiedDateOnRootXML()
public function testLastModifiedDateOnRootXML(): void
{
Config::inst()->update(GoogleSitemap::class, 'enabled', true);
Config::inst()->set(GoogleSitemap::class, 'enabled', true);
if (!class_exists('Page')) {
$this->markTestIncomplete('No cms module installed, page related test skipped');
}
$page = $this->objFromFixture('Page', 'Page1');
$page->publish('Stage', 'Live');
$page->publishSingle();
$page->flushCache();
$page2 = $this->objFromFixture('Page', 'Page2');
$page2->publish('Stage', 'Live');
$page2->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\"='".$page2->ID."'");
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();
@ -176,43 +169,38 @@ class GoogleSitemapTest extends FunctionalTest
);
}
public function testIndexFilePaginatedSitemapFiles()
public function testIndexFilePaginatedSitemapFiles(): void
{
$original = Config::inst()->get('GoogleSitemap', 'objects_per_sitemap');
Config::inst()->update(GoogleSitemap::class, 'objects_per_sitemap', 1);
Config::inst()->set(GoogleSitemap::class, 'objects_per_sitemap', 1);
GoogleSitemap::register_dataobject(TestDataObject::class);
$obj = $this->objFromFixture(TestDataObject::class, 'DataObjectTest1');
$obj1 = $this->objFromFixture(TestDataObject::class, 'DataObjectTest2');
$obj2 = $this->objFromFixture(TestDataObject::class, 'UnindexedDataObject');
$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');
$body = $response->getBody();
$expected = "<loc>". Director::absoluteURL(
"sitemap.xml/sitemap/Wilr-GoogleSitemaps-Tests-Model-TestDataObject/1"
) ."</loc>";
$this->assertEquals(
1,
substr_count($body, $expected),
'A link to the first page of GoogleSitemapTest_DataObject exists'
);
$this->assertXmlStringEqualsXmlFile(__DIR__ . '/xml/' . __FUNCTION__ . '.xml', $body);
$expected = "<loc>". Director::absoluteURL(
"sitemap.xml/sitemap/Wilr-GoogleSitemaps-Tests-Model-TestDataObject/2"
) ."</loc>";
$this->assertEquals(
1,
substr_count($body, $expected),
'A link to the second page GoogleSitemapTest_DataObject exists'
);
Config::inst()->update(GoogleSitemap::class, 'objects_per_sitemap', $original);
Config::inst()->set(GoogleSitemap::class, 'objects_per_sitemap', $original);
}
public function testRegisterRoutesIncludesAllRoutes()
public function testRegisterRoutesIncludesAllRoutes(): void
{
GoogleSitemap::register_route('/test/');
GoogleSitemap::register_routes(array(
GoogleSitemap::register_routes([
'/test/', // duplication should be replaced
'/unittests/',
'/anotherlink/'
), 'weekly');
], 'weekly');
$response = $this->get('sitemap.xml/sitemap/GoogleSitemapRoute/1');
$body = $response->getBody();
@ -221,10 +209,10 @@ class GoogleSitemapTest extends FunctionalTest
$this->assertEquals(3, substr_count($body, "<loc>"));
}
public function testAccessingNestedSiteMap()
public function testAccessingNestedSiteMap(): void
{
$original = Config::inst()->get('GoogleSitemap', 'objects_per_sitemap');
Config::inst()->update(GoogleSitemap::class, 'objects_per_sitemap', 1);
Config::inst()->set(GoogleSitemap::class, 'objects_per_sitemap', 1);
GoogleSitemap::register_dataobject(TestDataObject::class);
$response = $this->get('sitemap.xml/sitemap/Wilr-GoogleSitemaps-Tests-Model-TestDataObject/1');
@ -232,43 +220,43 @@ class GoogleSitemapTest extends FunctionalTest
$this->assertEquals(200, $response->getStatusCode(), 'successful loaded nested sitemap');
Config::inst()->update(GoogleSitemap::class, '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');
}
$page = $this->objFromFixture('Page', 'Page1');
$page->publish('Stage', 'Live');
$page->publishSingle();
$page->flushCache();
$page2 = $this->objFromFixture('Page', 'Page2');
$page2->publish('Stage', 'Live');
$page2->publishSingle();
$page2->flushCache();
$this->assertDOSContains(array(
array('Title' => 'Testpage1'),
array('Title' => 'Testpage2')
), GoogleSitemap::inst()->getItems(SiteTree::class), "There should be 2 pages in the sitemap after publishing");
$this->assertListContains([
['Title' => 'Testpage1'],
['Title' => 'Testpage2']
], GoogleSitemap::inst()->getItems(SiteTree::class), "There should be 2 pages in the sitemap after publishing");
// check if we make a page readonly that it is hidden
$page2->CanViewType = 'LoggedInUsers';
$page2->write();
$page2->publish('Stage', 'Live');
$page2->publishSingle();
$this->logOut();
$this->assertDOSEquals(array(
array('Title' => 'Testpage1')
), GoogleSitemap::inst()->getItems(SiteTree::class), "There should be only 1 page, other is logged in only");
$this->assertListEquals([
['Title' => 'Testpage1']
], GoogleSitemap::inst()->getItems(SiteTree::class), "There should be only 1 page, other is logged in only");
}
public function testAccess()
public function testAccess(): void
{
Config::inst()->update(GoogleSitemap::class, 'enabled', true);
Config::inst()->set(GoogleSitemap::class, 'enabled', true);
$response = $this->get('sitemap.xml');
@ -280,7 +268,7 @@ class GoogleSitemapTest extends FunctionalTest
$this->assertEquals(200, $response->getStatusCode(), 'Sitemap returns a 200 success when enabled');
$this->assertEquals('application/xml; charset="utf-8"', $response->getHeader('Content-Type'));
Config::inst()->update(GoogleSitemap::class, 'enabled', false);
Config::inst()->set(GoogleSitemap::class, 'enabled', false);
$response = $this->get('sitemap.xml');
$this->assertEquals(404, $response->getStatusCode(), 'Sitemap index returns a 404 when disabled');
@ -289,7 +277,7 @@ class GoogleSitemapTest extends FunctionalTest
$this->assertEquals(404, $response->getStatusCode(), 'Sitemap file returns a 404 when disabled');
}
public function testDecoratorAddsFields()
public function testDecoratorAddsFields(): void
{
if (!class_exists("Page")) {
$this->markTestIncomplete('No cms module installed, page related test skipped');
@ -305,7 +293,7 @@ class GoogleSitemapTest extends FunctionalTest
$this->assertInstanceOf(LiteralField::class, $tab->fieldByName('GoogleSitemapIntro'));
}
public function testGetPriority()
public function testGetPriority(): void
{
if (!class_exists("Page")) {
$this->markTestIncomplete('No cms module installed, page related test skipped');
@ -326,7 +314,7 @@ class GoogleSitemapTest extends FunctionalTest
$this->assertFalse($page->getGooglePriority());
}
public function testUnpublishedPage()
public function testUnpublishedPage(): void
{
if (!class_exists('SilverStripe\CMS\Model\SiteTree')) {
$this->markTestSkipped('Test skipped; CMS module required for testUnpublishedPage');
@ -335,12 +323,12 @@ class GoogleSitemapTest extends FunctionalTest
$orphanedPage = new \SilverStripe\CMS\Model\SiteTree();
$orphanedPage->ParentID = 999999; // missing parent id
$orphanedPage->write();
$orphanedPage->publish("Stage", "Live");
$orphanedPage->publishSingle();
$rootPage = new \SilverStripe\CMS\Model\SiteTree();
$rootPage->ParentID = 0;
$rootPage->write();
$rootPage->publish("Stage", "Live");
$rootPage->publishSingle();
$oldMode = Versioned::get_reading_mode();
Versioned::set_reading_mode('Live');
@ -354,7 +342,7 @@ class GoogleSitemapTest extends FunctionalTest
Versioned::set_reading_mode($oldMode);
throw $ex;
} // finally {
Versioned::set_reading_mode($oldMode);
Versioned::set_reading_mode($oldMode);
// }
}
}

View File

@ -1,15 +1,15 @@
Wilr\GoogleSitemaps\Tests\Model\TestDataObject:
DataObjectTest1:
Priority: 0.4
DataObjectTest2:
Priority: 0.2
UnindexedDataObject:
Priority: -1
DataObjectTest1:
Priority: 0.4
DataObjectTest2:
Priority: 0.2
UnindexedDataObject:
Priority: -1
Wilr\GoogleSitemaps\Tests\Model\OtherDataObject:
OtherDataObjectTest2:
Priority: 0.3
OtherDataObjectTest2:
Priority: 0.3
Wilr\GoogleSitemaps\Tests\Model\UnviewableDataObject:
Unviewable1:
Priority: 0.4
Unviewable1:
Priority: 0.4

View File

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

View 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>

View 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>

View 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>