Compare commits
124 Commits
Author | SHA1 | Date |
---|---|---|
Will Rossiter | 794778ae20 | |
Sukhwinder Singh | 05e6963f04 | |
Will Rossiter | 3f75c06a77 | |
jareddreyerss | a13d3dd9de | |
Will Rossiter | 59acd419ef | |
Sunny Side Up | aeeb960566 | |
Will Rossiter | c7f260bb33 | |
Brooke Lord | c2f5162e97 | |
Brooke Lord | 59e56c3b69 | |
Brooke Lord | d9da059a83 | |
Brooke Lord | 54ddc6ccf7 | |
Will Rossiter | cbccf88a62 | |
Will Rossiter | 8a7c8aa9cc | |
Will Rossiter | def6be347c | |
Will Rossiter | 523d196d12 | |
Mohamed Alsharaf | f064330acd | |
Will Rossiter | f1bddb626d | |
Luke Fromhold | 77a366871e | |
Will Rossiter | 81b8d870a4 | |
Will Rossiter | 9b0e0f1064 | |
Will Rossiter | 7370c85dae | |
Tyler | bf6156c103 | |
Will Rossiter | a29de80408 | |
Will Rossiter | 0fbfec9f66 | |
Will Rossiter | 54e5043622 | |
Mohamed Alsharaf | 286115174d | |
Will Rossiter | cfcb002cf3 | |
Jan Metzger | 0e965ecd46 | |
Will Rossiter | cb56ac11d9 | |
Will Rossiter | 6adb11b5bf | |
sinan-evanshunt | 8d321bd0a1 | |
terry | 35665402cb | |
Will Rossiter | 7326028c7c | |
Will Rossiter | 401f723e7e | |
Will Rossiter | 71e62966d0 | |
Lukas Erni | 1915f9ab59 | |
Lukas Erni | 1d535ade02 | |
Will Rossiter | 56b25a424f | |
Will Rossiter | 67e8d601d5 | |
Will Rossiter | 67c8ad044e | |
Will Rossiter | a333b56711 | |
Will Rossiter | 745de7a7af | |
Will Rossiter | b5f322646f | |
Will Rossiter | 312b78f88b | |
Florian Thoma | f026001f8b | |
Florian Thoma | ced2213be4 | |
Will Rossiter | 17b21683a2 | |
Tomasz Pirc | 7668ba8f48 | |
Will Rossiter | 4214c091e5 | |
Christopher Darling | 799f669da2 | |
Will Rossiter | 982d96c203 | |
Pavol Tuka | 56ef63f6c3 | |
Will Rossiter | c1fc291e59 | |
Will Rossiter | 36c9b9f1d4 | |
Will Rossiter | 1397fa0e30 | |
Will Rossiter | 473b88ee05 | |
Andreas Piening | 2caacf8e86 | |
Will Rossiter | 7f8937dd1f | |
Will Rossiter | 764a8f5bb2 | |
Will Rossiter | d8beb33d86 | |
Will Rossiter | 2860c390b0 | |
Jarkko Linnanvirta | 41390002d5 | |
Will Rossiter | 710b8c3518 | |
Will Rossiter | 4d131b06ac | |
Andy Karwal | f7b48e2b99 | |
Will Rossiter | 71c662e4d1 | |
Will Rossiter | 64ead69f4e | |
Will Rossiter | 572da5fa8a | |
Will Rossiter | a0a8c34d2f | |
Will Rossiter | adb3ed94fc | |
Daniel Hensby | c3fcd84542 | |
Daniel Hensby | 776e6bd9d9 | |
Daniel Hensby | 36091bc840 | |
Daniel Hensby | f343f312eb | |
Daniel Hensby | 6e49a3e01e | |
Daniel Hensby | b42305dfef | |
Daniel Hensby | 729311249c | |
Daniel Hensby | 326b1ce4b0 | |
Daniel Hensby | 98f65de824 | |
Will Rossiter | 628dbd8404 | |
Nivanka Fonseka | c5388b6e5b | |
Nivanka Fonseka | 29be1bc504 | |
Will Rossiter | c00f420a8b | |
Will Rossiter | 36ef636249 | |
zanderwar | e51dd3877f | |
Daniel Hensby | f51c9e12bf | |
Daniel Hensby | 64a9465d7e | |
Daniel Hensby | bfaaa42e10 | |
Robbie Averill | d5b35cf36c | |
Robbie Averill | b11c6d771e | |
Robbie Averill | 450283bd1b | |
Robbie Averill | 1d8c6faa2e | |
Robbie Averill | d1d5c2b268 | |
Will Rossiter | 9e7aed0f2d | |
Will Rossiter | 742724dad4 | |
Will Rossiter | 38c489c931 | |
Will Rossiter | 0daa163ec1 | |
Will Rossiter | c8e142718d | |
Will Rossiter | 2699956b23 | |
Danae Miller-Clendon | 27898476d8 | |
Will Rossiter | 71567fec58 | |
Will Rossiter | d88ed57a07 | |
tim | 030dd83613 | |
Will Rossiter | b40a66422b | |
Will Rossiter | 313e4649c1 | |
Will Rossiter | 840f664cd9 | |
Will Rossiter | 56b23dbdf8 | |
Will Rossiter | 77f6a02ae7 | |
Will Rossiter | 526a0eec4d | |
Will Rossiter | e0821f8f03 | |
Will Rossiter | 830abcc4f9 | |
Matt Peel | fa92f0d13f | |
Will Rossiter | 8307b56f3f | |
digitall-it | d8f8cafd2d | |
Mike Cochrane | b8766edbee | |
Mike Cochrane | 9f845f5af7 | |
Mike Cochrane | 4333bb6777 | |
Will Rossiter | 51fa6f4c25 | |
Loz Calver | 671522de5f | |
Loz Calver | 286c79160a | |
Will Rossiter | 552684ce12 | |
Will Rossiter | 930fd7f9ac | |
Will Rossiter | e607ea9fc4 | |
Ingo Schommer | 9a95e58d6e |
|
@ -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
|
|
@ -1 +1,4 @@
|
|||
.DS_Store
|
||||
/vendor
|
||||
/public
|
||||
/composer.lock
|
||||
|
|
|
@ -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/*]
|
37
.travis.yml
37
.travis.yml
|
@ -1,37 +0,0 @@
|
|||
# See https://github.com/silverstripe-labs/silverstripe-travis-support for setup details
|
||||
|
||||
sudo: false
|
||||
|
||||
language: php
|
||||
|
||||
php:
|
||||
- 5.3
|
||||
- 5.4
|
||||
- 5.5
|
||||
- 5.6
|
||||
- 7.0
|
||||
|
||||
env:
|
||||
- DB=MYSQL CORE_RELEASE=3.2
|
||||
|
||||
matrix:
|
||||
include:
|
||||
- php: 5.6
|
||||
env: DB=MYSQL CORE_RELEASE=3
|
||||
- php: 5.6
|
||||
env: DB=MYSQL CORE_RELEASE=3.1
|
||||
- php: 5.6
|
||||
env: DB=PGSQL CORE_RELEASE=3.2
|
||||
allow_failures:
|
||||
- php: 7.0
|
||||
|
||||
before_script:
|
||||
- composer self-update || true
|
||||
- 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
|
||||
- composer install
|
||||
- composer require wilr/silverstripe-googlesitemaps
|
||||
|
||||
script:
|
||||
- vendor/bin/phpunit googlesitemaps/tests
|
|
@ -0,0 +1,2 @@
|
|||
mappings:
|
||||
GoogleSitemap: Wilr\GoogleSitemaps\GoogleSitemap
|
37
LICENSE
37
LICENSE
|
@ -1,24 +1,13 @@
|
|||
* Copyright (c) 2011, Silverstripe Ltd.
|
||||
* All rights reserved.
|
||||
*
|
||||
* 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 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.
|
||||
* * Neither the name of the <organization> nor the
|
||||
* 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 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.
|
||||
BSD 3-Clause License
|
||||
|
||||
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:
|
||||
|
||||
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 documentation and/or other materials provided with the distribution.
|
||||
|
||||
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.
|
||||
|
||||
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.
|
||||
|
|
37
README.md
37
README.md
|
@ -1,34 +1,37 @@
|
|||
# Google Sitemaps Module
|
||||
|
||||
[![Build Status](https://secure.travis-ci.org/wilr/silverstripe-googlesitemaps.png?branch=master)](http://travis-ci.org/wilr/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
|
||||
|
||||
* Will Rossiter (Nickname: wrossiter, willr) <will@fullscreen.io>
|
||||
|
||||
## Requirements
|
||||
|
||||
* SilverStripe 3.1
|
||||
- Will Rossiter (Nickname: wrossiter, willr) <will@fullscreen.io>
|
||||
|
||||
## Installation
|
||||
|
||||
> composer require "silverstripe/googlesitemaps"
|
||||
> 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
|
||||
|
||||
SilverStripe provides 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.
|
||||
Provides support for the [Sitemaps XML Protocol](http://www.sitemaps.org/protocol.html),
|
||||
enabling Google, Bing and other search engines to index the web pages on your
|
||||
site. This helps your SilverStripe website rank well in search engines, and to
|
||||
encourage the information on your site to be discovered by Google quickly.
|
||||
|
||||
Therefore, all Silverstripe websites contain a special controller which can
|
||||
be visited: http://yoursite.com/sitemap.xml
|
||||
Any new pages published or unpublished on your website automatically update the
|
||||
Sitemap.
|
||||
|
||||
Flush this route to ensure the changes take effect with: http://yoursite.com/sitemap.xml?flush=1
|
||||
|
||||
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
|
||||
|
||||
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
|
||||
|
||||
// add the extension to pages
|
||||
if (class_exists('SiteTree')) {
|
||||
SiteTree::add_extension('GoogleSitemapSiteTreeExtension');
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
// 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
|
||||
---
|
||||
GoogleSitemap:
|
||||
Wilr\GoogleSitemaps\GoogleSitemap:
|
||||
enabled: true
|
||||
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
|
||||
---
|
||||
Director:
|
||||
SilverStripe\Control\Director:
|
||||
rules:
|
||||
'sitemap.xml': 'GoogleSitemapController'
|
||||
'sitemap.xml': 'Wilr\GoogleSitemaps\Control\GoogleSitemapController'
|
||||
|
|
|
@ -1,88 +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"');
|
||||
$this->getResponse()->addHeader('X-Robots-Tag', 'noindex');
|
||||
|
||||
$sitemaps = GoogleSitemap::inst()->getSitemaps();
|
||||
$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->unsanitiseClassName($this->request->param('ID'));
|
||||
$page = $this->request->param('OtherID');
|
||||
|
||||
if (GoogleSitemap::enabled() && $class && $page && ($class == 'SiteTree' || $class == 'GoogleSitemapRoute' || GoogleSitemap::is_registered($class))) {
|
||||
Config::inst()->update('SSViewer', 'set_source_file_comments', false);
|
||||
|
||||
$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
|
||||
);
|
||||
} else {
|
||||
return new SS_HTTPResponse('Page not found', 404);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Unsanitise a namespaced class' name from a URL param
|
||||
* @return string
|
||||
*/
|
||||
protected function unsanitiseClassName($class)
|
||||
{
|
||||
return str_replace('-', '\\', $class);
|
||||
}
|
||||
}
|
|
@ -1,125 +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");
|
||||
}
|
||||
|
||||
/**
|
||||
* 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;
|
||||
|
||||
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;
|
||||
} elseif ($priority == -1) {
|
||||
return false;
|
||||
} else {
|
||||
return (is_numeric($priority) && $priority <= 1.0) ? $priority : 0.5;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,18 +1,53 @@
|
|||
{
|
||||
"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 by Google quickly.",
|
||||
"type": "silverstripe-module",
|
||||
"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": "~3.1"
|
||||
},
|
||||
"extra": {
|
||||
"installer-name": "googlesitemaps"
|
||||
"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": "^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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
242
css/style.css
242
css/style.css
|
@ -1,131 +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;
|
||||
.content {
|
||||
margin: 0 auto;
|
||||
max-width: 122rem;
|
||||
padding: 2rem;
|
||||
}
|
||||
|
||||
vertical-align: top;
|
||||
.content__title {
|
||||
align-items: center;
|
||||
color: var(--color-black);
|
||||
display: inline-flex;
|
||||
font-size: 2rem;
|
||||
font-weight: 600;
|
||||
margin: 0 0 3rem;
|
||||
}
|
||||
table.tablesorter tbody tr{
|
||||
background-color: #FFF;
|
||||
|
||||
.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;
|
||||
}
|
||||
table.tablesorter tbody tr.odd {
|
||||
background-color:#EFF2F3;
|
||||
|
||||
.content__text {
|
||||
margin: 1rem 0;
|
||||
}
|
||||
table.tablesorter tbody tr:hover {
|
||||
background-color: #D8E1E8;
|
||||
|
||||
.content__text a {
|
||||
color: var(--color-blue-bright);
|
||||
text-decoration: underline;
|
||||
}
|
||||
table.tablesorter tbody a{
|
||||
color: #444;color: #005A92;
|
||||
text-decoration: none;
|
||||
|
||||
.content__text a:hover {
|
||||
color: var(--color-blue-dark);
|
||||
text-decoration: none;
|
||||
}
|
||||
table.tablesorter tbody a:hover{
|
||||
text-decoration: underline;
|
||||
|
||||
.table-wrapper {
|
||||
border-top: 0.1rem solid var(--color-grey-medium);
|
||||
margin: 3rem 0 5rem;
|
||||
overflow-x: auto;
|
||||
}
|
||||
table.tablesorter tbody tr:hover td,
|
||||
table.tablesorter tbody tr:hover a{
|
||||
color: #000;
|
||||
|
||||
.table__cell {
|
||||
padding: 1rem;
|
||||
}
|
||||
#Footer{
|
||||
margin: 50px 0 10px;
|
||||
text-align: right;
|
||||
font-size: 0.8em;
|
||||
|
||||
.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;
|
||||
}
|
||||
|
|
171
docs/en/index.md
171
docs/en/index.md
|
@ -1,119 +1,144 @@
|
|||
# Google Sitemaps Module
|
||||
|
||||
SilverStripe provides support for the Google Sitemaps XML system, enabling
|
||||
Google and other search engines to see all pages on your site. This helps
|
||||
your SilverStripe website rank well in search engines, and to encourage the
|
||||
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 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
|
||||
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
|
||||
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
|
||||
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 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
|
||||
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,
|
||||
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).
|
||||
|
||||
In the CMS, in the Settings tab for each page, you can set the importance
|
||||
In the CMS, in the Settings tab for each page, you can set the importance
|
||||
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
|
||||
---
|
||||
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
|
||||
---
|
||||
GoogleSitemap:
|
||||
enabled: true
|
||||
objects_per_sitemap: 1000
|
||||
google_notification_enabled: true
|
||||
use_show_in_search: true
|
||||
```yml
|
||||
---
|
||||
Name: customgooglesitemaps
|
||||
After: googlesitemaps
|
||||
---
|
||||
Wilr\GoogleSitemaps\GoogleSitemap:
|
||||
enabled: true
|
||||
objects_per_sitemap: 1000
|
||||
use_show_in_search: true
|
||||
```
|
||||
|
||||
### Including DataObjects
|
||||
|
||||
The module provides support for including DataObject subclasses as pages in the
|
||||
The module provides support for including DataObject subclasses as pages in the
|
||||
SiteTree such as comments, forum posts and other pages which are stored in your
|
||||
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:
|
||||
|
||||
* 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
|
||||
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
|
||||
|
||||
class MyDataObject extends DataObject {
|
||||
|
||||
function canView($member = null) {
|
||||
return true;
|
||||
}
|
||||
|
||||
function AbsoluteLink() {
|
||||
return Director::absoluteURL($this->Link());
|
||||
}
|
||||
|
||||
function Link() {
|
||||
return 'MyController/show/'. $this->ID;
|
||||
}
|
||||
}
|
||||
<?php
|
||||
|
||||
use SilverStripe\ORM\DataObject;
|
||||
use SilverStripe\Control\Director;
|
||||
|
||||
After those methods have been defined on your DataObject you now need to tell
|
||||
class MyDataObject extends DataObject {
|
||||
|
||||
function canView($member = null) {
|
||||
return true;
|
||||
}
|
||||
|
||||
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.
|
||||
|
||||
GoogleSitemap::register_dataobject('MyDataObject');
|
||||
use Wilr\GoogleSitemaps\GoogleSitemap;
|
||||
|
||||
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
|
||||
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
|
||||
instead of the previous code you would write:
|
||||
|
||||
GoogleSitemap::register_dataobject('MyDataObject', 'daily');
|
||||
|
||||
use Wilr\GoogleSitemaps\GoogleSitemap;
|
||||
|
||||
GoogleSitemap::register_dataobject('MyDataObject', 'daily');
|
||||
|
||||
See the following blog post for more information:
|
||||
|
||||
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.
|
||||
|
||||
GoogleSitemap::register_routes(array(
|
||||
'/my-custom-controller/',
|
||||
'/Security/',
|
||||
'/Security/login/'
|
||||
));
|
||||
use Wilr\GoogleSitemaps\GoogleSitemap;
|
||||
|
||||
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()
|
||||
{
|
||||
// ..
|
||||
}
|
||||
}
|
||||
```
|
||||
|
|
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 |
|
@ -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 |
|
@ -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);
|
|
@ -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'
|
|
@ -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'
|
|
@ -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'
|
|
@ -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'
|
|
@ -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>
|
|
@ -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>
|
|
@ -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), '/');
|
||||
}
|
||||
}
|
|
@ -1,10 +1,16 @@
|
|||
<?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
|
||||
* additionally options and configuration.
|
||||
*
|
||||
* @package googlesitemaps
|
||||
* Decorate the page object to provide google sitemaps with additional options
|
||||
* and configuration.
|
||||
*/
|
||||
class GoogleSitemapExtension extends DataExtension
|
||||
{
|
||||
|
@ -18,50 +24,55 @@ class GoogleSitemapExtension extends DataExtension
|
|||
|
||||
if ($this->owner->hasMethod('AbsoluteLink')) {
|
||||
$hostHttp = parse_url(Director::protocolAndHost(), PHP_URL_HOST);
|
||||
$objHttp = parse_url($this->owner->AbsoluteLink(), 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();
|
||||
$can = ($this->owner->getGooglePriority() !== false);
|
||||
}
|
||||
|
||||
// Allow override. In this case, since this can return multiple results, we'll use an "and" based policy. That is
|
||||
// if any value is false then the current value will be false. Only only if all are true will we then return true.
|
||||
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) {
|
||||
$can = min($override);
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @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.
|
||||
|
@ -72,42 +83,49 @@ class GoogleSitemapExtension extends DataExtension
|
|||
{
|
||||
$field = $this->owner->hasField('Priority');
|
||||
|
||||
if (isset($this->Priority) || ($field && $this->Priority = $this->owner->getField('Priority'))) {
|
||||
return ($this->Priority < 0) ? false : $this->Priority;
|
||||
if ($field) {
|
||||
$priority = $this->owner->getField('Priority');
|
||||
|
||||
return ($priority < 0) ? false : $priority;
|
||||
}
|
||||
|
||||
return GoogleSitemap::get_priority_for_class($this->owner->class);
|
||||
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
|
||||
* 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
|
||||
* @return DBDatetime
|
||||
*/
|
||||
public function getChangeFrequency()
|
||||
{
|
||||
if ($freq = GoogleSitemap::get_frequency_for_class($this->owner->class)) {
|
||||
if ($freq = GoogleSitemap::get_frequency_for_class($this->owner->ClassName)) {
|
||||
return $freq;
|
||||
}
|
||||
|
||||
$date = date('Y-m-d H:i:s');
|
||||
|
||||
$created = new SS_Datetime();
|
||||
$created = new DBDatetime();
|
||||
$created->value = ($this->owner->Created) ? $this->owner->Created : $date;
|
||||
|
||||
$now = new SS_Datetime();
|
||||
$now = new DBDatetime();
|
||||
$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);
|
||||
$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';
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -1,4 +1,22 @@
|
|||
<?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
|
||||
|
@ -8,37 +26,30 @@
|
|||
* 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 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>
|
||||
* ---
|
||||
* Name: customgooglesitemaps
|
||||
* After: googlesitemaps
|
||||
* ---
|
||||
* GoogleSitemap:
|
||||
* enabled: true
|
||||
* objects_per_sitemap: 1000
|
||||
* google_notification_enabled: true
|
||||
* use_show_in_search: true
|
||||
* ---
|
||||
* 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
|
||||
*
|
||||
* @package googlesitemaps
|
||||
*/
|
||||
class GoogleSitemap extends Object
|
||||
class GoogleSitemap
|
||||
{
|
||||
use Extensible;
|
||||
use Injectable;
|
||||
use Configurable;
|
||||
|
||||
/**
|
||||
* List of {@link DataObject} class names to include. As well as the change
|
||||
|
@ -46,7 +57,7 @@ class GoogleSitemap extends Object
|
|||
*
|
||||
* @var array
|
||||
*/
|
||||
private static $dataobjects = array();
|
||||
private static $dataobjects = [];
|
||||
|
||||
/**
|
||||
* List of custom routes to include in the sitemap (such as controller
|
||||
|
@ -54,7 +65,7 @@ class GoogleSitemap extends Object
|
|||
*
|
||||
* @var array
|
||||
*/
|
||||
private static $routes = array();
|
||||
private static $routes = [];
|
||||
|
||||
/**
|
||||
* @config
|
||||
|
@ -80,12 +91,12 @@ class GoogleSitemap extends Object
|
|||
public static function register_dataobject($className, $changeFreq = 'monthly', $priority = '0.6')
|
||||
{
|
||||
if (!self::is_registered($className)) {
|
||||
$className::add_extension('GoogleSitemapExtension');
|
||||
$className::add_extension(GoogleSitemapExtension::class);
|
||||
|
||||
self::$dataobjects[$className] = array(
|
||||
self::$dataobjects[$className] = [
|
||||
'frequency' => ($changeFreq) ? $changeFreq : 'monthly',
|
||||
'priority' => ($priority) ? $priority : '0.6'
|
||||
);
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -120,10 +131,10 @@ class GoogleSitemap extends Object
|
|||
{
|
||||
if (!isset(self::$dataobjects[$className])) {
|
||||
$lowerKeys = array_change_key_case(self::$dataobjects);
|
||||
|
||||
|
||||
return isset($lowerKeys[$className]);
|
||||
}
|
||||
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -205,7 +216,8 @@ class GoogleSitemap extends Object
|
|||
*/
|
||||
public function getItems($class, $page = 1)
|
||||
{
|
||||
//normalise the class name
|
||||
$page = (int) $page;
|
||||
|
||||
try {
|
||||
$reflectionClass = new ReflectionClass($class);
|
||||
$class = $reflectionClass->getName();
|
||||
|
@ -215,9 +227,9 @@ class GoogleSitemap extends Object
|
|||
}
|
||||
|
||||
$output = new ArrayList();
|
||||
$count = Config::inst()->get('GoogleSitemap', 'objects_per_sitemap');
|
||||
$filter = Config::inst()->get('GoogleSitemap', 'use_show_in_search');
|
||||
$redirector = Config::inst()->get('GoogleSitemap', '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
|
||||
|
@ -225,15 +237,15 @@ class GoogleSitemap extends Object
|
|||
Translatable::disable_locale_filter();
|
||||
}
|
||||
|
||||
if ($class == "SiteTree") {
|
||||
$instances = Versioned::get_by_stage('SiteTree', 'Live');
|
||||
if ($class == 'SilverStripe\CMS\Model\SiteTree') {
|
||||
$instances = Versioned::get_by_stage('SilverStripe\CMS\Model\SiteTree', 'Live');
|
||||
|
||||
if($filter) {
|
||||
if ($filter) {
|
||||
$instances = $instances->filter('ShowInSearch', 1);
|
||||
}
|
||||
|
||||
if($redirector) {
|
||||
foreach (ClassInfo::subclassesFor('RedirectorPage') as $redirectorClass) {
|
||||
if ($redirector) {
|
||||
foreach (ClassInfo::subclassesFor('SilverStripe\\CMS\\Model\\RedirectorPage') as $redirectorClass) {
|
||||
$instances = $instances->exclude('ClassName', $redirectorClass);
|
||||
}
|
||||
}
|
||||
|
@ -337,11 +349,11 @@ class GoogleSitemap extends Object
|
|||
*/
|
||||
public function getSitemaps()
|
||||
{
|
||||
$countPerFile = Config::inst()->get('GoogleSitemap', 'objects_per_sitemap');
|
||||
$countPerFile = Config::inst()->get(__CLASS__, 'objects_per_sitemap');
|
||||
$sitemaps = new ArrayList();
|
||||
$filter = Config::inst()->get('GoogleSitemap', 'use_show_in_search');
|
||||
$filter = Config::inst()->get(__CLASS__, 'use_show_in_search');
|
||||
|
||||
if (class_exists('SiteTree')) {
|
||||
if (class_exists('SilverStripe\CMS\Model\SiteTree')) {
|
||||
// move to extension hook. At the moment moduleexists config hook
|
||||
// does not work.
|
||||
if (class_exists('Translatable')) {
|
||||
|
@ -349,7 +361,7 @@ class GoogleSitemap extends Object
|
|||
}
|
||||
|
||||
$filter = ($filter) ? "\"ShowInSearch\" = 1" : "";
|
||||
$class = 'SiteTree';
|
||||
$class = 'SilverStripe\CMS\Model\SiteTree';
|
||||
$instances = Versioned::get_by_stage($class, 'Live', $filter);
|
||||
$this->extend("alterDataList", $instances, $class);
|
||||
$count = $instances->count();
|
||||
|
@ -359,13 +371,13 @@ class GoogleSitemap extends Object
|
|||
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');
|
||||
|
||||
$sitemaps->push(new ArrayData(array(
|
||||
'ClassName' => 'SiteTree',
|
||||
'ClassName' => $this->sanitiseClassName('SilverStripe\CMS\Model\SiteTree'),
|
||||
'LastModified' => $lastModified,
|
||||
'Page' => $i
|
||||
)));
|
||||
|
@ -385,7 +397,7 @@ class GoogleSitemap extends Object
|
|||
->limit($countPerFile, ($i - 1) * $countPerFile)
|
||||
->last();
|
||||
|
||||
$lastModified = ($sliced) ? $sliced->dbObject('LastEdited')->Format('Y-m-d') : 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),
|
||||
|
@ -421,64 +433,6 @@ class GoogleSitemap extends Object
|
|||
return static::inst()->getSitemaps();
|
||||
}
|
||||
|
||||
/**
|
||||
* 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;
|
||||
}
|
||||
|
||||
/**
|
||||
* Is GoogleSitemap enabled?
|
||||
*
|
||||
|
@ -486,12 +440,13 @@ class GoogleSitemap extends Object
|
|||
*/
|
||||
public static function enabled()
|
||||
{
|
||||
return (Config::inst()->get('GoogleSitemap', 'enabled', Config::INHERITED));
|
||||
return (Config::inst()->get(__CLASS__, 'enabled'));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Convenience method for manufacturing an instance for hew instance-level methods (and for easier type definition).
|
||||
* Convenience method for manufacturing an instance for hew instance-level
|
||||
* methods (and for easier type definition).
|
||||
*
|
||||
* @return GoogleSitemap
|
||||
*/
|
||||
|
@ -506,6 +461,6 @@ class GoogleSitemap extends Object
|
|||
*/
|
||||
protected function sanitiseClassName($class)
|
||||
{
|
||||
return str_replace('\\', '-', $class);
|
||||
return str_replace('\\', '-', (string) $class);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,14 @@
|
|||
<?php
|
||||
|
||||
|
||||
namespace Wilr\GoogleSitemaps;
|
||||
|
||||
interface Sitemapable
|
||||
{
|
||||
/**
|
||||
* Return the absolute URL for this object
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function AbsoluteLink();
|
||||
}
|
|
@ -1,8 +0,0 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<?xml-stylesheet type='text/xsl' href='{$AbsoluteBaseURL}googlesitemaps/templates/xml-sitemapindex.xsl'?>
|
||||
<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 %>
|
||||
</sitemapindex>
|
|
@ -1,12 +0,0 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<?xml-stylesheet type='text/xsl' href='{$AbsoluteBaseURL}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>
|
|
@ -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="https://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>
|
|
@ -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,64 +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="https://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() {
|
||||
$("#sitemapindex").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 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">
|
||||
<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>
|
||||
</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>
|
|
@ -1,36 +1,60 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* TODO: Migrate to new instance level interface instead of using static methods for retrieval of site maps and items (i.e. ->getSitemaps() instead of ::get_sitemaps()).
|
||||
*
|
||||
* @package googlesitemaps
|
||||
* @subpackage tests
|
||||
*/
|
||||
namespace Wilr\GoogleSitemaps\Tests;
|
||||
|
||||
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;
|
||||
|
||||
class GoogleSitemapTest extends FunctionalTest
|
||||
{
|
||||
protected static $fixture_file = [
|
||||
'GoogleSitemapTest.yml'
|
||||
];
|
||||
|
||||
public static $fixture_file = 'googlesitemaps/tests/GoogleSitemapTest.yml';
|
||||
protected $usesDatabase = true;
|
||||
|
||||
protected $extraDataObjects = array(
|
||||
'GoogleSitemapTest_DataObject',
|
||||
'GoogleSitemapTest_OtherDataObject',
|
||||
'GoogleSitemapTest_UnviewableDataObject',
|
||||
'SilverStripe\GoogleSitemaps\Test_DataObject',
|
||||
);
|
||||
public static function get_fixture_file()
|
||||
{
|
||||
$files = [__DIR__ . '/GoogleSitemapTest.yml'];
|
||||
|
||||
public function setUp()
|
||||
if (class_exists('Page')) {
|
||||
$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();
|
||||
|
||||
if (class_exists('Page')) {
|
||||
$this->loadFixture('googlesitemaps/tests/GoogleSitemapPageTest.yml');
|
||||
}
|
||||
|
||||
GoogleSitemap::clear_registered_dataobjects();
|
||||
GoogleSitemap::clear_registered_routes();
|
||||
}
|
||||
|
||||
public function tearDown()
|
||||
protected function tearDown(): void
|
||||
{
|
||||
parent::tearDown();
|
||||
|
||||
|
@ -38,121 +62,145 @@ class GoogleSitemapTest extends FunctionalTest
|
|||
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/');
|
||||
|
||||
$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("GoogleSitemapTest_DataObject", '');
|
||||
GoogleSitemap::register_dataobject(TestDataObject::class, '');
|
||||
$google = new GoogleSitemap();
|
||||
|
||||
$items = GoogleSitemap::get_items('GoogleSitemapTest_DataObject', 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("GoogleSitemapTest_OtherDataObject");
|
||||
$this->assertEquals(1, GoogleSitemap::get_items('GoogleSitemapTest_OtherDataObject', 1)->count());
|
||||
GoogleSitemap::register_dataobject(OtherDataObject::class);
|
||||
$this->assertEquals(1, $google->getItems(OtherDataObject::class, 1)->count());
|
||||
|
||||
GoogleSitemap::register_dataobject("GoogleSitemapTest_UnviewableDataObject");
|
||||
$this->assertEquals(0, GoogleSitemap::get_items('GoogleSitemapTest_UnviewableDataObject', 1)->count());
|
||||
GoogleSitemap::register_dataobject(UnviewableDataObject::class);
|
||||
$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("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');
|
||||
$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/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');
|
||||
$this->assertXmlStringEqualsXmlFile(__DIR__ . '/xml/' . __FUNCTION__ . '.xml', $body);
|
||||
}
|
||||
|
||||
public function testLastModifiedDateOnRootXML()
|
||||
public function testLastModifiedDateOnRootXML(): void
|
||||
{
|
||||
Config::inst()->update('GoogleSitemap', '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();
|
||||
|
||||
$expected = '<lastmod>2014-03-14</lastmod>';
|
||||
|
||||
$this->assertEquals(1, substr_count($body, $expected), 'The last mod date should use most recent LastEdited date');
|
||||
$this->assertEquals(
|
||||
1,
|
||||
substr_count($body, $expected),
|
||||
'The last mod date should use most recent LastEdited date'
|
||||
);
|
||||
}
|
||||
|
||||
public function testIndexFilePaginatedSitemapFiles()
|
||||
public function testIndexFilePaginatedSitemapFiles(): void
|
||||
{
|
||||
$original = Config::inst()->get('GoogleSitemap', 'objects_per_sitemap');
|
||||
Config::inst()->update('GoogleSitemap', 'objects_per_sitemap', 1);
|
||||
GoogleSitemap::register_dataobject("GoogleSitemapTest_DataObject");
|
||||
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/GoogleSitemapTest_DataObject/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/GoogleSitemapTest_DataObject/2") ."</loc>";
|
||||
$this->assertEquals(1, substr_count($body, $expected), 'A link to the second page GoogleSitemapTest_DataObject exists');
|
||||
|
||||
Config::inst()->update('GoogleSitemap', '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();
|
||||
|
@ -161,139 +209,96 @@ 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', 'objects_per_sitemap', 1);
|
||||
GoogleSitemap::register_dataobject("GoogleSitemapTest_DataObject");
|
||||
Config::inst()->set(GoogleSitemap::class, 'objects_per_sitemap', 1);
|
||||
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();
|
||||
|
||||
$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 testAccessingNestedSiteMapCaseInsensitive()
|
||||
public function testGetItemsWithPages(): void
|
||||
{
|
||||
$original = Config::inst()->get('GoogleSitemap', 'objects_per_sitemap');
|
||||
Config::inst()->update('GoogleSitemap', 'objects_per_sitemap', 1);
|
||||
GoogleSitemap::register_dataobject("GoogleSitemapTest_DataObject");
|
||||
|
||||
$response = $this->get('sitemap.xml/sitemap/googlesitemaptest_dataobject/1');
|
||||
$body = $response->getBody();
|
||||
|
||||
$this->assertEquals(200, $response->getStatusCode(), 'successful loaded nested sitemap');
|
||||
|
||||
Config::inst()->update('GoogleSitemap', 'objects_per_sitemap', $original);
|
||||
}
|
||||
|
||||
public function testAccessingNestedNamespacedSiteMap()
|
||||
{
|
||||
$original = Config::inst()->get('GoogleSitemap', 'objects_per_sitemap');
|
||||
Config::inst()->update('GoogleSitemap', 'objects_per_sitemap', 1);
|
||||
GoogleSitemap::register_dataobject("SilverStripe\\GoogleSitemaps\\Test_DataObject");
|
||||
|
||||
$response = $this->get('sitemap.xml/sitemap/SilverStripe-GoogleSitemaps-Test_DataObject/1');
|
||||
$body = $response->getBody();
|
||||
|
||||
$this->assertEquals(200, $response->getStatusCode(), 'successful loaded nested sitemap');
|
||||
|
||||
Config::inst()->update('GoogleSitemap', 'objects_per_sitemap', $original);
|
||||
}
|
||||
|
||||
public function testAccessingNestedNamespacedSiteMapCaseInsensitive()
|
||||
{
|
||||
$original = Config::inst()->get('GoogleSitemap', 'objects_per_sitemap');
|
||||
Config::inst()->update('GoogleSitemap', 'objects_per_sitemap', 1);
|
||||
GoogleSitemap::register_dataobject("SilverStripe\\GoogleSitemaps\\Test_DataObject");
|
||||
|
||||
$response = $this->get('sitemap.xml/sitemap/silverstripe-googlesitemaps-test_dataobject/1');
|
||||
$body = $response->getBody();
|
||||
|
||||
$this->assertEquals(200, $response->getStatusCode(), 'successful loaded nested sitemap');
|
||||
|
||||
Config::inst()->update('GoogleSitemap', 'objects_per_sitemap', $original);
|
||||
}
|
||||
|
||||
public function testGetItemsWithPages()
|
||||
{
|
||||
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::get_items('SiteTree'), "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');
|
||||
|
||||
$this->session()->inst_set('loggedInAs', null);
|
||||
|
||||
$this->assertDOSEquals(array(
|
||||
array('Title' => 'Testpage1')
|
||||
), GoogleSitemap::get_items('SiteTree'), "There should be only 1 page, other is logged in only");
|
||||
$page2->publishSingle();
|
||||
|
||||
$this->logOut();
|
||||
|
||||
$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', 'enabled', true);
|
||||
|
||||
Config::inst()->set(GoogleSitemap::class, 'enabled', true);
|
||||
|
||||
$response = $this->get('sitemap.xml');
|
||||
|
||||
$this->assertEquals(200, $response->getStatusCode(), 'Sitemap returns a 200 success when enabled');
|
||||
$this->assertEquals('application/xml; charset="utf-8"', $response->getHeader('Content-Type'));
|
||||
|
||||
GoogleSitemap::register_dataobject("GoogleSitemapTest_DataObject");
|
||||
$response = $this->get('sitemap.xml/sitemap/GoogleSitemapTest_DataObject/1');
|
||||
|
||||
GoogleSitemap::register_dataobject(TestDataObject::class);
|
||||
$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('application/xml; charset="utf-8"', $response->getHeader('Content-Type'));
|
||||
|
||||
Config::inst()->remove('GoogleSitemap', 'enabled');
|
||||
Config::inst()->update('GoogleSitemap', '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');
|
||||
|
||||
$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');
|
||||
}
|
||||
|
||||
public function testDecoratorAddsFields()
|
||||
|
||||
public function testDecoratorAddsFields(): void
|
||||
{
|
||||
if (!class_exists("Page")) {
|
||||
$this->markTestIncomplete('No cms module installed, page related test skipped');
|
||||
}
|
||||
|
||||
$page = $this->objFromFixture('Page', 'Page1');
|
||||
|
||||
|
||||
$fields = $page->getSettingsFields();
|
||||
$tab = $fields->fieldByName('Root')->fieldByName('Settings')->fieldByName('GoogleSitemap');
|
||||
|
||||
$this->assertInstanceOf('Tab', $tab);
|
||||
$this->assertInstanceOf('DropdownField', $tab->fieldByName('Priority'));
|
||||
$this->assertInstanceOf('LiteralField', $tab->fieldByName('GoogleSitemapIntro'));
|
||||
|
||||
$this->assertInstanceOf(Tab::class, $tab);
|
||||
$this->assertInstanceOf(DropdownField::class, $tab->fieldByName('Priority'));
|
||||
$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');
|
||||
}
|
||||
|
||||
|
||||
$page = $this->objFromFixture('Page', 'Page1');
|
||||
|
||||
// invalid field doesn't break google
|
||||
|
@ -303,31 +308,31 @@ class GoogleSitemapTest extends FunctionalTest
|
|||
// 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
|
||||
$page->Priority = -1;
|
||||
$this->assertFalse($page->getGooglePriority());
|
||||
}
|
||||
|
||||
public function testUnpublishedPage()
|
||||
|
||||
public function testUnpublishedPage(): void
|
||||
{
|
||||
if (!class_exists('SiteTree')) {
|
||||
if (!class_exists('SilverStripe\CMS\Model\SiteTree')) {
|
||||
$this->markTestSkipped('Test skipped; CMS module required for testUnpublishedPage');
|
||||
}
|
||||
|
||||
$orphanedPage = new SiteTree();
|
||||
|
||||
$orphanedPage = new \SilverStripe\CMS\Model\SiteTree();
|
||||
$orphanedPage->ParentID = 999999; // missing parent id
|
||||
$orphanedPage->write();
|
||||
$orphanedPage->publish("Stage", "Live");
|
||||
|
||||
$rootPage = new SiteTree();
|
||||
$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::reading_stage('Live');
|
||||
|
||||
Versioned::set_reading_mode('Live');
|
||||
|
||||
try {
|
||||
$this->assertEmpty($orphanedPage->hasPublishedParent());
|
||||
$this->assertEmpty($orphanedPage->canIncludeInGoogleSitemap());
|
||||
|
@ -337,73 +342,7 @@ class GoogleSitemapTest extends FunctionalTest
|
|||
Versioned::set_reading_mode($oldMode);
|
||||
throw $ex;
|
||||
} // finally {
|
||||
Versioned::set_reading_mode($oldMode);
|
||||
Versioned::set_reading_mode($oldMode);
|
||||
// }
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @package googlesitemaps
|
||||
* @subpackage tests
|
||||
*/
|
||||
class GoogleSitemapTest_DataObject extends DataObject implements TestOnly
|
||||
{
|
||||
|
||||
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_OtherDataObject extends DataObject implements TestOnly
|
||||
{
|
||||
|
||||
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,15 +1,15 @@
|
|||
GoogleSitemapTest_DataObject:
|
||||
DataObjectTest1:
|
||||
Priority: 0.4
|
||||
DataObjectTest2:
|
||||
Priority: 0.2
|
||||
UnindexedDataObject:
|
||||
Priority: -1
|
||||
Wilr\GoogleSitemaps\Tests\Model\TestDataObject:
|
||||
DataObjectTest1:
|
||||
Priority: 0.4
|
||||
DataObjectTest2:
|
||||
Priority: 0.2
|
||||
UnindexedDataObject:
|
||||
Priority: -1
|
||||
|
||||
GoogleSitemapTest_OtherDataObject:
|
||||
OtherDataObjectTest2:
|
||||
Priority: 0.3
|
||||
Wilr\GoogleSitemaps\Tests\Model\OtherDataObject:
|
||||
OtherDataObjectTest2:
|
||||
Priority: 0.3
|
||||
|
||||
GoogleSitemapTest_UnviewableDataObject:
|
||||
Unviewable1:
|
||||
Priority: 0.4
|
||||
Wilr\GoogleSitemaps\Tests\Model\UnviewableDataObject:
|
||||
Unviewable1:
|
||||
Priority: 0.4
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
}
|
|
@ -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();
|
||||
}
|
||||
}
|
|
@ -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();
|
||||
}
|
||||
}
|
|
@ -1,27 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace SilverStripe\GoogleSitemaps;
|
||||
|
||||
use Director, DataObject, TestOnly;
|
||||
|
||||
/**
|
||||
* @package googlesitemaps
|
||||
* @subpackage tests
|
||||
*/
|
||||
class Test_DataObject extends DataObject implements TestOnly
|
||||
{
|
||||
|
||||
public static $db = array(
|
||||
'Priority' => 'Varchar(10)'
|
||||
);
|
||||
|
||||
public function canView($member = null)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
public function AbsoluteLink()
|
||||
{
|
||||
return Director::absoluteBaseURL();
|
||||
}
|
||||
}
|
|
@ -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>
|
|
@ -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>
|
|
@ -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