├── .editorconfig
├── .gitignore
├── .travis.yml
├── CODE_OF_CONDUCT.md
├── LICENSE.md
├── README.md
├── composer.json
├── config
└── routes.php
├── phpunit.xml.dist
├── src
├── Controller
│ ├── AppController.php
│ └── SitemapsController.php
├── Lib
│ └── Iterators
│ │ ├── ExtFilteredDirIterator.php
│ │ └── PagesIterator.php
├── Model
│ └── Behavior
│ │ └── SitemapBehavior.php
└── Template
│ └── Sitemaps
│ ├── index.ctp
│ └── xml
│ └── index.ctp
├── tests
├── App
│ └── Template
│ │ └── Layout
│ │ └── default.ctp
├── Fixture
│ └── PagesFixture.php
├── TestCase
│ ├── Controller
│ │ └── SitemapsControllerTest.php
│ └── Model
│ │ └── Behavior
│ │ └── SitemapBehaviorTest.php
├── bootstrap.php
└── config
│ └── routes.php
└── webroot
└── empty
/.editorconfig:
--------------------------------------------------------------------------------
1 | # EditorConfig is awesome: http://EditorConfig.org
2 |
3 | # Unix-style newlines with a newline ending every file
4 | [*]
5 | end_of_line = lf
6 | insert_final_newline = true
7 |
8 | # Tab indentation (no size specified)
9 | [{*.js,*.php,*.ctp}]
10 | indent_style = tab
11 |
12 | # Matches the exact files package.json and .travis.yml
13 | [{package.json,.travis.yml,composer.json,*.yaml,*.yml}]
14 | indent_style = space
15 | indent_size = 2
16 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Composer
2 | /composer.lock
3 |
4 | # CakePHP 3
5 | /vendor/*
6 | /tmp/*
7 | /logs/*
8 |
9 | # CakePHP 2
10 | /app/tmp/*
11 | /vendors/*
12 |
13 | # OS
14 | .DS_Store
15 | .AppleDouble
16 | .LSOverride
17 | Icon?
18 | ._*
19 | .Spotlight-V100
20 | .Trashes
21 | .AppleDB
22 | .AppleDesktop
23 | Network Trash Folder
24 | Temporary Items
25 | .apdisk
26 |
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | language: php
2 |
3 | sudo: false
4 |
5 | php:
6 | - 5.6
7 | - 7.0
8 | - 7.1
9 |
10 | # Environment Variables to set
11 | env:
12 | global:
13 |
14 |
15 | # Cache the composer directories, only allowed if using the container based setup
16 | # which depends on setting sudo to false
17 | cache:
18 | directories:
19 | - $HOME/.composer/cache
20 |
21 | # Branches to be built or not
22 | branches:
23 | # Blacklist these branches
24 | except:
25 | - gh-pages
26 |
27 | before_install:
28 | - composer self-update
29 | - mkdir -p build/logs
30 |
31 | install:
32 | - composer install --no-interaction
33 |
34 | before_script:
35 | - phpenv rehash
36 | - vendor/bin/phpcs --config-set installed_paths vendor/loadsys/loadsys_codesniffer,vendor/cakephp/cakephp-codesniffer
37 |
38 | script:
39 | - vendor/bin/phpcs -np --extensions=php --standard=Loadsys ./src ./tests
40 | - vendor/bin/phpunit --coverage-clover=build/logs/clover.xml
41 |
42 | after_script:
43 | - php vendor/bin/coveralls -v
44 |
45 | notifications:
46 | email: false
47 |
--------------------------------------------------------------------------------
/CODE_OF_CONDUCT.md:
--------------------------------------------------------------------------------
1 | # Contributor Covenant Code of Conduct
2 |
3 | ## Our Pledge
4 |
5 | In the interest of fostering an open and welcoming environment, we as
6 | contributors and maintainers pledge to making participation in our project and
7 | our community a harassment-free experience for everyone, regardless of age, body
8 | size, disability, ethnicity, gender identity and expression, level of experience,
9 | nationality, personal appearance, race, religion, or sexual identity and
10 | orientation.
11 |
12 | ## Our Standards
13 |
14 | Examples of behavior that contributes to creating a positive environment
15 | include:
16 |
17 | * Using welcoming and inclusive language
18 | * Being respectful of differing viewpoints and experiences
19 | * Gracefully accepting constructive criticism
20 | * Focusing on what is best for the community
21 | * Showing empathy towards other community members
22 |
23 | Examples of unacceptable behavior by participants include:
24 |
25 | * The use of sexualized language or imagery and unwelcome sexual attention or
26 | advances
27 | * Trolling, insulting/derogatory comments, and personal or political attacks
28 | * Public or private harassment
29 | * Publishing others' private information, such as a physical or electronic
30 | address, without explicit permission
31 | * Other conduct which could reasonably be considered inappropriate in a
32 | professional setting
33 |
34 | ## Our Responsibilities
35 |
36 | Project maintainers are responsible for clarifying the standards of acceptable
37 | behavior and are expected to take appropriate and fair corrective action in
38 | response to any instances of unacceptable behavior.
39 |
40 | Project maintainers have the right and responsibility to remove, edit, or
41 | reject comments, commits, code, wiki edits, issues, and other contributions
42 | that are not aligned to this Code of Conduct, or to ban temporarily or
43 | permanently any contributor for other behaviors that they deem inappropriate,
44 | threatening, offensive, or harmful.
45 |
46 | ## Scope
47 |
48 | This Code of Conduct applies both within project spaces and in public spaces
49 | when an individual is representing the project or its community. Examples of
50 | representing a project or community include using an official project e-mail
51 | address, posting via an official social media account, or acting as an appointed
52 | representative at an online or offline event. Representation of a project may be
53 | further defined and clarified by project maintainers.
54 |
55 | ## Enforcement
56 |
57 | Instances of abusive, harassing, or otherwise unacceptable behavior may be
58 | reported by contacting the project team at conduct@loadsys.com. All
59 | complaints will be reviewed and investigated and will result in a response that
60 | is deemed necessary and appropriate to the circumstances. The project team is
61 | obligated to maintain confidentiality with regard to the reporter of an incident.
62 | Further details of specific enforcement policies may be posted separately.
63 |
64 | Project maintainers who do not follow or enforce the Code of Conduct in good
65 | faith may face temporary or permanent repercussions as determined by other
66 | members of the project's leadership.
67 |
68 | ## Attribution
69 |
70 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4,
71 | available at [http://contributor-covenant.org/version/1/4][version]
72 |
73 | [homepage]: http://contributor-covenant.org
74 | [version]: http://contributor-covenant.org/version/1/4/
75 |
--------------------------------------------------------------------------------
/LICENSE.md:
--------------------------------------------------------------------------------
1 | # The MIT License (MIT)
2 |
3 | Copyright (c) 2016 Loadsys Web Strategies
4 |
5 | > Permission is hereby granted, free of charge, to any person obtaining a copy
6 | > of this software and associated documentation files (the "Software"), to deal
7 | > in the Software without restriction, including without limitation the rights
8 | > to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | > copies of the Software, and to permit persons to whom the Software is
10 | > furnished to do so, subject to the following conditions:
11 | >
12 | > The above copyright notice and this permission notice shall be included in
13 | > all copies or substantial portions of the Software.
14 | >
15 | > THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | > IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | > FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | > AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | > LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | > OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21 | > THE SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # CakePHP-Sitemap
2 |
3 | [](https://github.com/loadsys/CakePHP-Sitemap/releases)
4 | [](https://travis-ci.org/loadsys/CakePHP-Sitemap)
5 | [](https://coveralls.io/r/loadsys/CakePHP-Sitemap)
6 | [](LICENSE.md)
7 | [](https://packagist.org/packages/loadsys/cakephp_sitemap)
8 |
9 | The Sitemap provides a mechanism for displaying Sitemap style information (the url, change frequency, priority and last modified datetime) for a set of Tables that CakePHP has access to.
10 |
11 |
12 | ## Requirements
13 |
14 | * CakePHP 3.0.0+
15 | * PHP 5.6+
16 |
17 |
18 | ## Installation
19 |
20 | ```shell
21 | $ composer require loadsys/cakephp_sitemap
22 | ```
23 |
24 | In your `config/bootstrap.php` file, add:
25 |
26 | ```php
27 | Plugin::load('Sitemap', ['bootstrap' => false, 'routes' => true]);
28 | ```
29 |
30 | OR
31 |
32 | ```shell
33 | $ bin/cake plugin load Sitemap -r
34 | ```
35 |
36 | ## Usage
37 |
38 | * Add list of tables to display Sitemap records via an array at `Sitemap.tables`
39 |
40 | ```php
41 | Configure::write('Sitemap.tables', [
42 | 'Pages',
43 | 'Sites',
44 | 'Camps',
45 | ]);
46 | ```
47 |
48 | * Add the `Sitemap.Sitemap` Behavior to each table as well
49 |
50 | ```php
51 | $this->addBehavior('Sitemap.Sitemap');
52 | ```
53 |
54 | You can now access the sitemap at `/sitemap.xml`.
55 |
56 | ### Configuration
57 |
58 | * Default configuration options for the `Sitemap` Behavior is:
59 |
60 | ```php
61 | 'cacheConfigKey' => 'default',
62 | 'lastmod' => 'modified',
63 | 'changefreq' => 'daily',
64 | 'priority' => '0.9',
65 | 'conditions' => [],
66 | 'order' => [],
67 | 'fields' => [],
68 | 'implementedMethods' => [
69 | 'getUrl' => 'returnUrlForEntity',
70 | ],
71 | 'implementedFinders' => [
72 | 'forSitemap' => 'findSitemapRecords',
73 | ],
74 | ```
75 |
76 | * To modify these options for instance to change the `changefreq` when listing records, update the `addBehavior` method call for the `Table` in question like so:
77 |
78 | ```php
79 | $this->addBehavior('Sitemap.Sitemap', ['changefreq' => 'weekly']);
80 | ```
81 |
82 | * To customize the url generated for each record create a method named `getUrl` in the matching `Table` class.
83 |
84 | ```php
85 | public function getUrl(\Cake\ORM\Entity $entity) {
86 | return \Cake\Routing\Router::url(
87 | [
88 | 'prefix' => false,
89 | 'plugin' => false,
90 | 'controller' => $this->registryAlias(),
91 | 'action' => 'display',
92 | $entity->display_id,
93 | ],
94 | true
95 | );
96 | }
97 | ```
98 |
99 | * To customize the templates used when displaying the Sitemap, the CakePHP Book provides information regarding [overriding Plugin Templates](http://book.cakephp.org/3.0/en/plugins.html#overriding-plugin-templates-from-inside-your-application).
100 |
101 | ## Contributing
102 |
103 | ### Code of Conduct
104 |
105 | This project has adopted the Contributor Covenant as its [code of conduct](CODE_OF_CONDUCT.md). All contributors are expected to adhere to this code. [Translations are available](http://contributor-covenant.org/).
106 |
107 | ### Reporting Issues
108 |
109 | Please use [GitHub Isuses](https://github.com/loadsys/CakePHP-Sitemap/issues) for listing any known defects or issues.
110 |
111 | ### Development
112 |
113 | When developing this plugin, please fork and issue a PR for any new development.
114 |
115 | Set up a working copy:
116 | ```shell
117 | $ git clone git@github.com:YOUR_USERNAME/CakePHP-Sitemap.git
118 | $ cd CakePHP-Sitemap/
119 | $ composer install
120 | $ vendor/bin/phpcs --config-set installed_paths vendor/loadsys/loadsys_codesniffer,vendor/cakephp/cakephp-codesniffer
121 | ```
122 |
123 | Make your changes:
124 | ```shell
125 | $ git checkout -b your-topic-branch
126 | # (Make your changes. Write some tests.)
127 | $ vendor/bin/phpunit
128 | $ vendor/bin/phpcs -p --extensions=php --standard=Loadsys ./src ./tests
129 | ```
130 |
131 | Then commit and push your changes to your fork, and open a pull request.
132 |
133 |
134 | ## License
135 |
136 | [MIT](https://github.com/loadsys/CakePHP-Sitemap/blob/master/LICENSE.md)
137 |
138 |
139 | ## Copyright
140 |
141 | [Loadsys Web Strategies](http://www.loadsys.com) 2016
142 |
--------------------------------------------------------------------------------
/composer.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "loadsys/cakephp_sitemap",
3 | "description": "A CakePHP Plugin for adding automatic XML and HTML Sitemaps to an app",
4 | "type": "cakephp-plugin",
5 | "keywords": [
6 | "cakephp",
7 | "sitemap"
8 | ],
9 | "license": "MIT",
10 | "homepage": "https://github.com/loadsys/CakePHP-Sitemap",
11 | "authors": [
12 | {
13 | "name": "Justin Yost",
14 | "homepage": "https://github.com/jtyost2",
15 | "email": "justin@loadsys.com",
16 | "role": "Developer"
17 | }
18 | ],
19 | "support": {
20 | "issues": "https://github.com/loadsys/CakePHP-Sitemap/issues",
21 | "source": "https://github.com/loadsys/CakePHP-Sitemap"
22 | },
23 | "require": {
24 | "php": ">=5.6.0",
25 | "cakephp/cakephp": "~3.1",
26 | "composer/installers": "~1.0"
27 | },
28 | "require-dev": {
29 | "phpunit/phpunit": "~4.8",
30 | "loadsys/loadsys_codesniffer": "~3.0",
31 | "satooshi/php-coveralls": "~2.0"
32 | },
33 | "autoload": {
34 | "psr-4": {
35 | "Sitemap\\": "src"
36 | }
37 | },
38 | "autoload-dev": {
39 | "psr-4": {
40 | "Cake\\Test\\": "vendor/cakephp/cakephp/test",
41 | "Sitemap\\Test\\": "tests"
42 | }
43 | }
44 | }
45 |
--------------------------------------------------------------------------------
/config/routes.php:
--------------------------------------------------------------------------------
1 | '/sitemap'],
7 | function ($routes) {
8 | $routes->extensions(['xml']);
9 | $routes->connect('/', [
10 | 'controller' => 'Sitemaps',
11 | 'plugin' => 'Sitemap',
12 | 'action' => 'index'
13 | ]);
14 | }
15 | );
16 |
--------------------------------------------------------------------------------
/phpunit.xml.dist:
--------------------------------------------------------------------------------
1 |
2 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 | ./tests/TestCase
18 |
19 |
20 |
21 |
22 |
23 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 | ./vendor/
36 | ./vendor/
37 |
38 | ./tests/
39 | ./tests/
40 |
41 |
42 |
43 |
44 |
--------------------------------------------------------------------------------
/src/Controller/AppController.php:
--------------------------------------------------------------------------------
1 | loadModel($table);
29 | $data[$table] = $tableInstance->find('forSitemap');
30 | }
31 |
32 | $this->set('data', $data);
33 | $this->set('_serialize', false);
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/src/Lib/Iterators/ExtFilteredDirIterator.php:
--------------------------------------------------------------------------------
1 | allowed = $allowedExtensions;
41 | }
42 | }
43 |
44 | /**
45 | * accept
46 | *
47 | * Called for each item in the Iterator. Will allow the entry if:
48 | * - It is not ./ or ../
49 | * - It is a sub-directory
50 | * - It is in the provided ::allowed array
51 | * - Or if no ::allowed array has been provided (which essentially allows all files and folders.)
52 | *
53 | * @access public
54 | * @return bool True for all non-dot directories, and files with extensions in $this->allowed.
55 | */
56 | public function accept() {
57 | $current = parent::current();
58 |
59 | return (
60 | !$current->isDot()
61 | &&
62 | (
63 | !is_array($this->allowed) // No extensions defined. Allow everything through.
64 | || in_array($current->getExtension(), $this->allowed) // Allow if extension is in provided list.
65 | || $current->isDir() // Allow if entry is a sub-directory.
66 | )
67 | );
68 | }
69 | }
70 |
--------------------------------------------------------------------------------
/src/Lib/Iterators/PagesIterator.php:
--------------------------------------------------------------------------------
1 | request->webroot from the calling Controller. Used
34 | * when generating individual record arrays.
35 | *
36 | * @var string
37 | */
38 | private $webroot = null;
39 |
40 | /**
41 | * __construct
42 | *
43 | * Creates a new ExtFilteredDirIterator (which is based on a
44 | * DirectoryIterator) that spits out image record arrays instead
45 | * of SplFileInfo objects. Suitable for passing directly to a
46 | * view and used in a foreach() loop directly.
47 | *
48 | * @param string $path Filesystem path for the folder to read.
49 | * @param array $depth An ordered array of intermediate folders
50 | * between the image root folder and the
51 | * current directory represented by
52 | * basename($path).
53 | * @param string $webroot The relative Cake webroot as returned
54 | * in a Controller by $this->request->webroot.
55 | * (There is no static access to this property,
56 | * hence having to pass it in.)
57 | * @param array $allowedExtensions An optional array of file extensions
58 | * to filter the resulting directory
59 | * list against.
60 | */
61 | public function __construct($path, $depth, $webroot, $allowedExtensions = null) {
62 | $this->depth = $depth;
63 | $this->webroot = $webroot;
64 | if (is_array($allowedExtensions)) {
65 | $this->allowed = $allowedExtensions; // Save this to pass into subfolder count calculations.
66 | }
67 |
68 | parent::__construct($path, $this->allowed);
69 | }
70 |
71 | /**
72 | * accept
73 | *
74 | * In addition to filtering by filename extension in our parent
75 | * ExtFilteredDirIterator, also filter out any .ctp files that begin with
76 | * 'admin_' to prevent modifying a management view.
77 | *
78 | * @return bool True if the basename of the current file does not begin with "admin_".
79 | */
80 | public function accept() {
81 | $current = parent::current();
82 |
83 | return parent::accept() && (strpos($current->getBasename('.ctp'), 'admin_') !== 0);
84 | }
85 |
86 | /**
87 | * current
88 | *
89 | * Takes a Fileinfo object representing a file or folder from the
90 | * $this->_standardPath folder and an array of elements representing the
91 | * intermediate folder names to the file from the "root" in order.
92 | * Generates a new "Page" record using this information and returns the
93 | * resulting array. Example returned record:
94 | *
95 | * array(
96 | * 'basename' => 'file.ext',
97 | * 'filename' => 'subfolder/file.ext',
98 | * 'title' => 'File',
99 | * 'url' => '/subfolder/file.ext',
100 | * 'bytes' => 14566,
101 | * 'modified' => 1383075606, // unix timestamp
102 | * );
103 | *
104 | * Folders will have an dditional [children] element that contains an
105 | * integer count of the (matching) folder contents from that sub-directory.
106 | *
107 | * @return array A fake [Page] record including basename, filename
108 | * (including relative path from the image root),
109 | * display_url (as an absolute URL without the FQDN), url
110 | * (relative to Cake's root folder), bytes, modified
111 | * (and 'children', in the case of directories) fields.
112 | */
113 | public function current() {
114 | $fileinfo = parent::current();
115 | $depth = $this->depth;
116 | $parent = implode('/', $depth);
117 | $url = str_replace(WWW_ROOT, $this->webroot, Router::url(array_merge(
118 | [
119 | 'plugin' => false,
120 | 'controller' => 'pages',
121 | 'action' => 'display',
122 | ],
123 | $depth,
124 | [$fileinfo->getBasename('.ctp')]
125 | )));
126 | $page = [
127 | 'basename' => $fileinfo->getFilename(),
128 | 'filename' => ltrim($parent . '/' . $fileinfo->getFilename(), '/'),
129 | 'title' => Inflector::humanize($fileinfo->getBasename('.ctp')),
130 | 'url' => str_replace($this->webroot, '/', $url),
131 | 'bytes' => $fileinfo->getSize(),
132 | 'modified' => $fileinfo->getMTime(),
133 | ];
134 | if ($fileinfo->isDir()) { // Override the target URL and get a count of the children in subdirs.
135 | $page['url'] = array_merge(
136 | [
137 | 'plugin' => false,
138 | 'controller' => 'pages',
139 | 'action' => 'index',
140 | ],
141 | $depth,
142 | [$page['basename']]
143 | );
144 | $page['children'] = iterator_count(new ExtFilteredDirIterator($fileinfo->getRealPath(), $this->allowed));
145 | }
146 |
147 | return $page;
148 | }
149 | }
150 |
--------------------------------------------------------------------------------
/src/Model/Behavior/SitemapBehavior.php:
--------------------------------------------------------------------------------
1 | 'default',
27 | 'lastmod' => 'modified',
28 | 'changefreq' => 'daily',
29 | 'priority' => '0.9',
30 | 'conditions' => [],
31 | 'order' => [],
32 | 'fields' => [],
33 | 'implementedMethods' => [
34 | 'getUrl' => 'returnUrlForEntity',
35 | ],
36 | 'implementedFinders' => [
37 | 'forSitemap' => 'findSitemapRecords',
38 | ],
39 | ];
40 |
41 | /**
42 | * Constructor
43 | *
44 | * Merges config with the default and store in the config property
45 | *
46 | * @param \Cake\ORM\Table $table The table this behavior is attached to.
47 | * @param array $config The config for this behavior.
48 | */
49 | public function __construct(Table $table, array $config = []) {
50 | parent::__construct($table, $config);
51 | }
52 |
53 | /**
54 | * Constructor hook method.
55 | *
56 | * Implement this method to avoid having to overwrite
57 | * the constructor and call parent.
58 | *
59 | * @param array $config The configuration settings provided to this behavior.
60 | * @return void
61 | */
62 | public function initialize(array $config) {
63 | parent::initialize($config);
64 | }
65 |
66 | /**
67 | * Return the URL for the primary view action for an Entity.
68 | *
69 | * @param \Cake\ORM\Entity $entity Entity object passed in to return the url for.
70 | * @return string Returns the URL string.
71 | */
72 | public function returnUrlForEntity(Entity $entity) {
73 | return Router::url(
74 | [
75 | 'plugin' => null,
76 | 'prefix' => null,
77 | 'controller' => $this->_table->alias(),
78 | 'action' => 'view',
79 | $entity->{$this->_table->primaryKey()},
80 | ],
81 | true
82 | );
83 | }
84 |
85 | /**
86 | * Find the Sitemap Records for a Table.
87 | *
88 | * @param \Cake\ORM\Query $query The Query being modified.
89 | * @param array $options The array of options for the find.
90 | * @return \Cake\ORM\Query Returns the modified Query object.
91 | */
92 | public function findSitemapRecords(Query $query, array $options) {
93 | $query = $query
94 | ->where($this->_config['conditions'])
95 | ->cache("sitemap_{$query->repository()->alias()}", $this->_config['cacheConfigKey'])
96 | ->order($this->_config['order'])
97 | ->formatResults(function ($results) {
98 | return $this->mapResults($results);
99 | });
100 |
101 | if (!empty($this->_config['fields'])) {
102 | $query = $query->select($this->_config['fields']);
103 | }
104 |
105 | return $query;
106 | }
107 |
108 | /**
109 | * Format Results method to take the ResultSetInterface and map it to add
110 | * calculated fields for the Sitemap.
111 | *
112 | * @param \Cake\Datasource\ResultSetInterface $results The results of a Query
113 | * operation.
114 | * @return \Cake\Collection\CollectionInterface Returns the modified collection
115 | * of Results.
116 | */
117 | public function mapResults(ResultSetInterface $results) {
118 | return $results->map(function ($entity) {
119 | return $this->mapEntity($entity);
120 | });
121 | }
122 |
123 | /**
124 | * Modify an entity with new `_` fields for the Sitemap display.
125 | *
126 | * @param \Cake\ORM\Entity $entity The entity being modified.
127 | * @return \Cake\ORM\Entity Returns the modified entity.
128 | */
129 | public function mapEntity(Entity $entity) {
130 | $entity['_loc'] = $this->_table->getUrl($entity);
131 | $entity['_lastmod'] = $entity->{$this->_config['lastmod']};
132 | $entity['_changefreq'] = $this->_config['changefreq'];
133 | $entity['_priority'] = $this->_config['priority'];
134 |
135 | return $entity;
136 | }
137 | }
138 |
--------------------------------------------------------------------------------
/src/Template/Sitemaps/index.ctp:
--------------------------------------------------------------------------------
1 |
2 | = __('Sitemap') ?>
3 |
4 |
5 | $dataForKey): ?>
6 | = __($key) ?>
7 |
8 |
9 |
10 |
11 | = __('Location') ?>
12 | |
13 |
14 | = __('Priority') ?>
15 | |
16 |
17 | = __('Change Frequency') ?>
18 | |
19 |
20 | = __('Last Modified') ?>
21 | |
22 |
23 |
24 |
25 |
26 |
27 |
28 | = h($record->_loc) ?>
29 | |
30 |
31 | = h($record->_priority) ?>
32 | |
33 |
34 | = h($record->_changefreq) ?>
35 | |
36 |
37 | = h($record->_lastmod) ?>
38 | |
39 |
40 |
41 |
42 |
43 |
44 |
--------------------------------------------------------------------------------
/src/Template/Sitemaps/xml/index.ctp:
--------------------------------------------------------------------------------
1 | ' ?>
2 |
3 | $dataForKey): ?>
4 |
5 |
6 |
7 |
8 | = h($record->_loc) ?>
9 |
10 |
11 | = h($record->_lastmod) ?>
12 |
13 |
14 | = h($record->_changefreq) ?>
15 |
16 |
17 | = h($record->_priority) ?>
18 |
19 |
20 |
21 |
22 |
23 |
--------------------------------------------------------------------------------
/tests/App/Template/Layout/default.ctp:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | = $this->Html->charset() ?>
5 |
6 |
7 | = $this->fetch('title') ?>
8 |
9 | = $this->Html->meta('icon') ?>
10 |
11 | = $this->fetch('meta') ?>
12 | = $this->fetch('css') ?>
13 | = $this->fetch('script') ?>
14 |
15 |
16 | = $this->Flash->render() ?>
17 |
18 | = $this->fetch('content') ?>
19 |
20 |
21 |
22 |
--------------------------------------------------------------------------------
/tests/Fixture/PagesFixture.php:
--------------------------------------------------------------------------------
1 | ['type' => 'uuid', 'length' => null, 'null' => false, 'default' => null, 'comment' => 'UUID primary key.', 'precision' => null],
21 | 'title' => ['type' => 'string', 'length' => 255, 'null' => false, 'default' => null, 'comment' => 'Title of the record.', 'precision' => null, 'fixed' => null],
22 | 'keywords' => ['type' => 'string', 'length' => 255, 'null' => false, 'default' => null, 'comment' => 'Meta keywords of the record.', 'precision' => null, 'fixed' => null],
23 | 'description' => ['type' => 'string', 'length' => 255, 'null' => false, 'default' => null, 'comment' => 'Meta description of the record.', 'precision' => null, 'fixed' => null],
24 | 'body' => ['type' => 'text', 'length' => null, 'null' => false, 'default' => null, 'comment' => 'Main (HTML) body content for this page.', 'precision' => null],
25 | 'is_indexed' => ['type' => 'boolean', 'length' => null, 'null' => false, 'default' => '1', 'comment' => 'Boolean field that determines if a Page is indexable by search engines.', 'precision' => null],
26 | 'created' => ['type' => 'datetime', 'length' => null, 'null' => true, 'default' => null, 'comment' => 'Date and time of record creation.', 'precision' => null],
27 | 'modified' => ['type' => 'datetime', 'length' => null, 'null' => true, 'default' => null, 'comment' => 'Date and time of last modification.', 'precision' => null],
28 | '_constraints' => [
29 | 'primary' => ['type' => 'primary', 'columns' => ['id'], 'length' => []],
30 | ],
31 | '_options' => [
32 | 'engine' => 'InnoDB',
33 | 'collation' => 'utf8_general_ci'
34 | ],
35 | ];
36 | // @codingStandardsIgnoreEnd
37 |
38 | /**
39 | * Records
40 | *
41 | * @var array
42 | */
43 | public $records = [
44 | [
45 | 'id' => '3b65a356-6df8-11e5-b2cc-000c29a33c4c',
46 | 'title' => 'Test Page 1',
47 | 'keywords' => 'Lorem ipsum dolor sit amet',
48 | 'description' => 'Lorem ipsum dolor sit amet',
49 | 'body' => 'Lorem ipsum dolor sit amet, aliquet feugiat. Convallis morbi fringilla gravida, phasellus feugiat dapibus velit nunc, pulvinar eget sollicitudin venenatis cum nullam, vivamus ut a sed, mollitia lectus. Nulla vestibulum massa neque ut et, id hendrerit sit, feugiat in taciti enim proin nibh, tempor dignissim, rhoncus duis vestibulum nunc mattis convallis.',
50 | 'is_indexed' => 1,
51 | 'created' => '2015-10-08 21:27:04',
52 | 'modified' => '2015-10-08 21:27:04',
53 | ],
54 | [
55 | 'id' => '55057c21-eab2-40f1-bea9-b24f7bc4f74e',
56 | 'title' => 'Test Page 2',
57 | 'keywords' => 'Lorem ipsum dolor sit amet',
58 | 'description' => 'Lorem ipsum dolor sit amet',
59 | 'body' => 'Lorem ipsum dolor sit amet, aliquet feugiat. Convallis morbi fringilla gravida, phasellus feugiat dapibus velit nunc, pulvinar eget sollicitudin venenatis cum nullam, vivamus ut a sed, mollitia lectus. Nulla vestibulum massa neque ut et, id hendrerit sit, feugiat in taciti enim proin nibh, tempor dignissim, rhoncus duis vestibulum nunc mattis convallis.',
60 | 'is_indexed' => 1,
61 | 'created' => '2015-10-08 21:27:04',
62 | 'modified' => '2015-10-08 21:27:04',
63 | ],
64 | [
65 | 'id' => '05f79d0f-efd1-49b4-b627-c8721bdcc635',
66 | 'title' => 'Test Page 3',
67 | 'keywords' => 'Lorem ipsum dolor sit amet',
68 | 'description' => 'Lorem ipsum dolor sit amet',
69 | 'body' => 'Lorem ipsum dolor sit amet, aliquet feugiat. Convallis morbi fringilla gravida, phasellus feugiat dapibus velit nunc, pulvinar eget sollicitudin venenatis cum nullam, vivamus ut a sed, mollitia lectus. Nulla vestibulum massa neque ut et, id hendrerit sit, feugiat in taciti enim proin nibh, tempor dignissim, rhoncus duis vestibulum nunc mattis convallis.',
70 | 'is_indexed' => 1,
71 | 'created' => '2015-10-08 21:27:04',
72 | 'modified' => '2015-10-08 21:27:04',
73 | ],
74 | [
75 | 'id' => '05f79d0f-efd2-49b4-b627-c8721bdcc635',
76 | 'title' => 'Test Page 4',
77 | 'keywords' => 'Lorem ipsum dolor sit amet',
78 | 'description' => 'Lorem ipsum dolor sit amet',
79 | 'body' => 'Lorem ipsum dolor sit amet, aliquet feugiat. Convallis morbi fringilla gravida, phasellus feugiat dapibus velit nunc, pulvinar eget sollicitudin venenatis cum nullam, vivamus ut a sed, mollitia lectus. Nulla vestibulum massa neque ut et, id hendrerit sit, feugiat in taciti enim proin nibh, tempor dignissim, rhoncus duis vestibulum nunc mattis convallis.',
80 | 'is_indexed' => 1,
81 | 'created' => '2015-10-08 21:27:04',
82 | 'modified' => '2015-10-08 21:27:04',
83 | ],
84 | [
85 | 'id' => '25f79d0f-efd2-49b4-b627-c8721bdcc635',
86 | 'title' => 'Test Page 5',
87 | 'keywords' => 'Lorem ipsum dolor sit amet',
88 | 'description' => 'Lorem ipsum dolor sit amet',
89 | 'body' => 'Lorem ipsum dolor sit amet, aliquet feugiat. Convallis morbi fringilla gravida, phasellus feugiat dapibus velit nunc, pulvinar eget sollicitudin venenatis cum nullam, vivamus ut a sed, mollitia lectus. Nulla vestibulum massa neque ut et, id hendrerit sit, feugiat in taciti enim proin nibh, tempor dignissim, rhoncus duis vestibulum nunc mattis convallis.',
90 | 'is_indexed' => 0,
91 | 'created' => '2015-10-08 21:27:04',
92 | 'modified' => '2015-10-08 21:27:04',
93 | ],
94 | ];
95 | }
96 |
--------------------------------------------------------------------------------
/tests/TestCase/Controller/SitemapsControllerTest.php:
--------------------------------------------------------------------------------
1 | Pages = TableRegistry::get('Pages');
36 | $sitemapConfig = [];
37 | $this->Pages->addBehavior('Sitemap.Sitemap', $sitemapConfig);
38 | $this->setupSession();
39 | }
40 |
41 | /**
42 | * tearDown method
43 | *
44 | * @return void
45 | */
46 | public function tearDown() {
47 | unset($this->Pages);
48 |
49 | Configure::clear();
50 |
51 | parent::tearDown();
52 | }
53 |
54 | /**
55 | * setup Session properties needed for the controller level tests
56 | *
57 | * @return void
58 | */
59 | public function setupSession() {
60 | $this->session([]);
61 | }
62 |
63 | /**
64 | * Test index method with no models defined
65 | *
66 | * @return void
67 | * @covers \Sitemap\Controller\SitemapsController::index
68 | */
69 | public function testIndexNoModels() {
70 | $Controller = $this->getMock(
71 | '\Sitemap\Controller\SitemapsController',
72 | ['set']
73 | );
74 |
75 | $Controller->expects($this->at(0))
76 | ->method('set')
77 | ->with('data', [])
78 | ->will($this->returnValue(true));
79 |
80 | $Controller->expects($this->at(1))
81 | ->method('set')
82 | ->with('_serialize', false)
83 | ->will($this->returnValue(true));
84 |
85 | $Controller->index();
86 | }
87 |
88 | /**
89 | * Test index method with no models defined
90 | *
91 | * @return void
92 | * @covers \Sitemap\Controller\SitemapsController::index
93 | */
94 | public function testIndexWithModels() {
95 | Configure::write('Sitemap.tables', ['Pages']);
96 | $pagesFindQuery = $this->Pages->find('forSitemap');
97 |
98 | $Controller = $this->getMock(
99 | '\Sitemap\Controller\SitemapsController',
100 | ['set', 'loadModel']
101 | );
102 |
103 | $Controller->expects($this->at(1))
104 | ->method('set')
105 | ->with('data', ['Pages' => $pagesFindQuery])
106 | ->will($this->returnValue(true));
107 |
108 | $Controller->expects($this->at(2))
109 | ->method('set')
110 | ->with('_serialize', false)
111 | ->will($this->returnValue(true));
112 |
113 | $Controller->expects($this->once())
114 | ->method('loadModel')
115 | ->with('Pages')
116 | ->willReturn($this->Pages);
117 |
118 | $Controller->Pages = $this->Pages;
119 |
120 | $Controller->index();
121 | }
122 |
123 | /**
124 | * Test the index method that the correct route is loaded and works.
125 | *
126 | * @return void
127 | * @covers \Sitemap\Controller\SitemapsController::index
128 | */
129 | public function testIndexAccess() {
130 | $this->get('/sitemap.xml');
131 |
132 | $this->assertResponseOk();
133 | }
134 |
135 | /**
136 | * Test that the index method can execute finds on namespaced plugin tables
137 | *
138 | * @return void
139 | */
140 | public function testLoadingPluginTables() {
141 | $exampleTableName = 'Example/Plugin.Posts';
142 | Configure::write('Sitemap.tables', [$exampleTableName]);
143 |
144 | $tableInstance = new Table([
145 | 'registryAlias' => 'Example/Plugin.Posts',
146 | 'alias' => 'Posts',
147 | 'table' => 'posts',
148 | 'schema' => new TableSchema('posts', [
149 | 'id' => ['type' => 'integer'],
150 | 'title' => ['type' => 'string'],
151 | ]),
152 | ]);
153 | $tableInstance->addBehavior('Sitemap.Sitemap');
154 |
155 | $Controller = $this->getMockBuilder(SitemapsController::class)
156 | ->setMethods(['loadModel'])
157 | ->getMock();
158 |
159 | $Controller->expects($this->once())
160 | ->method('loadModel')
161 | ->with($exampleTableName)
162 | ->willReturn($tableInstance);
163 |
164 | $Controller->index();
165 | }
166 | }
167 |
--------------------------------------------------------------------------------
/tests/TestCase/Model/Behavior/SitemapBehaviorTest.php:
--------------------------------------------------------------------------------
1 | Pages = TableRegistry::get('Pages');
54 | $sitemapConfig = [];
55 | $this->Pages->addBehavior('Sitemap.Sitemap', $sitemapConfig);
56 | }
57 |
58 | /**
59 | * tearDown method
60 | *
61 | * @return void
62 | */
63 | public function tearDown() {
64 | unset($this->Pages);
65 |
66 | parent::tearDown();
67 | }
68 |
69 | /**
70 | * Test initial setup, essentially verify configuartion is passing in correctly.
71 | *
72 | * @return void
73 | */
74 | public function testInitialization() {
75 | $sitemapConfig = [];
76 | $configOut = [
77 | 'cacheConfigKey' => 'default',
78 | 'lastmod' => 'modified',
79 | 'changefreq' => 'daily',
80 | 'priority' => '0.9',
81 | 'conditions' => [],
82 | 'order' => [],
83 | 'fields' => [],
84 | 'implementedMethods' => [
85 | 'getUrl' => 'returnUrlForEntity',
86 | ],
87 | 'implementedFinders' => [
88 | 'forSitemap' => 'findSitemapRecords',
89 | ],
90 | ];
91 |
92 | $Sitemap = new TestSitemapBehavior($this->Pages, $sitemapConfig);
93 | $this->assertEquals(
94 | $configOut,
95 | $Sitemap->_config,
96 | 'On an empty configuration array, the default configuration should be equal'
97 | );
98 |
99 | $sitemapConfig = [
100 | 'cacheConfigKey' => 'canary',
101 | 'lastmod' => 'lastmoddate',
102 | 'fields' => ['id', 'modified'],
103 | ];
104 | $configOut = [
105 | 'cacheConfigKey' => 'canary',
106 | 'lastmod' => 'lastmoddate',
107 | 'changefreq' => 'daily',
108 | 'priority' => '0.9',
109 | 'conditions' => [],
110 | 'order' => [],
111 | 'fields' => ['id', 'modified'],
112 | 'implementedMethods' => [
113 | 'getUrl' => 'returnUrlForEntity',
114 | ],
115 | 'implementedFinders' => [
116 | 'forSitemap' => 'findSitemapRecords',
117 | ],
118 | ];
119 |
120 | $Sitemap = new TestSitemapBehavior($this->Pages, $sitemapConfig);
121 | $this->assertEquals(
122 | $configOut,
123 | $Sitemap->_config,
124 | 'On an valid configuration array, the configuration should be updated to match.'
125 | );
126 | }
127 |
128 | /**
129 | * Test returnUrlForEntity method
130 | *
131 | * @return void
132 | */
133 | public function testReturnUrlForEntity() {
134 | $id = 'canary';
135 | $entity = new Entity([
136 | 'id' => $id,
137 | ]);
138 | $this->assertEquals(
139 | "/pages/view/{$id}",
140 | $this->Pages->getUrl($entity),
141 | 'The method getUrl should return a valid url string for the passed entity.'
142 | );
143 | }
144 |
145 | /**
146 | * Test the url for a plugin entity is correct
147 | *
148 | * @return void
149 | */
150 | public function testReturnUrlForPluginEntity() {
151 | Router::connect('/posts/view/:id', ['controller' => 'Posts', 'action' => 'view'], ['id' => '[\d]+', 'pass' => ['id']]);
152 |
153 | $exampleTableName = 'Example/Plugin.Posts';
154 | Configure::write('Sitemap.tables', [$exampleTableName]);
155 |
156 | $tableInstance = new Table([
157 | 'registryAlias' => 'Example/Plugin.Posts',
158 | 'alias' => 'Posts',
159 | 'table' => 'posts',
160 | 'schema' => new TableSchema('posts', [
161 | 'id' => ['type' => 'integer'],
162 | 'title' => ['type' => 'string'],
163 | ]),
164 | ]);
165 | $tableInstance->setPrimaryKey('id');
166 | $tableInstance->addBehavior('Sitemap.Sitemap');
167 |
168 | $post = new Entity([
169 | 'id' => 1,
170 | 'title' => 'First post',
171 | ], [
172 | 'source' => $tableInstance,
173 | ]);
174 |
175 | $expected = '/posts/view/1';
176 |
177 | $behavior = $tableInstance->behaviors()->get('Sitemap');
178 |
179 | $this->assertEquals($expected, $behavior->returnUrlForEntity($post));
180 | }
181 |
182 | /**
183 | * Test ::findSitemapRecords when the Fields config is set.
184 | *
185 | * @return void
186 | */
187 | public function testFindSitemapRecordsFields() {
188 | $configs = [
189 | 'conditions' => [
190 | 'field1' => 'value1',
191 | ],
192 | 'cacheConfigKey' => 'cache-key-asdf',
193 | 'order' => [
194 | 'field2' => 'value2',
195 | ],
196 | 'fields' => [
197 | 'field3' => 'value3',
198 | ],
199 | ];
200 | $options = [
201 | 'foo' => 'baz',
202 | ];
203 |
204 | $TableMock = $this
205 | ->getMockBuilder('\Cake\ORM\Table')
206 | ->disableOriginalConstructor()
207 | ->getMock();
208 | $SitemapBehavior = $this
209 | ->getMockBuilder('\Sitemap\Model\Behavior\SitemapBehavior')
210 | ->setMethods(['mapResults'])
211 | ->setConstructorArgs([$TableMock, $configs])
212 | ->getMock();
213 |
214 | $QueryMock = $this->getMockBuilder('\Cake\ORM\Query')
215 | ->setMethods([
216 | 'where',
217 | 'cache',
218 | 'order',
219 | 'formatResults',
220 | 'select',
221 | 'repository',
222 | 'alias',
223 | ])
224 | ->disableOriginalConstructor()
225 | ->getMock();
226 |
227 | $QueryMock->expects($this->once())
228 | ->method('where')
229 | ->with($configs['conditions'])
230 | ->will($this->returnSelf());
231 | $QueryMock->expects($this->once())
232 | ->method('repository')
233 | ->with()
234 | ->will($this->returnSelf());
235 | $QueryMock->expects($this->once())
236 | ->method('alias')
237 | ->with()
238 | ->will($this->returnValue('alias-canary'));
239 | $QueryMock->expects($this->once())
240 | ->method('cache')
241 | ->with('sitemap_alias-canary', $configs['cacheConfigKey'])
242 | ->will($this->returnSelf());
243 | $QueryMock->expects($this->once())
244 | ->method('order')
245 | ->with($configs['order'])
246 | ->will($this->returnSelf());
247 | $QueryMock->expects($this->once())
248 | ->method('formatResults')
249 | ->with($this->isInstanceOf('closure'))
250 | ->will($this->returnSelf());
251 | $QueryMock->expects($this->once())
252 | ->method('select')
253 | ->with($configs['fields'])
254 | ->will($this->returnValue('canary'));
255 |
256 | $output = $SitemapBehavior->findSitemapRecords($QueryMock, $options);
257 | $this->assertEquals(
258 | 'canary',
259 | $output,
260 | 'The output from ::findSitemapRecords should be our mocked response from the Query Object.'
261 | );
262 | }
263 |
264 | /**
265 | * Test ::findSitemapRecords when the Fields config is not set.
266 | *
267 | * @return void
268 | */
269 | public function testFindSitemapRecordsNoFields() {
270 | $configs = [
271 | 'conditions' => [
272 | 'field1' => 'value1',
273 | ],
274 | 'cacheConfigKey' => 'cache-key-asdf',
275 | 'order' => [
276 | 'field2' => 'value2',
277 | ],
278 | ];
279 | $options = [
280 | 'foo' => 'baz',
281 | ];
282 |
283 | $TableMock = $this
284 | ->getMockBuilder('\Cake\ORM\Table')
285 | ->disableOriginalConstructor()
286 | ->getMock();
287 | $SitemapBehavior = $this
288 | ->getMockBuilder('\Sitemap\Model\Behavior\SitemapBehavior')
289 | ->setMethods(['mapResults'])
290 | ->setConstructorArgs([$TableMock, $configs])
291 | ->getMock();
292 |
293 | $QueryMock = $this->getMockBuilder('\Cake\ORM\Query')
294 | ->setMethods([
295 | 'where',
296 | 'cache',
297 | 'order',
298 | 'formatResults',
299 | 'select',
300 | 'repository',
301 | 'alias',
302 | ])
303 | ->disableOriginalConstructor()
304 | ->getMock();
305 |
306 | $QueryMock->expects($this->once())
307 | ->method('where')
308 | ->with($configs['conditions'])
309 | ->will($this->returnSelf());
310 | $QueryMock->expects($this->once())
311 | ->method('repository')
312 | ->with()
313 | ->will($this->returnSelf());
314 | $QueryMock->expects($this->once())
315 | ->method('alias')
316 | ->with()
317 | ->will($this->returnValue('alias-canary'));
318 | $QueryMock->expects($this->once())
319 | ->method('cache')
320 | ->with('sitemap_alias-canary', $configs['cacheConfigKey'])
321 | ->will($this->returnSelf());
322 | $QueryMock->expects($this->once())
323 | ->method('order')
324 | ->with($configs['order'])
325 | ->will($this->returnSelf());
326 | $QueryMock->expects($this->once())
327 | ->method('formatResults')
328 | ->with($this->isInstanceOf('closure'))
329 | ->will($this->returnValue('canary'));
330 | $QueryMock->expects($this->never())
331 | ->method('select');
332 |
333 | $output = $SitemapBehavior->findSitemapRecords($QueryMock, $options);
334 | $this->assertEquals(
335 | 'canary',
336 | $output,
337 | 'The output from ::findSitemapRecords should be our mocked response from the Query Object.'
338 | );
339 | }
340 |
341 | /**
342 | * Test the mapEntity method.
343 | *
344 | * @return void
345 | */
346 | public function testMapEntity() {
347 | $pageId = '3b65a356-6df8-11e5-b2cc-000c29a33c4c';
348 | $sitemapConfig = [];
349 | $Sitemap = new TestSitemapBehavior($this->Pages, $sitemapConfig);
350 |
351 | $entity = $this->Pages->get($pageId);
352 | $entity = $Sitemap->mapEntity($entity);
353 | $this->assertEquals(
354 | "/pages/view/{$pageId}",
355 | $entity->_loc,
356 | 'The _loc field should be set to our standard url'
357 | );
358 |
359 | $this->assertEquals(
360 | new Time('2015-10-08 21:27:04'),
361 | $entity->_lastmod,
362 | 'The _loc field should be set to our standard url'
363 | );
364 |
365 | $this->assertEquals(
366 | "daily",
367 | $entity->_changefreq,
368 | 'The _changefreq field should be set to our standard daily'
369 | );
370 |
371 | $this->assertEquals(
372 | "0.9",
373 | $entity->_priority,
374 | 'The _priority field should be set to our standard 0.9'
375 | );
376 | unset($Sitemap);
377 |
378 | // test with a modified Sitemap Configuration
379 | $sitemapConfig = [
380 | 'priority' => '0.1',
381 | 'changefreq' => 'weekly',
382 | ];
383 | $Sitemap = new TestSitemapBehavior($this->Pages, $sitemapConfig);
384 |
385 | $entity = $this->Pages->get($pageId);
386 | $entity = $Sitemap->mapEntity($entity);
387 | $this->assertEquals(
388 | "/pages/view/{$pageId}",
389 | $entity->_loc,
390 | 'The _loc field should be set to our standard url'
391 | );
392 |
393 | $this->assertEquals(
394 | new Time('2015-10-08 21:27:04'),
395 | $entity->_lastmod,
396 | 'The _loc field should be set to our standard url'
397 | );
398 |
399 | $this->assertEquals(
400 | "weekly",
401 | $entity->_changefreq,
402 | 'The _changefreq field should be set to our modified weekly'
403 | );
404 |
405 | $this->assertEquals(
406 | "0.1",
407 | $entity->_priority,
408 | 'The _priority field should be set to our modified 0.1'
409 | );
410 | }
411 | }
412 |
--------------------------------------------------------------------------------
/tests/bootstrap.php:
--------------------------------------------------------------------------------
1 | 'App',
22 | 'encoding' => 'UTF-8',
23 | 'paths' => [
24 | 'templates' => [APP . 'Template' . DS],
25 | ],
26 | ]);
27 | Cake\Core\Configure::write('debug', true);
28 |
29 | date_default_timezone_set('UTC');
30 | mb_internal_encoding('UTF-8');
31 |
32 | $Tmp = new Cake\Filesystem\Folder(TMP);
33 | $Tmp->create(TMP . 'cache/models', 0770);
34 | $Tmp->create(TMP . 'cache/persistent', 0770);
35 | $Tmp->create(TMP . 'cache/views', 0770);
36 |
37 | $cache = [
38 | 'default' => [
39 | 'engine' => 'File',
40 | 'path' => CACHE,
41 | ],
42 | '_cake_core_' => [
43 | 'className' => 'File',
44 | 'prefix' => 'sitemap_',
45 | 'path' => CACHE . 'persistent/',
46 | 'serialize' => true,
47 | 'duration' => '+10 seconds',
48 | ],
49 | '_cake_model_' => [
50 | 'className' => 'File',
51 | 'prefix' => 'sitemap_',
52 | 'path' => CACHE . 'models/',
53 | 'serialize' => 'File',
54 | 'duration' => '+10 seconds',
55 | ],
56 | ];
57 |
58 | Cake\Cache\Cache::config($cache);
59 |
60 | Cake\Core\Plugin::load('Sitemap', ['path' => ROOT . DS, 'routes' => true]);
61 |
62 | // Ensure default test connection is defined
63 | if (!getenv('db_class')) {
64 | putenv('db_class=Cake\Database\Driver\Sqlite');
65 | putenv('db_dsn=sqlite::memory:');
66 | }
67 |
68 | Cake\Datasource\ConnectionManager::config('test', [
69 | 'className' => 'Cake\Database\Connection',
70 | 'driver' => getenv('db_class'),
71 | 'dsn' => getenv('db_dsn'),
72 | 'database' => getenv('db_database'),
73 | 'username' => getenv('db_username'),
74 | 'password' => getenv('db_password'),
75 | 'timezone' => 'UTC',
76 | 'quoteIdentifiers' => true,
77 | 'cacheMetadata' => true,
78 | ]);
79 |
80 | Cake\Routing\DispatcherFactory::add('Routing');
81 |
82 | class_alias('Cake\Controller\Controller', 'App\Controller\AppController');
83 |
--------------------------------------------------------------------------------
/tests/config/routes.php:
--------------------------------------------------------------------------------
1 | connect('/pages/view/:id', [
8 | 'controller' => 'Pages',
9 | 'action' => 'view',
10 | ], ['pass' => ['id']]);
11 | });
12 |
13 | require ROOT . DS . 'config/routes.php';
14 |
--------------------------------------------------------------------------------
/webroot/empty:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/loadsys/CakePHP-Sitemap/0b0a4ad359898757eef745f92ad9456b6b2581b5/webroot/empty
--------------------------------------------------------------------------------