├── .gitignore ├── CHANGELOG.md ├── LICENSE ├── README.md ├── blueprints.yaml ├── blueprints └── sitemap.yaml ├── classes └── SitemapEntry.php ├── composer.json ├── composer.lock ├── hebe.json ├── languages.yaml ├── pages └── sitemap.md ├── sitemap-news.xsl ├── sitemap.php ├── sitemap.xsl ├── sitemap.yaml ├── templates ├── sitemap-extensions │ ├── image.xml.twig │ └── news.xml.twig ├── sitemap-news.xml.twig ├── sitemap.html.twig ├── sitemap.json.twig └── sitemap.xml.twig └── vendor ├── autoload.php └── composer ├── ClassLoader.php ├── InstalledVersions.php ├── LICENSE ├── autoload_classmap.php ├── autoload_namespaces.php ├── autoload_psr4.php ├── autoload_real.php ├── autoload_static.php ├── installed.json ├── installed.php └── platform_check.php /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # v5.1.0 2 | ## 06/17/24 3 | 4 | 1. [](#new) 5 | * Added page-level `lastmod` options [#113](https://github.com/getgrav/grav-plugin-sitemap/pull/113) 6 | 1. [](#improved) 7 | * Updated README.md with page override options. 8 | 9 | # v5.0.0 10 | ## 10/05/2023 11 | 12 | 1. [](#new) 13 | * New capability to support "standalone" Sitemap News pages 14 | * New XSL format for Sitemap News 15 | * Added a toggle for XSL transform support to be disabled 16 | 1. [](#improved) 17 | * Improved blueprint and created sections 18 | * Hide `x-default` alternate links when `include_default_lang` is enabled 19 | 1. [](#bugfix) 20 | * Fixed an issue with translated links were not being handled properly and not showing all alternate languages 21 | 22 | # v4.0.0 23 | ## 09/22/2023 24 | 25 | 1. [](#new) 26 | * Added support for Google News tags [#93](https://github.com/getgrav/grav-plugin-sitemap/pull/93) 27 | * Enhanced Google News to allow restriction to paths and configurable max-age in days 28 | 1. [](#improved) 29 | * Added YAML syntax highlighting in README.md [#101](https://github.com/getgrav/grav-plugin-sitemap/pull/101) 30 | 31 | # v3.0.2 32 | ## 06/14/2022 33 | 34 | 1. [](#new) 35 | * Added new `route:` field to JSON format sitemap 36 | 1. [](#bugfix) 37 | * Fixed an issue with `x-default` entry not working with non-string based language code 38 | 39 | # v3.0.1 40 | ## 02/23/2021 41 | 42 | 1. [](#new) 43 | * Added ability to disable multi-lang completely to replicate prior functionality [#96](https://github.com/getgrav/grav-plugin-sitemap/pull/96) 44 | * Added support for new optional `html_support` option that allows you to render the sitemap as an HTML page in your site when you access the sitemap URL with no extension or `.html`. Can be customized and extended in your theme as needed. 45 | 46 | # v3.0.0 47 | ## 01/30/2021 48 | 49 | 1. [](#new) 50 | * Added complete multi-language support utilizing [Google Search recommended SEO best-practices](https://developers.google.com/search/docs/advanced/crawling/localized-versions?hl=en&visit_id=637468720624267418-280936473&rd=2) for bi-directional linking to translated pages. 51 | * Added support fo `x-default` hreflang entries. 52 | * Added support for new `sitemap.json` custom format that is useful for other plugins to understand the multi-language structure of the site 53 | * Added support for sitemap images per [Google guidelines](https://developers.google.com/search/docs/advanced/sitemaps/image-sitemaps) [#81](https://github.com/getgrav/grav-plugin-sitemap/pull/81) 54 | 55 | # v2.0.2 56 | ## 12/02/2020 57 | 58 | 1. [](#improved) 59 | * Improved readme/blueprints with regex information [#78](https://github.com/getgrav/grav-plugin-sitemap/pull/78) 60 | 61 | # v2.0.1 62 | ## 07/01/2020 63 | 64 | 1. [](#bugfix) 65 | * Fixed a case issue with `SitemapEntry` PHP class 66 | 67 | # v2.0.0 68 | ## 07/01/2020 69 | 70 | 1. [](#new) 71 | * Added a new `Ignore External URLs` option that defaults to `true` 72 | * Added a new `Ignore Protected Pages` option that defaults to `true` [#62](https://github.com/getgrav/grav-plugin-sitemap/issues/62) 73 | * Added a new `onSitemapProcessed()` event to allow for dynamic manipulation of the sitemap 74 | 1. [](#improved) 75 | * Improved `SitemapEntry` to allow setting via constructor 76 | * Added `changefreq` and `priority` to manually and dynamically added entries 77 | * Use composer for autoloading 78 | 1. [](#bugfix) 79 | * Force a fallback to `en` to ensure you can't get `null/false` language [#74](https://github.com/getgrav/grav-plugin-sitemap/issues/74) 80 | 81 | # v1.9.5 82 | ## 04/27/2020 83 | 84 | 1. [](#improved) 85 | * Add admin toggle for `ignore` [#68](https://github.com/getgrav/grav-plugin-sitemap/pull/68) 86 | * Omit empty `` [#70](https://github.com/getgrav/grav-plugin-sitemap/pull/70) 87 | * Added Chinese language [#73](https://github.com/getgrav/grav-plugin-sitemap/pull/73) 88 | * Added German language [#66](https://github.com/getgrav/grav-plugin-sitemap/pull/66) 89 | 90 | # v1.9.4 91 | ## 10/19/2019 92 | 93 | 1. [](#bugfix) 94 | * Fixed a regression issue that caused sitemap not to work 'unless' you had an existing `sitemap` page [#65](https://github.com/getgrav/grav-plugin-sitemap/issues/65) 95 | 96 | # v1.9.3 97 | ## 10/18/2019 98 | 99 | 1. [](#improved) 100 | * Support existing `sitemap` HTML page to be used in place of XSL version 101 | * Remove `/` from end of home URLS [#58](https://github.com/getgrav/grav-plugin-sitemap/pull/58) 102 | * Include translated pages only [#57](https://github.com/getgrav/grav-plugin-sitemap/pull/57) 103 | * Make sure `modular` pages are not included in configuration [#56](https://github.com/getgrav/grav-plugin-sitemap/pull/56) 104 | 105 | # v1.9.2 106 | ## 05/09/2019 107 | 108 | 1. [](#improved) 109 | * Enhanced HTML layout with XSL transformation [#24](https://github.com/getgrav/grav-plugin-sitemap/pull/24) 110 | * Global toggles for change frequency and priority [#52](https://github.com/getgrav/grav-plugin-sitemap/pull/52) 111 | * Added a meta name="robots" content="noindex" into the header for seo [#50](https://github.com/getgrav/grav-plugin-sitemap/pull/50) 112 | * Added `ru` and `uk` translations [#61](https://github.com/getgrav/grav-plugin-sitemap/pull/61) 113 | 1. [](#bugfix) 114 | * Only add published translations to the sitemap [#43](https://github.com/getgrav/grav-plugin-sitemap/issues/43) 115 | 116 | # v1.9.1 117 | ## 04/21/2017 118 | 119 | 1. [](#bugfix) 120 | * Add a namespace xhtml for a international sitemap [#40](https://github.com/getgrav/grav-plugin-sitemap/pull/40) 121 | 122 | # v1.9.0 123 | ## 04/19/2017 124 | 125 | 1. [](#new) 126 | * Added wildcard ignores [#34](https://github.com/getgrav/grav-plugin-sitemap/pull/34) 127 | * Added ability to add external URLs to sitemap [#35](https://github.com/getgrav/grav-plugin-sitemap/pull/35) 128 | * Added page-level ignores [#37](https://github.com/getgrav/grav-plugin-sitemap/pull/37) 129 | * Added multilanguage support [#36](https://github.com/getgrav/grav-plugin-sitemap/pull/36) 130 | 131 | # v1.8.0 132 | ## 03/14/2017 133 | 134 | 1. [](#new) 135 | * Added `changefreq` and `priority` [#28](https://github.com/getgrav/grav-plugin-sitemap/pull/28) 136 | 1. [](#improved) 137 | * Use `$page->canonical()` rather than `$page->permalink()` [#28](https://github.com/getgrav/grav-plugin-sitemap/pull/28) 138 | 139 | # v1.7.0 140 | ## 10/19/2016 141 | 142 | 1. [](#new) 143 | * Use new Grav feature to force output to be XML even when not passed `.xml` in URL 144 | 145 | # v1.6.2 146 | ## 07/14/2016 147 | 148 | 1. [](#bugfix) 149 | * Fix sitemap XLS in multilanguage 150 | 151 | # v1.6.1 152 | ## 05/30/2016 153 | 154 | 1. [](#bugfix) 155 | * Priority should be `float` in blueprints 156 | 157 | # v1.6.0 158 | ## 04/29/2016 159 | 160 | 1. [](#new) 161 | * Added compatibility with Grav Admin 1.1 162 | 1. [](#improved) 163 | * Use some common translated strings in the blueprint 164 | 165 | # v1.5.0 166 | ## 01/06/2016 167 | 168 | 1. [](#new) 169 | * Added a default XSL file for the sitemap 170 | 1. [](#improved) 171 | * Added a note to the README on how to only allow the link to the .xml sitemap 172 | 1. [](#bugfix) 173 | * Fixed saving the `priority` option when adding it to a page through the Admin Plugin 174 | 175 | # v1.4.2 176 | ## 11/11/2015 177 | 178 | 1. [](#bugfix) 179 | * Escape the `loc` so it's properly parsed 180 | 181 | # v1.4.1 182 | ## 10/07/2015 183 | 184 | 1. [](#bugfix) 185 | * Avoid duplication of sitemap items 186 | 187 | # v1.4.0 188 | ## 08/25/2015 189 | 190 | 1. [](#improved) 191 | * Added blueprints for Grav Admin plugin 192 | 1. [](#bugfix) 193 | * Don't show unpublished pages in sitemap 194 | 195 | # v1.3.0 196 | ## 02/25/2015 197 | 198 | 1. [](#new) 199 | * Added `ignores` list to allow certain routes to be left out of sitemap 200 | 201 | # v1.2.0 202 | ## 11/30/2014 203 | 204 | 1. [](#new) 205 | * ChangeLog started... 206 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2014 Grav 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 all 13 | 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 THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Grav Sitemap Plugin 2 | 3 | `Sitemap` is a [Grav](https://github.com/getgrav/grav) Plugin that generates a [map of your pages](https://en.wikipedia.org/wiki/Site_map) in `XML` format that is easily understandable and indexable by Search engines. 4 | 5 | # Installation 6 | 7 | Installing the Sitemap plugin can be done in one of two ways. Our GPM (Grav Package Manager) installation method enables you to quickly and easily install the plugin with a simple terminal command, while the manual method enables you to do so via a zip file. 8 | 9 | ## GPM Installation (Preferred) 10 | 11 | The simplest way to install this plugin is via the [Grav Package Manager (GPM)](https://learn.getgrav.org/advanced/grav-gpm) through your system's Terminal (also called the command line). From the root of your Grav install type: 12 | 13 | bin/gpm install sitemap 14 | 15 | This will install the Sitemap plugin into your `/user/plugins` directory within Grav. Its files can be found under `/your/site/grav/user/plugins/sitemap`. 16 | 17 | ## Manual Installation 18 | 19 | To install this plugin, just download the zip version of this repository and unzip it under `/your/site/grav/user/plugins`. Then, rename the folder to `sitemap`. You can find these files either on [GitHub](https://github.com/getgrav/grav-plugin-sitemap) or via [GetGrav.org](https://getgrav.org/downloads/plugins#extras). 20 | 21 | You should now have all the plugin files under 22 | 23 | /your/site/grav/user/plugins/sitemap 24 | 25 | >> NOTE: This plugin is a modular component for Grav which requires [Grav](https://github.com/getgrav/grav), the [Error](https://github.com/getgrav/grav-plugin-error) and [Problems](https://github.com/getgrav/grav-plugin-problems) plugins, and a theme to be installed in order to operate. 26 | 27 | 28 | # Usage 29 | 30 | The `sitemap` plugin works out of the box. You can just go directly to `http://yoursite.com/sitemap` and you will see the generated `XML`. 31 | 32 | ## Config Defaults 33 | 34 | ```yaml 35 | enabled: true 36 | route: '/sitemap' 37 | ignore_external: true 38 | ignore_protected: true 39 | ignore_redirect: true 40 | ignores: 41 | - /blog/blog-post-to-ignore 42 | - /ignore-this-route 43 | - /ignore-children-of-this-route/.* 44 | include_news_tags: false 45 | standalone_sitemap_news: false 46 | sitemap_news_path: '/sitemap-news.xml' 47 | news_max_age_days: 2 48 | news_enabled_paths: 49 | - /blog 50 | whitelist: 51 | html_support: false 52 | urlset: 'http://www.sitemaps.org/schemas/sitemap/0.9' 53 | urlnewsset: 'http://www.google.com/schemas/sitemap-news/0.9' 54 | short_date_format: true 55 | include_changefreq: true 56 | changefreq: daily 57 | include_priority: true 58 | priority: !!float 1 59 | additions: 60 | - 61 | location: /something-special 62 | lastmod: '2020-04-16' 63 | changefreq: hourly 64 | priority: 0.3 65 | - 66 | location: /something-else 67 | lastmod: '2020-04-17' 68 | changefreq: weekly 69 | priority: 0.2 70 | ``` 71 | 72 | You can ignore your own pages by providing a list of routes to ignore. You can also use a page's Frontmatter to signal that the sitemap should ignore it: 73 | 74 | ```yaml 75 | sitemap: 76 | ignore: true 77 | ``` 78 | 79 | ## Overrides 80 | 81 | You can override several elements of the sitemap entry for the page in the page's header. For example, as well as `ignore` mentioned above, these are available: 82 | 83 | ```yaml 84 | sitemap: 85 | lastmod: # e.g. '2024-04-17' 86 | changefreq: # always| hourly | daily: | weekly | monthly | yearly | never 87 | priority: # 0.1 -> 1.0 88 | ``` 89 | 90 | ## Multi-Language Support 91 | 92 | The latest Sitemap `v3.0` includes all new multi-language support utilizing the latest [Google Search SEO Recomendations](https://developers.google.com/search/docs/advanced/crawling/localized-versions?hl=en&visit_id=637468720624267418-280936473&rd=2) which creates bi-directional `hreflang` entries for each language available. 93 | 94 | This is handled automatically based on your Grav multi-language System configuration. 95 | 96 | ### News Support 97 | 98 | New in version 4.0 of the plugin is support for Google's [**News Sitemap Extension**](https://developers.google.com/search/docs/crawling-indexing/sitemaps/news-sitemap) that uses a specific tags under a `` tag to provide Google News specific data. When enabled, the news extensions will be enabled when an item is in one of the configured news paths (`/` by default, so all), and if the published date is not older than the configured `max age` (default of 2 per Googles recommendations). 99 | 100 | The output of the news tags is controlled by an overridable `sitemap-extensions/news.html.twig` template. 101 | 102 | The default behavior when **Include News Tags** is enabled, is to include the news tags directly in the primary `sitemap.xml` file. However, if you enabled the **Standalone News URLs** option, news tags will not be added to the primary `sitemap.xml`, rather, they will be available in standalone paths that contain only the pages in the designated news paths. 103 | 104 | For example, the default behavior is to enable `/blog` as a news path. If this path exists, you have content in subfolders of this page, and that content is less than the defined "News Max Age" (2 days recommended by Google), then that sitemap-news-specific sitemap would be available via: 105 | 106 | ``` 107 | https://yoursite.com/blog/sitemap-news.xml 108 | ``` 109 | 110 | You can change the "News Path" to be something other than `sitemap-news.xml` if you wish. 111 | 112 | 113 | ## Images 114 | 115 | You can add images to the sitemap by adding an entry in the page's Frontmatter. 116 | 117 | ```yaml 118 | sitemap: 119 | images: 120 | your_image: 121 | loc: your-image.png 122 | caption: A caption for the image 123 | geoloc: Amsterdam, The Netherlands 124 | title: The title of your image 125 | license: A URL to the license of the image. 126 | ``` 127 | 128 | For more info on images in sitemaps see [Google image sitemaps](https://support.google.com/webmasters/answer/178636?hl=en). 129 | 130 | ## Only allow access to the .xml file 131 | 132 | If you want your sitemap to only be accessible via `sitemap.xml` for example, set the route to `/sitemap` and add this to your `.htaccess` file: 133 | 134 | `Redirect 301 /sitemap /sitemap.xml` 135 | 136 | ## HTML Support 137 | 138 | As of Sitemap version `3.0.1` you can enable `html_support` in the configuration and then when you go to `/sitemap` or `/sitemap.html` you will view an HTML version of the sitemap per the `templates/sitemap.html.twig` template. 139 | 140 | You can copy and extend this Twig template in your theme to customize it for your needs. 141 | 142 | ## Manually add pages to the sitemap 143 | 144 | You can manually add URLs to the sitemap using the Admin settings, or by adding entries to your `sitemap.yaml` with this format: 145 | 146 | ```yaml 147 | additions: 148 | - 149 | location: /something-special 150 | lastmod: '2020-04-16' 151 | changefreq: hourly 152 | priority: 0.3 153 | ``` 154 | Note that Regex support is available: Just append `.*` to a path to ignore all of it's children. 155 | 156 | ## Dynamically adding pages to the sitemap 157 | 158 | If you have some dynamic content being added to your site via another plugin, or perhaps a 3rd party API, you can now add them dynamically to the sitemap with a simple event: 159 | 160 | Make sure you are subscribed to the `onSitemapProcessed` event then add simply add your entry to the sitemap like this: 161 | 162 | ```php 163 | public function onSitemapProcessed(\RocketTheme\Toolbox\Event\Event $e) 164 | { 165 | $sitemap = $e['sitemap']; 166 | $location = \Grav\Common\Utils::url('/foo-location', true); 167 | $sitemap['/foo'] = new \Grav\Plugin\Sitemap\SitemapEntry($location, '2020-07-02', 'weekly', '2.0'); 168 | $e['sitemap'] = $sitemap; 169 | } 170 | ``` 171 | 172 | The use `Utils::url()` method allow us to easily create the correct full URL by passing it a route plus the optional `true` parameter. 173 | -------------------------------------------------------------------------------- /blueprints.yaml: -------------------------------------------------------------------------------- 1 | name: Sitemap 2 | type: plugin 3 | slug: sitemap 4 | version: 5.1.0 5 | description: "Provide automatically generated **XML sitemaps** with this very useful, as a simple to configure Grav plugin." 6 | icon: map-marker 7 | author: 8 | name: Team Grav 9 | email: devs@getgrav.org 10 | url: http://getgrav.org 11 | homepage: https://github.com/getgrav/grav-plugin-sitemap 12 | keywords: sitemap, plugin, xml, map, index 13 | bugs: https://github.com/getgrav/grav-plugin-sitemap/issues 14 | license: MIT 15 | 16 | dependencies: 17 | - { name: grav, version: '>=1.6.0' } 18 | 19 | form: 20 | validation: strict 21 | fields: 22 | enabled: 23 | type: toggle 24 | label: PLUGIN_ADMIN.PLUGIN_STATUS 25 | highlight: 0 26 | default: 0 27 | options: 28 | 1: PLUGIN_ADMIN.ENABLED 29 | 0: PLUGIN_ADMIN.DISABLED 30 | validate: 31 | type: bool 32 | 33 | route: 34 | type: text 35 | size: medium 36 | label: PLUGIN_SITEMAP.ROUTE 37 | placeholder: /sitemap 38 | validate: 39 | pattern: "/([a-z-_]+/?)+" 40 | 41 | multilang_enabled: 42 | type: toggle 43 | label: PLUGIN_SITEMAP.MULTILANG_ENABLED 44 | help: PLUGIN_SITEMAP.MULTILANG_ENABLED_HELP 45 | highlight: 1 46 | default: 1 47 | options: 48 | 1: PLUGIN_ADMIN.ENABLED 49 | 0: PLUGIN_ADMIN.DISABLED 50 | validate: 51 | type: bool 52 | 53 | ignore_external: 54 | type: toggle 55 | label: PLUGIN_SITEMAP.IGNORE_EXTERNAL 56 | help: PLUGIN_SITEMAP.IGNORE_EXTERNAL_HELP 57 | highlight: 1 58 | default: 1 59 | options: 60 | 1: PLUGIN_ADMIN.ENABLED 61 | 0: PLUGIN_ADMIN.DISABLED 62 | validate: 63 | type: bool 64 | 65 | ignore_protected: 66 | type: toggle 67 | label: PLUGIN_SITEMAP.IGNORE_PROTECTED 68 | help: PLUGIN_SITEMAP.IGNORE_PROTECTED_HELP 69 | highlight: 1 70 | default: 1 71 | options: 72 | 1: PLUGIN_ADMIN.ENABLED 73 | 0: PLUGIN_ADMIN.DISABLED 74 | validate: 75 | type: bool 76 | 77 | ignore_redirect: 78 | type: toggle 79 | label: PLUGIN_SITEMAP.IGNORE_REDIRECT 80 | help: PLUGIN_SITEMAP.IGNORE_REDIRECT_HELP 81 | highlight: 1 82 | default: 1 83 | options: 84 | 1: PLUGIN_ADMIN.ENABLED 85 | 0: PLUGIN_ADMIN.DISABLED 86 | validate: 87 | type: bool 88 | 89 | xsl_transform: 90 | type: toggle 91 | label: PLUGIN_SITEMAP.XSL_TRANSFORM 92 | help: PLUGIN_SITEMAP.XSL_TRANSFORM_HELP 93 | highlight: 1 94 | default: 1 95 | options: 96 | 1: PLUGIN_ADMIN.ENABLED 97 | 0: PLUGIN_ADMIN.DISABLED 98 | validate: 99 | type: bool 100 | 101 | html_support: 102 | type: toggle 103 | label: PLUGIN_SITEMAP.HTML_SUPPORT 104 | help: PLUGIN_SITEMAP.HTML_SUPPORT_HELP 105 | highlight: 0 106 | default: 0 107 | options: 108 | 1: PLUGIN_ADMIN.ENABLED 109 | 0: PLUGIN_ADMIN.DISABLED 110 | validate: 111 | type: bool 112 | 113 | ignores: 114 | type: array 115 | label: PLUGIN_SITEMAP.IGNORES 116 | help: PLUGIN_SITEMAP.IGNORES_HELP 117 | value_only: true 118 | placeholder_value: '/ignore-this-route' 119 | 120 | news_section: 121 | type: section 122 | title: Sitemap News 123 | underline: true 124 | 125 | fields: 126 | 127 | include_news_tags: 128 | type: toggle 129 | label: PLUGIN_SITEMAP.INCLUDE_NEWS_TAGS 130 | help: PLUGIN_SITEMAP.INCLUDE_NEWS_TAGS_HELP 131 | highlight: 1 132 | default: 0 133 | options: 134 | 1: PLUGIN_ADMIN.ENABLED 135 | 0: PLUGIN_ADMIN.DISABLED 136 | validate: 137 | type: bool 138 | 139 | standalone_sitemap_news: 140 | type: toggle 141 | label: PLUGIN_SITEMAP.STANDALONE_SITEMAP_NEWS 142 | help: PLUGIN_SITEMAP.STANDALONE_SITEMAP_NEWS_HELP 143 | highlight: 1 144 | default: 0 145 | options: 146 | 1: PLUGIN_ADMIN.ENABLED 147 | 0: PLUGIN_ADMIN.DISABLED 148 | validate: 149 | type: bool 150 | 151 | sitemap_news_path: 152 | type: text 153 | size: medium 154 | label: PLUGIN_SITEMAP.SITEMAP_NEWS_PATH 155 | help: PLUGIN_SITEMAP.SITEMAP_NEWS_PATH_HELP 156 | 157 | news_max_age_days: 158 | type: number 159 | default: 2 160 | size: x-small 161 | label: PLUGIN_SITEMAP.NEWS_MAX_AGE_DAYS 162 | append: Days 163 | validate: 164 | type: int 165 | 166 | news_enabled_paths: 167 | type: array 168 | label: PLUGIN_SITEMAP.NEWS_TAG_PATHS 169 | value_only: true 170 | placeholder_value: '/news' 171 | 172 | data_section: 173 | type: section 174 | title: Sitemap Data 175 | underline: true 176 | fields: 177 | 178 | date_type: 179 | type: select 180 | label: PLUGIN_SITEMAP.DATE_TYPE 181 | default: page_date 182 | size: medium 183 | options: 184 | page_date: PLUGIN_SITEMAP.DATE_TYPE_PAGE_DATE 185 | last_modified: PLUGIN_SITEMAP.DATE_TYPE_LAST_MODIFIED 186 | 187 | include_changefreq: 188 | type: toggle 189 | label: PLUGIN_SITEMAP.INCLUDE_CHANGEFREQ 190 | help: PLUGIN_SITEMAP.INCLUDE_CHANGEFREQ_HELP 191 | highlight: 1 192 | default: 0 193 | options: 194 | 1: PLUGIN_ADMIN.ENABLED 195 | 0: PLUGIN_ADMIN.DISABLED 196 | validate: 197 | type: bool 198 | 199 | changefreq: 200 | type: select 201 | size: medium 202 | label: PLUGIN_SITEMAP.CHANGEFREQ 203 | default: '' 204 | options: 205 | '': PLUGIN_SITEMAP.CHANGEFREQ_DEFAULT 206 | always: PLUGIN_SITEMAP.CHANGEFREQ_ALWAYS 207 | hourly: PLUGIN_SITEMAP.CHANGEFREQ_HOURLY 208 | daily: PLUGIN_SITEMAP.CHANGEFREQ_DAILY 209 | weekly: PLUGIN_SITEMAP.CHANGEFREQ_WEEKLY 210 | monthly: PLUGIN_SITEMAP.CHANGEFREQ_MONTHLY 211 | yearly: PLUGIN_SITEMAP.CHANGEFREQ_YEARLY 212 | never: PLUGIN_SITEMAP.CHANGEFREQ_NEVER 213 | 214 | include_priority: 215 | type: toggle 216 | label: PLUGIN_SITEMAP.INCLUDE_PRIORITY 217 | help: PLUGIN_SITEMAP.INCLUDE_PRIORITY_HELP 218 | highlight: 1 219 | default: 0 220 | options: 221 | 1: PLUGIN_ADMIN.ENABLED 222 | 0: PLUGIN_ADMIN.DISABLED 223 | validate: 224 | type: bool 225 | 226 | priority: 227 | type: select 228 | label: PLUGIN_SITEMAP.PRIORITY 229 | size: small 230 | default: '' 231 | options: 232 | '': PLUGIN_SITEMAP.PRIORITY_USE_GLOBAL 233 | '0.1': 0.1 234 | '0.2': 0.2 235 | '0.3': 0.3 236 | '0.4': 0.4 237 | '0.5': 0.5 238 | '0.6': 0.6 239 | '0.7': 0.7 240 | '0.8': 0.8 241 | '0.9': 0.9 242 | '1.0': 1.0 243 | validate: 244 | type: float 245 | 246 | advanced_section: 247 | type: section 248 | title: Advanced Features 249 | underline: true 250 | fields: 251 | 252 | additions: 253 | type: list 254 | label: PLUGIN_SITEMAP.ADDITIONS 255 | help: PLUGIN_SITEMAP.ADDITIONS_HELP 256 | 257 | fields: 258 | .location: 259 | type: text 260 | label: PLUGIN_SITEMAP.LOCATION 261 | placeholder: "/not-a-grav-url" 262 | .lastmod: 263 | type: text 264 | label: PLUGIN_SITEMAP.LASTMOD 265 | placeholder: "2017-04-06" 266 | .changefreq: 267 | type: select 268 | label: PLUGIN_SITEMAP.CHANGEFREQ 269 | default: '' 270 | options: 271 | '': PLUGIN_SITEMAP.CHANGEFREQ_DEFAULT 272 | always: PLUGIN_SITEMAP.CHANGEFREQ_ALWAYS 273 | hourly: PLUGIN_SITEMAP.CHANGEFREQ_HOURLY 274 | daily: PLUGIN_SITEMAP.CHANGEFREQ_DAILY 275 | weekly: PLUGIN_SITEMAP.CHANGEFREQ_WEEKLY 276 | monthly: PLUGIN_SITEMAP.CHANGEFREQ_MONTHLY 277 | yearly: PLUGIN_SITEMAP.CHANGEFREQ_YEARLY 278 | never: PLUGIN_SITEMAP.CHANGEFREQ_NEVER 279 | .priority: 280 | type: select 281 | label: PLUGIN_SITEMAP.PRIORITY 282 | default: '' 283 | options: 284 | '': PLUGIN_SITEMAP.PRIORITY_USE_GLOBAL 285 | '0.1': 0.1 286 | '0.2': 0.2 287 | '0.3': 0.3 288 | '0.4': 0.4 289 | '0.5': 0.5 290 | '0.6': 0.6 291 | '0.7': 0.7 292 | '0.8': 0.8 293 | '0.9': 0.9 294 | '1.0': 1.0 295 | validate: 296 | type: float 297 | 298 | urlset: 299 | type: text 300 | default: 'http://www.sitemaps.org/schemas/sitemap/0.9' 301 | label: PLUGIN_SITEMAP.URLSET 302 | help: PLUGIN_SITEMAP.URLSET_HELP 303 | 304 | urlimageset: 305 | type: text 306 | default: 'http://www.google.com/schemas/sitemap-image/1.1' 307 | label: PLUGIN_SITEMAP.URLIMAGESET 308 | help: PLUGIN_SITEMAP.URLIMAGESET_HELP 309 | 310 | urlnewsset: 311 | type: text 312 | default: 'http://www.google.com/schemas/sitemap-news/0.9' 313 | label: PLUGIN_SITEMAP.URLNEWSSET 314 | help: PLUGIN_SITEMAP.URLNEWSSET_HELP 315 | -------------------------------------------------------------------------------- /blueprints/sitemap.yaml: -------------------------------------------------------------------------------- 1 | form: 2 | fields: 3 | tabs: 4 | fields: 5 | options: 6 | type: tab 7 | 8 | fields: 9 | 10 | sitemap: 11 | type: section 12 | title: PLUGIN_SITEMAP.SITEMAP 13 | underline: true 14 | 15 | fields: 16 | header.sitemap.ignore: 17 | type: toggle 18 | toggleable: true 19 | label: PLUGIN_SITEMAP.HEADER_IGNORE 20 | highlight: 0 21 | options: 22 | 1: PLUGIN_ADMIN.YES 23 | 0: PLUGIN_ADMIN.NO 24 | validate: 25 | type: bool 26 | required: false 27 | 28 | header.sitemap.lastmod: 29 | type: datetime 30 | label: PLUGIN_SITEMAP.HEADER_LASTMOD 31 | default: '' 32 | 33 | header.sitemap.changefreq: 34 | type: select 35 | label: PLUGIN_SITEMAP.HEADER_CHANGEFREQ 36 | default: '' 37 | options: 38 | '': PLUGIN_SITEMAP.CHANGEFREQ_DEFAULT 39 | always: PLUGIN_SITEMAP.CHANGEFREQ_ALWAYS 40 | hourly: PLUGIN_SITEMAP.CHANGEFREQ_HOURLY 41 | daily: PLUGIN_SITEMAP.CHANGEFREQ_DAILY 42 | weekly: PLUGIN_SITEMAP.CHANGEFREQ_WEEKLY 43 | monthly: PLUGIN_SITEMAP.CHANGEFREQ_MONTHLY 44 | yearly: PLUGIN_SITEMAP.CHANGEFREQ_YEARLY 45 | never: PLUGIN_SITEMAP.CHANGEFREQ_NEVER 46 | 47 | header.sitemap.priority: 48 | type: select 49 | label: PLUGIN_SITEMAP.HEADER_PRIORITY 50 | default: '' 51 | options: 52 | '': PLUGIN_SITEMAP.PRIORITY_USE_GLOBAL 53 | '0.1': 0.1 54 | '0.2': 0.2 55 | '0.3': 0.3 56 | '0.4': 0.4 57 | '0.5': 0.5 58 | '0.6': 0.6 59 | '0.7': 0.7 60 | '0.8': 0.8 61 | '0.9': 0.9 62 | '1.0': 1.0 63 | validate: 64 | type: float 65 | -------------------------------------------------------------------------------- /classes/SitemapEntry.php: -------------------------------------------------------------------------------- 1 | location = $location; 34 | $this->lastmod = $lastmod; 35 | $this->changefreq = $changefreq; 36 | $this->priority = $priority; 37 | $this->images = $images; 38 | } 39 | 40 | /** 41 | * @param array $data 42 | * @return SitemapEntry 43 | */ 44 | public function setData(array $data): SitemapEntry 45 | { 46 | foreach($data as $property => $value) 47 | { 48 | if (property_exists($this, $property)) { 49 | $this->{$property} = $value; 50 | } 51 | } 52 | return $this; 53 | } 54 | 55 | /** 56 | * @return mixed 57 | */ 58 | public function getLang() 59 | { 60 | return $this->lang; 61 | } 62 | 63 | /** 64 | * @param mixed $lang 65 | * @return SitemapEntry 66 | */ 67 | public function setLang($lang) 68 | { 69 | $this->lang = $lang; 70 | return $this; 71 | } 72 | 73 | 74 | /** 75 | * @return mixed 76 | */ 77 | public function getTitle() 78 | { 79 | return $this->title; 80 | } 81 | 82 | /** 83 | * @param mixed $title 84 | * @return SitemapEntry 85 | */ 86 | public function setTitle($title): SitemapEntry 87 | { 88 | $this->title = $title; 89 | return $this; 90 | } 91 | 92 | /** 93 | * @return mixed 94 | */ 95 | public function getRoute() 96 | { 97 | return $this->route; 98 | } 99 | 100 | /** 101 | * @param mixed $route 102 | * @return SitemapEntry 103 | */ 104 | public function setRoute($route): SitemapEntry 105 | { 106 | $this->route = $route; 107 | return $this; 108 | } 109 | 110 | /** 111 | * @return mixed 112 | */ 113 | public function getBaseLang() 114 | { 115 | return $this->base_lang; 116 | } 117 | 118 | /** 119 | * @param mixed $base_lang 120 | * @return SitemapEntry 121 | */ 122 | public function setBaseLang($base_lang): SitemapEntry 123 | { 124 | $this->base_lang = $base_lang; 125 | return $this; 126 | } 127 | 128 | /** 129 | * @return bool 130 | */ 131 | public function isTranslated(): bool 132 | { 133 | return $this->translated; 134 | } 135 | 136 | /** 137 | * @param bool $translated 138 | * @return SitemapEntry 139 | */ 140 | public function setTranslated(bool $translated): SitemapEntry 141 | { 142 | $this->translated = $translated; 143 | return $this; 144 | } 145 | 146 | /** 147 | * @return null 148 | */ 149 | public function getLocation() 150 | { 151 | return $this->location; 152 | } 153 | 154 | /** 155 | * @param null $location 156 | * @return SitemapEntry 157 | */ 158 | public function setLocation($location): SitemapEntry 159 | { 160 | $this->location = $location; 161 | return $this; 162 | } 163 | 164 | /** 165 | * @return null 166 | */ 167 | public function getLastmod() 168 | { 169 | return $this->lastmod; 170 | } 171 | 172 | /** 173 | * @param null $lastmod 174 | * @return SitemapEntry 175 | */ 176 | public function setLastmod($lastmod): SitemapEntry 177 | { 178 | $this->lastmod = $lastmod; 179 | return $this; 180 | } 181 | 182 | /** 183 | * @return null 184 | */ 185 | public function getChangefreq() 186 | { 187 | return $this->changefreq; 188 | } 189 | 190 | /** 191 | * @param null $changefreq 192 | * @return SitemapEntry 193 | */ 194 | public function setChangefreq($changefreq): SitemapEntry 195 | { 196 | $this->changefreq = $changefreq; 197 | return $this; 198 | } 199 | 200 | /** 201 | * @return null 202 | */ 203 | public function getPriority() 204 | { 205 | return $this->priority; 206 | } 207 | 208 | /** 209 | * @param null $priority 210 | * @return SitemapEntry 211 | */ 212 | public function setPriority($priority): SitemapEntry 213 | { 214 | $this->priority = $priority; 215 | return $this; 216 | } 217 | 218 | /** 219 | * @return null 220 | */ 221 | public function getImages() 222 | { 223 | return $this->images; 224 | } 225 | 226 | /** 227 | * @param null $images 228 | * @return SitemapEntry 229 | */ 230 | public function setImages($images) 231 | { 232 | $this->images = $images; 233 | return $this; 234 | } 235 | 236 | 237 | 238 | /** 239 | * @return array 240 | */ 241 | public function getHreflangs(): array 242 | { 243 | return $this->hreflangs; 244 | } 245 | 246 | /** 247 | * @param array $hreflang 248 | * @return SitemapEntry 249 | */ 250 | public function addHreflangs(array $hreflang): SitemapEntry 251 | { 252 | $this->hreflangs[] = $hreflang; 253 | return $this; 254 | } 255 | 256 | /** 257 | * @param array $hreflangs 258 | * @return SitemapEntry 259 | */ 260 | public function setHreflangs(array $hreflangs): SitemapEntry 261 | { 262 | $this->hreflangs = $hreflangs; 263 | return $this; 264 | } 265 | 266 | public function getTimestamp(): int 267 | { 268 | return $this->timestamp; 269 | } 270 | 271 | public function setTimestamp(int $timestamp): void 272 | { 273 | $this->timestamp = $timestamp; 274 | } 275 | 276 | public function getRawroute(): string 277 | { 278 | return $this->rawroute; 279 | } 280 | 281 | public function setRawroute(string $rawroute): void 282 | { 283 | $this->rawroute = $rawroute; 284 | } 285 | 286 | public function getLongdate(): string 287 | { 288 | return $this->longdate; 289 | } 290 | 291 | public function setLongdate(string $longdate): void 292 | { 293 | $this->longdate = $longdate; 294 | } 295 | 296 | public function getShortdate(): string 297 | { 298 | return $this->shortdate; 299 | } 300 | 301 | public function setShortdate(string $shortdate): void 302 | { 303 | $this->shortdate = $shortdate; 304 | } 305 | 306 | } 307 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "trilbymedia/sitemap", 3 | "type": "grav-sitemap", 4 | "description": "Provide automatically generated **XML sitemaps** with this very useful, but simple to configure, Grav plugin.", 5 | "keywords": ["plugin"], 6 | "homepage": "https://github.com/getgrav/grav-plugin-sitemap", 7 | "license": "MIT", 8 | "authors": [ 9 | { 10 | "name": "Team Grav", 11 | "email": "devs@getgrav.org", 12 | "homepage": "http://getgrav.org", 13 | "role": "Developer" 14 | } 15 | ], 16 | "require": { 17 | "php": ">=7.1.3" 18 | }, 19 | "autoload": { 20 | "psr-4": { 21 | "Grav\\Plugin\\Sitemap\\": "classes/" 22 | }, 23 | "classmap": ["sitemap.php"] 24 | }, 25 | "config": { 26 | "platform": { 27 | "php": "7.1.3" 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /composer.lock: -------------------------------------------------------------------------------- 1 | { 2 | "_readme": [ 3 | "This file locks the dependencies of your project to a known state", 4 | "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", 5 | "This file is @generated automatically" 6 | ], 7 | "content-hash": "7b3a8451f3be62bce1c1dd5736e649ea", 8 | "packages": [], 9 | "packages-dev": [], 10 | "aliases": [], 11 | "minimum-stability": "stable", 12 | "stability-flags": [], 13 | "prefer-stable": false, 14 | "prefer-lowest": false, 15 | "platform": { 16 | "php": ">=7.1.3" 17 | }, 18 | "platform-dev": [], 19 | "platform-overrides": { 20 | "php": "7.1.3" 21 | }, 22 | "plugin-api-version": "2.0.0" 23 | } 24 | -------------------------------------------------------------------------------- /hebe.json: -------------------------------------------------------------------------------- 1 | { 2 | "project":"grav-plugin-sitemap", 3 | "platforms":{ 4 | "grav":{ 5 | "nodes":{ 6 | "plugin":[ 7 | { 8 | "source":"/", 9 | "destination":"/user/plugins/sitemap" 10 | } 11 | ] 12 | } 13 | } 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /languages.yaml: -------------------------------------------------------------------------------- 1 | en: 2 | PLUGIN_SITEMAP: 3 | SITEMAP: 'Sitemap' 4 | HEADER_IGNORE: 'Sitemap ignore page' 5 | HEADER_LASTMOD: 'Sitemap Last Modified' 6 | HEADER_CHANGEFREQ: 'Sitemap Change Frequency' 7 | HEADER_PRIORITY: 'Sitemap priority' 8 | CHANGEFREQ: 'Global - Sitemap Change Frequency' 9 | CHANGEFREQ_DEFAULT: 'Use Global (daily)' 10 | CHANGEFREQ_ALWAYS: 'Always' 11 | CHANGEFREQ_HOURLY: 'Hourly' 12 | CHANGEFREQ_DAILY: 'Daily' 13 | CHANGEFREQ_WEEKLY: 'Weekly' 14 | CHANGEFREQ_MONTHLY: 'Monthly' 15 | CHANGEFREQ_YEARLY: 'Yearly' 16 | CHANGEFREQ_NEVER: 'Never' 17 | PRIORITY: 'Global - Sitemap Priority' 18 | PRIORITY_USE_GLOBAL: 'Use Global (1)' 19 | ROUTE: 'Route to Sitemap' 20 | IGNORES: 'Ignore URLs' 21 | IGNORES_HELP: 'URLs to ignore (You can ignore all children of a path by using regex and appending ".*" to the ignore path)' 22 | ADDITIONS: 'Additional URLs' 23 | ADDITIONS_HELP: 'Add external URLs to the sitemap' 24 | LOCATION: 'The URL location' 25 | LASTMOD: 'Last modification e.g. 2017-04-06' 26 | IGNORE_EXTERNAL: 'Ignore External URLs' 27 | IGNORE_EXTERNAL_HELP: 'By default Sitemap hides pages that have an `external` URL' 28 | IGNORE_PROTECTED: 'Ignore Protected Pages' 29 | IGNORE_PROTECTED_HELP: 'Ignore pages that custom "access" set to protect them via a login' 30 | IGNORE_REDIRECT: 'Ignore Redirect Pages' 31 | IGNORE_REDIRECT_HELP: 'Ignores pages that have a custom "redirect" entry in the header' 32 | URLSET: 'URLSet' 33 | URLSET_HELP: 'The URLSet XML Namespace' 34 | URLIMAGESET: 'URLImageSet' 35 | URLIMAGESET_HELP: 'The URLImageSet XML Namespace,' 36 | URLNEWSSET: 'URLNewsSet' 37 | URLNEWSSET_HELP: 'The URLNewsSet XML Namespace' 38 | INCLUDE_NEWS_TAGS: 'Include News Tags' 39 | NEWS_MAX_AGE_DAYS: 'News Max Age (Days)' 40 | NEWS_TAG_PATHS: 'News Enabled Paths' 41 | MULTILANG_ENABLED: 'Multi-Lang Features' 42 | MULTILANG_ENABLED_HELP: 'Enables support for multilanguage features' 43 | INCLUDE_CHANGEFREQ: 'Include Change Frequency' 44 | INCLUDE_PRIORITY: 'Include Priority' 45 | SHORT_DATE_FORMAT: 'Short Date Format' 46 | SHORT_DATE_FORMAT_HELP: 'Use Short or Long Date format' 47 | HTML_SUPPORT: 'HTML Support' 48 | HTML_SUPPORT_HELP: 'Use "sitemap.html.twig" if no extension or ".html" passed in the URL' 49 | TITLE_LOCATION: 'Location' 50 | TITLE_TITLE: 'Title' 51 | TITLE_LASTMOD: 'Last Modified' 52 | UNTITLED: 'Untitled' 53 | XSL_TRANSFORM: 'XSL Transform' 54 | XSL_TRANSFORM_HELP: 'Use XSL stylesheets to transform the XML output when viewing in a browser' 55 | DATE_TYPE: 'Date Type' 56 | DATE_TYPE_PAGE_DATE: 'Page Date' 57 | DATE_TYPE_LAST_MODIFIED: 'Last Modified' 58 | STANDALONE_SITEMAP_NEWS: 'Standalone News URLs' 59 | STANDALONE_SITEMAP_NEWS_HELP: 'Create a standalone sitemap-news.xml file for each news-enabled path' 60 | SITEMAP_NEWS_PATH: 'Sitemap News Path' 61 | SITEMAP_NEWS_PATH_HELP: 'The path to the Sitemap News file' 62 | ru: 63 | PLUGIN_SITEMAP: 64 | SITEMAP: 'Карта сайта' 65 | HEADER_CHANGEFREQ: 'Частота обновления карты сайта' 66 | HEADER_PRIORITY: 'Приоритет карты сайта' 67 | CHANGEFREQ: 'Глобальное - частота обновления карты сайта' 68 | CHANGEFREQ_DEFAULT: 'Использовать глобальное (ежедневно)' 69 | CHANGEFREQ_ALWAYS: 'Всегда' 70 | CHANGEFREQ_HOURLY: 'Ежечасно' 71 | CHANGEFREQ_DAILY: 'Ежедневно' 72 | CHANGEFREQ_WEEKLY: 'Еженедельно' 73 | CHANGEFREQ_MONTHLY: 'Ежемесячно' 74 | CHANGEFREQ_YEARLY: 'Ежегодно' 75 | CHANGEFREQ_NEVER: 'Никогда' 76 | PRIORITY: 'Глобальное - приоритет карты сайта' 77 | PRIORITY_USE_GLOBAL: 'Использовать глобальное (1)' 78 | ROUTE: 'Маршрут к карте сайта' 79 | IGNORES: 'Игнорировать' 80 | IGNORES_HELP: 'URL-адреса для игнорирования (Вы можете игнорировать всех дочерних элементов пути, используя регекс и добавляя ".*" к игнорируемому пути).' 81 | ADDITIONS: 'Дополнительные URL' 82 | ADDITIONS_HELP: 'Добавить внешние URL в карту сайта' 83 | LOCATION: 'Расположение URL' 84 | LASTMOD: 'Последнее изменение, например 2017-04-06' 85 | 86 | uk: 87 | PLUGIN_SITEMAP: 88 | SITEMAP: 'Карта сайту' 89 | HEADER_CHANGEFREQ: 'Частота оновлення карти сайту' 90 | HEADER_PRIORITY: 'Пріоритет карти сайту' 91 | CHANGEFREQ: 'Глобальне - частота оновлення карти сайту' 92 | CHANGEFREQ_DEFAULT: 'Використовувати глобальне (щодня)' 93 | CHANGEFREQ_ALWAYS: 'Завжди' 94 | CHANGEFREQ_HOURLY: 'Погодинно' 95 | CHANGEFREQ_DAILY: 'Щодня' 96 | CHANGEFREQ_WEEKLY: 'Щотижня' 97 | CHANGEFREQ_MONTHLY: 'Щомісячно' 98 | CHANGEFREQ_YEARLY: 'Щорічно' 99 | CHANGEFREQ_NEVER: 'Ніколи' 100 | PRIORITY: 'Глобальне - пріоритет карта сайту' 101 | PRIORITY_USE_GLOBAL: 'Використовувати глобальний (1)' 102 | ROUTE: 'Маршрут до карти сайту' 103 | IGNORES: 'Ігнорувати' 104 | IGNORES_HELP: 'URL-адреси для ігнорування' 105 | ADDITIONS: 'Додаткові URL-адреси' 106 | ADDITIONS_HELP: 'Додати зовнішні URL-адреси до карти сайту' 107 | LOCATION: 'Розташування URL-адреси' 108 | LASTMOD: 'Остання модифікація, напр. 2017-04-06' 109 | 110 | de: 111 | PLUGIN_SITEMAP: 112 | SITEMAP: 'Sitemap' 113 | HEADER_CHANGEFREQ: 'Sitemap Änderungsfrequenz' 114 | HEADER_PRIORITY: 'Sitemap Priorität' 115 | CHANGEFREQ: 'Global - Sitemap Änderungsfrequenz' 116 | CHANGEFREQ_DEFAULT: 'Benutze Global (Täglich)' 117 | CHANGEFREQ_ALWAYS: 'Immer' 118 | CHANGEFREQ_HOURLY: 'Stündlich' 119 | CHANGEFREQ_DAILY: 'Täglich' 120 | CHANGEFREQ_WEEKLY: 'Wöchentlich' 121 | CHANGEFREQ_MONTHLY: 'Monatlich' 122 | CHANGEFREQ_YEARLY: 'Jährlich' 123 | CHANGEFREQ_NEVER: 'Nie' 124 | PRIORITY: 'Global - Sitemap Priorität' 125 | PRIORITY_USE_GLOBAL: 'Benutze Global (1)' 126 | ROUTE: 'Pfad zur Sitemap' 127 | IGNORES: 'Ignorieren' 128 | IGNORES_HELP: 'Zu ignorierende URLs (Um alle Unterseiten einer URL zu ignorieren, können Sie Regex nutzen und ".*" an die zu ignorierende URL anhängen)' 129 | ADDITIONS: 'Zusätzliche URLs' 130 | ADDITIONS_HELP: 'Füge externe URLs zur Sitemap hinzu' 131 | LOCATION: 'Seiten Pfad' 132 | LASTMOD: 'Letzte Änderung e.g. 2017-04-06' 133 | MULTILANG_ENABLED: 'Mehrsprachigkeit' 134 | MULTILANG_ENABLED_HELP: 'Aktiviert Funktionen zur Mehrsprachigkeit' 135 | 136 | zh: 137 | PLUGIN_SITEMAP: 138 | SITEMAP: '网站地图' 139 | HEADER_CHANGEFREQ: '网站地图变更频率' 140 | HEADER_PRIORITY: '网站地图优先级' 141 | CHANGEFREQ: '全局 - 网站地图变更频率' 142 | CHANGEFREQ_DEFAULT: '使用全局 (每日)' 143 | CHANGEFREQ_ALWAYS: '总是' 144 | CHANGEFREQ_HOURLY: '每小时' 145 | CHANGEFREQ_DAILY: '每天' 146 | CHANGEFREQ_WEEKLY: '每周' 147 | CHANGEFREQ_MONTHLY: '每月' 148 | CHANGEFREQ_YEARLY: '每年' 149 | CHANGEFREQ_NEVER: '永不' 150 | PRIORITY: '全局 - 网站地图优先级' 151 | PRIORITY_USE_GLOBAL: '使用全局 (1)' 152 | ROUTE: '网站地图路径' 153 | IGNORES: '忽略' 154 | IGNORES_HELP: '忽略的 URL (你可以通过使用regex并在忽略路径后添加".*"来忽略一个路径的所有子节点)' 155 | ADDITIONS: '附加 URL' 156 | ADDITIONS_HELP: '添加外部 URL 到网站地图' 157 | LOCATION: 'URL 地址' 158 | LASTMOD: '上次修改 例如 2017-04-06' 159 | 160 | -------------------------------------------------------------------------------- /pages/sitemap.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Sitemap 3 | --- -------------------------------------------------------------------------------- /sitemap-news.xsl: -------------------------------------------------------------------------------- 1 | 7 | 8 | 9 | 10 | 11 | 12 | XML Sitemap 13 | 14 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 101 | 104 | 108 | 109 | 110 | 111 |
News Sitemap
locnews:titlenews:publication_date
94 | 95 | 96 | 97 | 98 | 99 | 100 | 102 | 103 | 105 | 107 |
112 | 113 | 114 |
115 |
116 | -------------------------------------------------------------------------------- /sitemap.php: -------------------------------------------------------------------------------- 1 | [ 50 | ['autoload', 100000], // TODO: Remove when plugin requires Grav >=1.7 51 | ['onPluginsInitialized', 0], 52 | ], 53 | 'onBlueprintCreated' => ['onBlueprintCreated', 0] 54 | ]; 55 | } 56 | 57 | /** 58 | * Composer autoload. 59 | *is 60 | * @return ClassLoader 61 | */ 62 | public function autoload(): ClassLoader 63 | { 64 | return require __DIR__ . '/vendor/autoload.php'; 65 | } 66 | 67 | /** 68 | * Enable sitemap only if url matches to the configuration. 69 | */ 70 | public function onPluginsInitialized() 71 | { 72 | if ($this->isAdmin()) { 73 | $this->active = false; 74 | return; 75 | } 76 | 77 | /** @var Uri $uri */ 78 | $uri = $this->grav['uri']; 79 | $route = $this->config()['route']; 80 | $uri_route = $uri->route(); 81 | $news_page = false; 82 | 83 | if ($this->config()['include_news_tags'] && 84 | $this->config()['standalone_sitemap_news'] && 85 | Utils::endsWith($uri->uri(), $this->config()['sitemap_news_path']) && 86 | in_array(dirname($uri->route()), $this->config()['news_enabled_paths'])) { 87 | $this->news_route = dirname($uri->route()); 88 | } 89 | 90 | 91 | if ($route === $uri->route() || !empty($this->news_route)) { 92 | 93 | $this->enable([ 94 | 'onTwigInitialized' => ['onTwigInitialized', 0], 95 | 'onTwigTemplatePaths' => ['onTwigTemplatePaths', 0], 96 | 'onPagesInitialized' => ['onPagesInitialized', 0], 97 | 'onPageInitialized' => ['onPageInitialized', 0], 98 | 'onTwigSiteVariables' => ['onTwigSiteVariables', 0] 99 | ]); 100 | } 101 | } 102 | 103 | /** 104 | * Generate data for the sitemap. 105 | */ 106 | public function onPagesInitialized() 107 | { 108 | /** @var Cache $cache */ 109 | $cache = $this->grav['cache']; 110 | 111 | /** @var Pages $pages */ 112 | $pages = $this->grav['pages']; 113 | 114 | $cache_id = md5('sitemap-data-'.$pages->getPagesCacheId()); 115 | // $this->sitemap = $cache->fetch($cache_id); 116 | 117 | if ($this->sitemap === false) { 118 | $this->multilang_enabled = $this->config->get('plugins.sitemap.multilang_enabled'); 119 | 120 | /** @var Language $language */ 121 | $language = $this->grav['language']; 122 | $default_lang = $language->getDefault() ?: 'en'; 123 | $active_lang = $language->getActive() ?? $default_lang; 124 | $languages = $this->multilang_enabled && $language->enabled() ? $language->getLanguages() : [$default_lang]; 125 | $include_default_lang = $this->config->get('system.languages.include_default_lang'); 126 | 127 | $this->multilang_skiplang_prefix = $this->config->get('system.languages.include_default_lang') ? '' : $language->getDefault(); 128 | $this->multilang_include_fallbacks = $this->config->get('system.languages.pages_fallback_only') || !empty($this->config->get('system.languages.content_fallback')); 129 | 130 | $this->datetime_format = $this->config->get('plugins.sitemap.short_date_format') ? 'Y-m-d' : 'Y-m-d\TH:i:sP'; 131 | $this->include_change_freq = $this->config->get('plugins.sitemap.include_changefreq'); 132 | $this->default_change_freq = $this->config->get('plugins.sitemap.changefreq'); 133 | $this->include_priority = $this->config->get('plugins.sitemap.include_priority'); 134 | $this->default_priority = $this->config->get('plugins.sitemap.priority'); 135 | $this->ignores = (array) $this->config->get('plugins.sitemap.ignores'); 136 | $this->ignore_external = $this->config->get('plugins.sitemap.ignore_external'); 137 | $this->ignore_protected = $this->config->get('plugins.sitemap.ignore_protected'); 138 | $this->ignore_redirect = $this->config->get('plugins.sitemap.ignore_redirect'); 139 | 140 | // Gather data for all languages 141 | foreach ($languages as $lang) { 142 | $language->init(); 143 | $language->setActive($lang); 144 | $pages->reset(); 145 | $this->addRouteData($pages, $lang); 146 | } 147 | 148 | // Reset back to active language 149 | if ($language->enabled() && $language->getActive() !== $active_lang) { 150 | $language->init(); 151 | $language->setActive($active_lang); 152 | $pages->reset(); 153 | } 154 | 155 | // Build sitemap 156 | foreach ($languages as $lang) { 157 | foreach($this->route_data as $route => $route_data) { 158 | if ($data = $route_data[$lang] ?? null) { 159 | $entry = new SitemapEntry(); 160 | $entry->setData($data); 161 | if ($language->enabled()) { 162 | foreach ($route_data as $l => $l_data) { 163 | $entry->addHreflangs(['hreflang' => $l, 'href' => $l_data['location']]); 164 | if ($include_default_lang === false && $l == $default_lang) { 165 | $entry->addHreflangs(['hreflang' => 'x-default', 'href' => $l_data['location']]); 166 | } 167 | } 168 | } 169 | $this->sitemap[$data['url']] = $entry; 170 | } 171 | } 172 | } 173 | 174 | $additions = (array) $this->config->get('plugins.sitemap.additions'); 175 | foreach ($additions as $addition) { 176 | if (isset($addition['location'])) { 177 | $location = Utils::url($addition['location'], true); 178 | $entry = new SitemapEntry($location,$addition['lastmod'] ?? null,$addition['changefreq'] ?? null, $addition['priority'] ?? null); 179 | $this->sitemap[$location] = $entry; 180 | } 181 | } 182 | $cache->save($cache_id, $this->sitemap); 183 | } 184 | 185 | $this->grav->fireEvent('onSitemapProcessed', new Event(['sitemap' => &$this->sitemap])); 186 | } 187 | 188 | public function onPageInitialized($event) 189 | { 190 | $page = $event['page'] ?? null; 191 | $route = $this->config->get('plugins.sitemap.route'); 192 | $uri = $this->grav['uri']; 193 | $html_support = $this->config->get('plugins.sitemap.html_support', false); 194 | $extension = $this->grav['uri']->extension() ?? ($html_support ? 'html': 'xml'); 195 | 196 | if (is_null($page) || $uri->route() === $route || !empty($this->news_route)) { 197 | 198 | // set a dummy page 199 | $page = new Page; 200 | $page->init(new \SplFileInfo(__DIR__ . '/pages/sitemap.md')); 201 | $page->templateFormat($extension); 202 | unset($this->grav['page']); 203 | $this->grav['page'] = $page; 204 | $twig = $this->grav['twig']; 205 | 206 | if (!empty($this->news_route)) { 207 | $header = $page->header(); 208 | $header->sitemap['news_route'] = $this->news_route; 209 | $page->header($header); 210 | $twig->template = "sitemap-news.$extension.twig"; 211 | } else { 212 | $twig->template = "sitemap.$extension.twig"; 213 | } 214 | 215 | } 216 | } 217 | 218 | // Access plugin events in this class 219 | public function onTwigInitialized() 220 | { 221 | $this->grav['twig']->twig()->addFunction( 222 | new TwigFunction('sort_sitemap_entries_by_language', [$this, 'sortSitemapEntriesByLanguage']) 223 | ); 224 | $this->grav['twig']->twig()->addFunction( 225 | new TwigFunction('timestamp_within_days', [$this, 'timestampWithinDays']) 226 | ); 227 | } 228 | 229 | /** 230 | * Add current directory to twig lookup paths. 231 | */ 232 | public function onTwigTemplatePaths() 233 | { 234 | $this->grav['twig']->twig_paths[] = __DIR__ . '/templates'; 235 | } 236 | 237 | /** 238 | * Set needed variables to display the sitemap. 239 | */ 240 | public function onTwigSiteVariables() 241 | { 242 | $twig = $this->grav['twig']; 243 | $twig->twig_vars['sitemap'] = $this->sitemap; 244 | } 245 | 246 | /** 247 | * Extend page blueprints with feed configuration options. 248 | * 249 | * @param Event $event 250 | */ 251 | public function onBlueprintCreated(Event $event) 252 | { 253 | static $inEvent = false; 254 | 255 | /** @var Data\Blueprint $blueprint */ 256 | $blueprint = $event['blueprint']; 257 | if (!$inEvent && $blueprint->get('form/fields/tabs', null, '/')) { 258 | if (!in_array($blueprint->getFilename(), array_keys($this->grav['pages']->modularTypes()))) { 259 | $inEvent = true; 260 | $blueprints = new Data\Blueprints(__DIR__ . '/blueprints/'); 261 | $extends = $blueprints->get('sitemap'); 262 | $blueprint->extend($extends, true); 263 | $inEvent = false; 264 | } 265 | } 266 | } 267 | 268 | public function sortSitemapEntriesByLanguage() 269 | { 270 | $entries = []; 271 | 272 | foreach ((array) $this->sitemap as $route => $entry) { 273 | $lang = $entry->getLang(); 274 | unset($entry->hreflangs); 275 | unset($entry->image); 276 | if ($lang === null) { 277 | $lang = $this->grav['language']->getDefault() ?: 'en'; 278 | } 279 | $entries[$lang][$route] = $entry; 280 | } 281 | return $entries; 282 | } 283 | 284 | public function timestampWithinDays(int $timestamp, int $days): bool 285 | { 286 | $now = time(); 287 | $days_ago = $now - ($days * 24 * 60 * 60); 288 | return $timestamp >= $days_ago; 289 | } 290 | 291 | protected function addRouteData($pages, $lang) 292 | { 293 | $routes = array_unique($pages->routes()); 294 | ksort($routes); 295 | 296 | foreach ($routes as $route => $path) { 297 | /** @var PageInterface $page */ 298 | $page = $pages->get($path); 299 | 300 | $rawroute = $page->rawRoute(); 301 | $header = $page->header(); 302 | $external_url = $this->ignore_external ? isset($header->external_url) : false; 303 | $protected_page = $this->ignore_protected ? isset($header->access) : false; 304 | $redirect_page = $this->ignore_redirect ? isset($header->redirect) : false; 305 | $config_ignored = preg_match(sprintf("@^(%s)$@i", implode('|', $this->ignores)), $page->route()); 306 | $page_ignored = $protected_page || $external_url || $redirect_page || (isset($header->sitemap['ignore']) ? $header->sitemap['ignore'] : false); 307 | 308 | if ($page->routable() && $page->published() && !$config_ignored && !$page_ignored) { 309 | $page_languages = array_keys($page->translatedLanguages()); 310 | $include_lang = $this->multilang_skiplang_prefix !== $lang; 311 | $location = $page->canonical($include_lang); 312 | $url = $page->url(false, $include_lang); 313 | $lastmod = !empty($header->sitemap['lastmod']) ? strtotime($header->sitemap['lastmod']) : $page->modified(); 314 | 315 | $lang_route = [ 316 | 'title' => $page->title(), 317 | 'url' => $url, 318 | 'route' => $route, 319 | 'lang' => $lang, 320 | 'translated' => in_array($lang, $page_languages), 321 | 'location' => $location, 322 | 'lastmod' => date($this->datetime_format, $lastmod), 323 | 'longdate' => date('Y-m-d\TH:i:sP', $page->date()), 324 | 'shortdate' => date('Y-m-d', $page->date()), 325 | 'timestamp' => $page->date(), 326 | 'rawroute' => $page->rawRoute(), 327 | ]; 328 | 329 | if ($this->include_change_freq) { 330 | $lang_route['changefreq'] = $header->sitemap['changefreq'] ?? $this->default_change_freq; 331 | } 332 | if ($this->include_priority) { 333 | $lang_route['priority'] = $header->sitemap['priority'] ?? $this->default_priority; 334 | } 335 | 336 | // optional add image 337 | $images = $header->sitemap['images'] ?? $this->config->get('plugins.sitemap.images') ?? []; 338 | 339 | if (isset($images)) { 340 | foreach ($images as $image => $values) { 341 | if (isset($values['loc'])) { 342 | $images[$image]['loc'] = $page->media()[$values['loc']]->url(); 343 | } else { 344 | unset($images[$image]); 345 | } 346 | } 347 | $lang_route['images'] = $images; 348 | } 349 | 350 | 351 | 352 | $this->route_data[$rawroute][$lang] = $lang_route; 353 | } 354 | } 355 | } 356 | } 357 | -------------------------------------------------------------------------------- /sitemap.xsl: -------------------------------------------------------------------------------- 1 | 6 | 7 | 8 | 9 | 10 | 11 | XML Sitemap 12 | 13 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 |
Sitemap
LocationLast ModifiedUpdate FrequencyPriority
104 | 105 | 106 |
107 |
108 | -------------------------------------------------------------------------------- /sitemap.yaml: -------------------------------------------------------------------------------- 1 | enabled: true 2 | route: '/sitemap' 3 | ignore_external: true 4 | ignore_protected: true 5 | ignore_redirect: true 6 | include_news_tags: false 7 | standalone_sitemap_news: false 8 | sitemap_news_path: '/sitemap-news.xml' 9 | news_max_age_days: 2 10 | news_enabled_paths: 11 | - /blog 12 | ignores: 13 | whitelist: 14 | xsl_transform: true 15 | html_support: false 16 | urlset: 'http://www.sitemaps.org/schemas/sitemap/0.9' 17 | urlnewsset: 'http://www.google.com/schemas/sitemap-news/0.9' 18 | urlimageset: 'http://www.google.com/schemas/sitemap-image/1.1' 19 | short_date_format: true 20 | include_changefreq: true 21 | changefreq: daily 22 | include_priority: true 23 | priority: !!float 1 24 | additions: [] 25 | multilang_enabled: true 26 | -------------------------------------------------------------------------------- /templates/sitemap-extensions/image.xml.twig: -------------------------------------------------------------------------------- 1 | 2 | {% if image.loc %} 3 | {{ url(image.loc, true) }} 4 | {% endif %} 5 | {% if image.caption %} 6 | {{ image.caption }} 7 | {% endif %} 8 | {% if image.geoloc %} 9 | {{ image.geoloc }} 10 | {% endif %} 11 | {% if image.title %} 12 | {{ image.title }} 13 | {% endif %} 14 | {% if image.license %} 15 | {{ image.license }} 16 | {% endif %} 17 | -------------------------------------------------------------------------------- /templates/sitemap-extensions/news.xml.twig: -------------------------------------------------------------------------------- 1 | 2 | 3 | {{ site.title }} 4 | {{ entry.lang }} 5 | 6 | {{ entry.longdate }} 7 | {{ entry.title }} 8 | 9 | -------------------------------------------------------------------------------- /templates/sitemap-news.xml.twig: -------------------------------------------------------------------------------- 1 | {% set sitemap_config = page.header.sitemap %} 2 | 3 | {% if config.plugins.sitemap.xsl_transform %} 4 | 5 | {% endif %} 6 | 10 | {% for entry in sitemap %} 11 | {% if timestamp_within_days(entry.timestamp, config.plugin.sitemap.news_max_age_days|default(2)) and 12 | entry.rawroute|starts_with(sitemap_config.news_route ~ '/') %} 13 | 14 | {{ entry.location|e }} 15 | {% include 'sitemap-extensions/news.xml.twig' %} 16 | 17 | {% endif %} 18 | {% endfor %} 19 | -------------------------------------------------------------------------------- /templates/sitemap.html.twig: -------------------------------------------------------------------------------- 1 | {% extends 'partials/base.html.twig' %} 2 | 3 | {% block content %} 4 | {% set language = grav.language %} 5 | {% set lang = language.enabled ? (language.active ?: language.default) : null %} 6 | {% set count = 1 %} 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | {% for entry in sitemap %} 19 | {% if lang is null or entry.lang == lang %} 20 | 21 | 22 | 23 | 24 | 25 | 26 | {% set count = count + 1 %} 27 | {% endif %} 28 | {% endfor %} 29 | 30 |
#{{ 'PLUGIN_SITEMAP.TITLE_TITLE'|t }}{{ 'PLUGIN_SITEMAP.TITLE_LOCATION'|t }}{{ 'PLUGIN_SITEMAP.TITLE_LASTMOD'|t }}
{{ count }}{{ entry.title ?: 'PLUGIN_SITEMAP.UNTITLED'|t }}{{ entry.location }}{{ entry.lastmod }}
31 | {% endblock %} 32 | -------------------------------------------------------------------------------- /templates/sitemap.json.twig: -------------------------------------------------------------------------------- 1 | {{ (sort_sitemap_entries_by_language()|json_encode|raw) }} -------------------------------------------------------------------------------- /templates/sitemap.xml.twig: -------------------------------------------------------------------------------- 1 | 2 | {% if config.plugins.sitemap.xsl_transform %} 3 | 4 | {% endif %} 5 | 12 | {% for entry in sitemap %} 13 | 14 | {{ entry.location|e }} 15 | {% if config.plugins.sitemap.include_news_tags and 16 | config.plugins.sitemap.standalone_sitemap_news == false and 17 | timestamp_within_days(entry.timestamp, config.plugin.sitemap.news_max_age_days|default(2)) and 18 | entry.rawroute|starts_with(config.plugins.sitemap.news_enabled_paths) 19 | %} 20 | {% include 'sitemap-extensions/news.xml.twig' %} 21 | {% endif %} 22 | {% for hreflang in entry.hreflangs %} 23 | 24 | {% endfor %} 25 | {% if entry.lastmod %} 26 | {{ entry.lastmod }} 27 | {% endif %} 28 | {% if entry.changefreq %} 29 | {{ entry.changefreq }} 30 | {% endif %} 31 | {% if entry.priority %} 32 | {{ entry.priority|number_format(1) }} 33 | {% endif %} 34 | {% for image in entry.images %} 35 | {% include 'sitemap-extensions/image.xml.twig' %} 36 | {% endfor %} 37 | 38 | {% endfor %} 39 | 40 | -------------------------------------------------------------------------------- /vendor/autoload.php: -------------------------------------------------------------------------------- 1 | 7 | * Jordi Boggiano 8 | * 9 | * For the full copyright and license information, please view the LICENSE 10 | * file that was distributed with this source code. 11 | */ 12 | 13 | namespace Composer\Autoload; 14 | 15 | /** 16 | * ClassLoader implements a PSR-0, PSR-4 and classmap class loader. 17 | * 18 | * $loader = new \Composer\Autoload\ClassLoader(); 19 | * 20 | * // register classes with namespaces 21 | * $loader->add('Symfony\Component', __DIR__.'/component'); 22 | * $loader->add('Symfony', __DIR__.'/framework'); 23 | * 24 | * // activate the autoloader 25 | * $loader->register(); 26 | * 27 | * // to enable searching the include path (eg. for PEAR packages) 28 | * $loader->setUseIncludePath(true); 29 | * 30 | * In this example, if you try to use a class in the Symfony\Component 31 | * namespace or one of its children (Symfony\Component\Console for instance), 32 | * the autoloader will first look for the class under the component/ 33 | * directory, and it will then fallback to the framework/ directory if not 34 | * found before giving up. 35 | * 36 | * This class is loosely based on the Symfony UniversalClassLoader. 37 | * 38 | * @author Fabien Potencier 39 | * @author Jordi Boggiano 40 | * @see https://www.php-fig.org/psr/psr-0/ 41 | * @see https://www.php-fig.org/psr/psr-4/ 42 | */ 43 | class ClassLoader 44 | { 45 | // PSR-4 46 | private $prefixLengthsPsr4 = array(); 47 | private $prefixDirsPsr4 = array(); 48 | private $fallbackDirsPsr4 = array(); 49 | 50 | // PSR-0 51 | private $prefixesPsr0 = array(); 52 | private $fallbackDirsPsr0 = array(); 53 | 54 | private $useIncludePath = false; 55 | private $classMap = array(); 56 | private $classMapAuthoritative = false; 57 | private $missingClasses = array(); 58 | private $apcuPrefix; 59 | 60 | public function getPrefixes() 61 | { 62 | if (!empty($this->prefixesPsr0)) { 63 | return call_user_func_array('array_merge', array_values($this->prefixesPsr0)); 64 | } 65 | 66 | return array(); 67 | } 68 | 69 | public function getPrefixesPsr4() 70 | { 71 | return $this->prefixDirsPsr4; 72 | } 73 | 74 | public function getFallbackDirs() 75 | { 76 | return $this->fallbackDirsPsr0; 77 | } 78 | 79 | public function getFallbackDirsPsr4() 80 | { 81 | return $this->fallbackDirsPsr4; 82 | } 83 | 84 | public function getClassMap() 85 | { 86 | return $this->classMap; 87 | } 88 | 89 | /** 90 | * @param array $classMap Class to filename map 91 | */ 92 | public function addClassMap(array $classMap) 93 | { 94 | if ($this->classMap) { 95 | $this->classMap = array_merge($this->classMap, $classMap); 96 | } else { 97 | $this->classMap = $classMap; 98 | } 99 | } 100 | 101 | /** 102 | * Registers a set of PSR-0 directories for a given prefix, either 103 | * appending or prepending to the ones previously set for this prefix. 104 | * 105 | * @param string $prefix The prefix 106 | * @param array|string $paths The PSR-0 root directories 107 | * @param bool $prepend Whether to prepend the directories 108 | */ 109 | public function add($prefix, $paths, $prepend = false) 110 | { 111 | if (!$prefix) { 112 | if ($prepend) { 113 | $this->fallbackDirsPsr0 = array_merge( 114 | (array) $paths, 115 | $this->fallbackDirsPsr0 116 | ); 117 | } else { 118 | $this->fallbackDirsPsr0 = array_merge( 119 | $this->fallbackDirsPsr0, 120 | (array) $paths 121 | ); 122 | } 123 | 124 | return; 125 | } 126 | 127 | $first = $prefix[0]; 128 | if (!isset($this->prefixesPsr0[$first][$prefix])) { 129 | $this->prefixesPsr0[$first][$prefix] = (array) $paths; 130 | 131 | return; 132 | } 133 | if ($prepend) { 134 | $this->prefixesPsr0[$first][$prefix] = array_merge( 135 | (array) $paths, 136 | $this->prefixesPsr0[$first][$prefix] 137 | ); 138 | } else { 139 | $this->prefixesPsr0[$first][$prefix] = array_merge( 140 | $this->prefixesPsr0[$first][$prefix], 141 | (array) $paths 142 | ); 143 | } 144 | } 145 | 146 | /** 147 | * Registers a set of PSR-4 directories for a given namespace, either 148 | * appending or prepending to the ones previously set for this namespace. 149 | * 150 | * @param string $prefix The prefix/namespace, with trailing '\\' 151 | * @param array|string $paths The PSR-4 base directories 152 | * @param bool $prepend Whether to prepend the directories 153 | * 154 | * @throws \InvalidArgumentException 155 | */ 156 | public function addPsr4($prefix, $paths, $prepend = false) 157 | { 158 | if (!$prefix) { 159 | // Register directories for the root namespace. 160 | if ($prepend) { 161 | $this->fallbackDirsPsr4 = array_merge( 162 | (array) $paths, 163 | $this->fallbackDirsPsr4 164 | ); 165 | } else { 166 | $this->fallbackDirsPsr4 = array_merge( 167 | $this->fallbackDirsPsr4, 168 | (array) $paths 169 | ); 170 | } 171 | } elseif (!isset($this->prefixDirsPsr4[$prefix])) { 172 | // Register directories for a new namespace. 173 | $length = strlen($prefix); 174 | if ('\\' !== $prefix[$length - 1]) { 175 | throw new \InvalidArgumentException("A non-empty PSR-4 prefix must end with a namespace separator."); 176 | } 177 | $this->prefixLengthsPsr4[$prefix[0]][$prefix] = $length; 178 | $this->prefixDirsPsr4[$prefix] = (array) $paths; 179 | } elseif ($prepend) { 180 | // Prepend directories for an already registered namespace. 181 | $this->prefixDirsPsr4[$prefix] = array_merge( 182 | (array) $paths, 183 | $this->prefixDirsPsr4[$prefix] 184 | ); 185 | } else { 186 | // Append directories for an already registered namespace. 187 | $this->prefixDirsPsr4[$prefix] = array_merge( 188 | $this->prefixDirsPsr4[$prefix], 189 | (array) $paths 190 | ); 191 | } 192 | } 193 | 194 | /** 195 | * Registers a set of PSR-0 directories for a given prefix, 196 | * replacing any others previously set for this prefix. 197 | * 198 | * @param string $prefix The prefix 199 | * @param array|string $paths The PSR-0 base directories 200 | */ 201 | public function set($prefix, $paths) 202 | { 203 | if (!$prefix) { 204 | $this->fallbackDirsPsr0 = (array) $paths; 205 | } else { 206 | $this->prefixesPsr0[$prefix[0]][$prefix] = (array) $paths; 207 | } 208 | } 209 | 210 | /** 211 | * Registers a set of PSR-4 directories for a given namespace, 212 | * replacing any others previously set for this namespace. 213 | * 214 | * @param string $prefix The prefix/namespace, with trailing '\\' 215 | * @param array|string $paths The PSR-4 base directories 216 | * 217 | * @throws \InvalidArgumentException 218 | */ 219 | public function setPsr4($prefix, $paths) 220 | { 221 | if (!$prefix) { 222 | $this->fallbackDirsPsr4 = (array) $paths; 223 | } else { 224 | $length = strlen($prefix); 225 | if ('\\' !== $prefix[$length - 1]) { 226 | throw new \InvalidArgumentException("A non-empty PSR-4 prefix must end with a namespace separator."); 227 | } 228 | $this->prefixLengthsPsr4[$prefix[0]][$prefix] = $length; 229 | $this->prefixDirsPsr4[$prefix] = (array) $paths; 230 | } 231 | } 232 | 233 | /** 234 | * Turns on searching the include path for class files. 235 | * 236 | * @param bool $useIncludePath 237 | */ 238 | public function setUseIncludePath($useIncludePath) 239 | { 240 | $this->useIncludePath = $useIncludePath; 241 | } 242 | 243 | /** 244 | * Can be used to check if the autoloader uses the include path to check 245 | * for classes. 246 | * 247 | * @return bool 248 | */ 249 | public function getUseIncludePath() 250 | { 251 | return $this->useIncludePath; 252 | } 253 | 254 | /** 255 | * Turns off searching the prefix and fallback directories for classes 256 | * that have not been registered with the class map. 257 | * 258 | * @param bool $classMapAuthoritative 259 | */ 260 | public function setClassMapAuthoritative($classMapAuthoritative) 261 | { 262 | $this->classMapAuthoritative = $classMapAuthoritative; 263 | } 264 | 265 | /** 266 | * Should class lookup fail if not found in the current class map? 267 | * 268 | * @return bool 269 | */ 270 | public function isClassMapAuthoritative() 271 | { 272 | return $this->classMapAuthoritative; 273 | } 274 | 275 | /** 276 | * APCu prefix to use to cache found/not-found classes, if the extension is enabled. 277 | * 278 | * @param string|null $apcuPrefix 279 | */ 280 | public function setApcuPrefix($apcuPrefix) 281 | { 282 | $this->apcuPrefix = function_exists('apcu_fetch') && filter_var(ini_get('apc.enabled'), FILTER_VALIDATE_BOOLEAN) ? $apcuPrefix : null; 283 | } 284 | 285 | /** 286 | * The APCu prefix in use, or null if APCu caching is not enabled. 287 | * 288 | * @return string|null 289 | */ 290 | public function getApcuPrefix() 291 | { 292 | return $this->apcuPrefix; 293 | } 294 | 295 | /** 296 | * Registers this instance as an autoloader. 297 | * 298 | * @param bool $prepend Whether to prepend the autoloader or not 299 | */ 300 | public function register($prepend = false) 301 | { 302 | spl_autoload_register(array($this, 'loadClass'), true, $prepend); 303 | } 304 | 305 | /** 306 | * Unregisters this instance as an autoloader. 307 | */ 308 | public function unregister() 309 | { 310 | spl_autoload_unregister(array($this, 'loadClass')); 311 | } 312 | 313 | /** 314 | * Loads the given class or interface. 315 | * 316 | * @param string $class The name of the class 317 | * @return bool|null True if loaded, null otherwise 318 | */ 319 | public function loadClass($class) 320 | { 321 | if ($file = $this->findFile($class)) { 322 | includeFile($file); 323 | 324 | return true; 325 | } 326 | } 327 | 328 | /** 329 | * Finds the path to the file where the class is defined. 330 | * 331 | * @param string $class The name of the class 332 | * 333 | * @return string|false The path if found, false otherwise 334 | */ 335 | public function findFile($class) 336 | { 337 | // class map lookup 338 | if (isset($this->classMap[$class])) { 339 | return $this->classMap[$class]; 340 | } 341 | if ($this->classMapAuthoritative || isset($this->missingClasses[$class])) { 342 | return false; 343 | } 344 | if (null !== $this->apcuPrefix) { 345 | $file = apcu_fetch($this->apcuPrefix.$class, $hit); 346 | if ($hit) { 347 | return $file; 348 | } 349 | } 350 | 351 | $file = $this->findFileWithExtension($class, '.php'); 352 | 353 | // Search for Hack files if we are running on HHVM 354 | if (false === $file && defined('HHVM_VERSION')) { 355 | $file = $this->findFileWithExtension($class, '.hh'); 356 | } 357 | 358 | if (null !== $this->apcuPrefix) { 359 | apcu_add($this->apcuPrefix.$class, $file); 360 | } 361 | 362 | if (false === $file) { 363 | // Remember that this class does not exist. 364 | $this->missingClasses[$class] = true; 365 | } 366 | 367 | return $file; 368 | } 369 | 370 | private function findFileWithExtension($class, $ext) 371 | { 372 | // PSR-4 lookup 373 | $logicalPathPsr4 = strtr($class, '\\', DIRECTORY_SEPARATOR) . $ext; 374 | 375 | $first = $class[0]; 376 | if (isset($this->prefixLengthsPsr4[$first])) { 377 | $subPath = $class; 378 | while (false !== $lastPos = strrpos($subPath, '\\')) { 379 | $subPath = substr($subPath, 0, $lastPos); 380 | $search = $subPath . '\\'; 381 | if (isset($this->prefixDirsPsr4[$search])) { 382 | $pathEnd = DIRECTORY_SEPARATOR . substr($logicalPathPsr4, $lastPos + 1); 383 | foreach ($this->prefixDirsPsr4[$search] as $dir) { 384 | if (file_exists($file = $dir . $pathEnd)) { 385 | return $file; 386 | } 387 | } 388 | } 389 | } 390 | } 391 | 392 | // PSR-4 fallback dirs 393 | foreach ($this->fallbackDirsPsr4 as $dir) { 394 | if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr4)) { 395 | return $file; 396 | } 397 | } 398 | 399 | // PSR-0 lookup 400 | if (false !== $pos = strrpos($class, '\\')) { 401 | // namespaced class name 402 | $logicalPathPsr0 = substr($logicalPathPsr4, 0, $pos + 1) 403 | . strtr(substr($logicalPathPsr4, $pos + 1), '_', DIRECTORY_SEPARATOR); 404 | } else { 405 | // PEAR-like class name 406 | $logicalPathPsr0 = strtr($class, '_', DIRECTORY_SEPARATOR) . $ext; 407 | } 408 | 409 | if (isset($this->prefixesPsr0[$first])) { 410 | foreach ($this->prefixesPsr0[$first] as $prefix => $dirs) { 411 | if (0 === strpos($class, $prefix)) { 412 | foreach ($dirs as $dir) { 413 | if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr0)) { 414 | return $file; 415 | } 416 | } 417 | } 418 | } 419 | } 420 | 421 | // PSR-0 fallback dirs 422 | foreach ($this->fallbackDirsPsr0 as $dir) { 423 | if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr0)) { 424 | return $file; 425 | } 426 | } 427 | 428 | // PSR-0 include paths. 429 | if ($this->useIncludePath && $file = stream_resolve_include_path($logicalPathPsr0)) { 430 | return $file; 431 | } 432 | 433 | return false; 434 | } 435 | } 436 | 437 | /** 438 | * Scope isolated include. 439 | * 440 | * Prevents access to $this/self from included files. 441 | */ 442 | function includeFile($file) 443 | { 444 | include $file; 445 | } 446 | -------------------------------------------------------------------------------- /vendor/composer/InstalledVersions.php: -------------------------------------------------------------------------------- 1 | 26 | array ( 27 | 'pretty_version' => 'dev-develop', 28 | 'version' => 'dev-develop', 29 | 'aliases' => 30 | array ( 31 | ), 32 | 'reference' => '47c51ab15542055b42183b14a29ed0b206e7a0f7', 33 | 'name' => 'trilbymedia/sitemap', 34 | ), 35 | 'versions' => 36 | array ( 37 | 'trilbymedia/sitemap' => 38 | array ( 39 | 'pretty_version' => 'dev-develop', 40 | 'version' => 'dev-develop', 41 | 'aliases' => 42 | array ( 43 | ), 44 | 'reference' => '47c51ab15542055b42183b14a29ed0b206e7a0f7', 45 | ), 46 | ), 47 | ); 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | public static function getInstalledPackages() 56 | { 57 | return array_keys(self::$installed['versions']); 58 | } 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | public static function isInstalled($packageName) 69 | { 70 | return isset(self::$installed['versions'][$packageName]); 71 | } 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | public static function satisfies(VersionParser $parser, $packageName, $constraint) 87 | { 88 | $constraint = $parser->parseConstraints($constraint); 89 | $provided = $parser->parseConstraints(self::getVersionRanges($packageName)); 90 | 91 | return $provided->matches($constraint); 92 | } 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | public static function getVersionRanges($packageName) 104 | { 105 | if (!isset(self::$installed['versions'][$packageName])) { 106 | throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed'); 107 | } 108 | 109 | $ranges = array(); 110 | if (isset(self::$installed['versions'][$packageName]['pretty_version'])) { 111 | $ranges[] = self::$installed['versions'][$packageName]['pretty_version']; 112 | } 113 | if (array_key_exists('aliases', self::$installed['versions'][$packageName])) { 114 | $ranges = array_merge($ranges, self::$installed['versions'][$packageName]['aliases']); 115 | } 116 | if (array_key_exists('replaced', self::$installed['versions'][$packageName])) { 117 | $ranges = array_merge($ranges, self::$installed['versions'][$packageName]['replaced']); 118 | } 119 | if (array_key_exists('provided', self::$installed['versions'][$packageName])) { 120 | $ranges = array_merge($ranges, self::$installed['versions'][$packageName]['provided']); 121 | } 122 | 123 | return implode(' || ', $ranges); 124 | } 125 | 126 | 127 | 128 | 129 | 130 | public static function getVersion($packageName) 131 | { 132 | if (!isset(self::$installed['versions'][$packageName])) { 133 | throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed'); 134 | } 135 | 136 | if (!isset(self::$installed['versions'][$packageName]['version'])) { 137 | return null; 138 | } 139 | 140 | return self::$installed['versions'][$packageName]['version']; 141 | } 142 | 143 | 144 | 145 | 146 | 147 | public static function getPrettyVersion($packageName) 148 | { 149 | if (!isset(self::$installed['versions'][$packageName])) { 150 | throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed'); 151 | } 152 | 153 | if (!isset(self::$installed['versions'][$packageName]['pretty_version'])) { 154 | return null; 155 | } 156 | 157 | return self::$installed['versions'][$packageName]['pretty_version']; 158 | } 159 | 160 | 161 | 162 | 163 | 164 | public static function getReference($packageName) 165 | { 166 | if (!isset(self::$installed['versions'][$packageName])) { 167 | throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed'); 168 | } 169 | 170 | if (!isset(self::$installed['versions'][$packageName]['reference'])) { 171 | return null; 172 | } 173 | 174 | return self::$installed['versions'][$packageName]['reference']; 175 | } 176 | 177 | 178 | 179 | 180 | 181 | public static function getRootPackage() 182 | { 183 | return self::$installed['root']; 184 | } 185 | 186 | 187 | 188 | 189 | 190 | 191 | 192 | public static function getRawData() 193 | { 194 | return self::$installed; 195 | } 196 | 197 | 198 | 199 | 200 | 201 | 202 | 203 | 204 | 205 | 206 | 207 | 208 | 209 | 210 | 211 | 212 | 213 | 214 | 215 | public static function reload($data) 216 | { 217 | self::$installed = $data; 218 | } 219 | } 220 | -------------------------------------------------------------------------------- /vendor/composer/LICENSE: -------------------------------------------------------------------------------- 1 | 2 | Copyright (c) Nils Adermann, Jordi Boggiano 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining a copy 5 | of this software and associated documentation files (the "Software"), to deal 6 | in the Software without restriction, including without limitation the rights 7 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | copies of the Software, and to permit persons to whom the Software is furnished 9 | to do so, subject to the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be included in all 12 | copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | THE SOFTWARE. 21 | 22 | -------------------------------------------------------------------------------- /vendor/composer/autoload_classmap.php: -------------------------------------------------------------------------------- 1 | $vendorDir . '/composer/InstalledVersions.php', 10 | 'Grav\\Plugin\\SitemapPlugin' => $baseDir . '/sitemap.php', 11 | ); 12 | -------------------------------------------------------------------------------- /vendor/composer/autoload_namespaces.php: -------------------------------------------------------------------------------- 1 | array($baseDir . '/classes'), 10 | ); 11 | -------------------------------------------------------------------------------- /vendor/composer/autoload_real.php: -------------------------------------------------------------------------------- 1 | = 50600 && !defined('HHVM_VERSION') && (!function_exists('zend_loader_file_encoded') || !zend_loader_file_encoded()); 32 | if ($useStaticLoader) { 33 | require __DIR__ . '/autoload_static.php'; 34 | 35 | call_user_func(\Composer\Autoload\ComposerStaticInit751b26473acbe012cea5482fdb145bdc::getInitializer($loader)); 36 | } else { 37 | $map = require __DIR__ . '/autoload_namespaces.php'; 38 | foreach ($map as $namespace => $path) { 39 | $loader->set($namespace, $path); 40 | } 41 | 42 | $map = require __DIR__ . '/autoload_psr4.php'; 43 | foreach ($map as $namespace => $path) { 44 | $loader->setPsr4($namespace, $path); 45 | } 46 | 47 | $classMap = require __DIR__ . '/autoload_classmap.php'; 48 | if ($classMap) { 49 | $loader->addClassMap($classMap); 50 | } 51 | } 52 | 53 | $loader->register(true); 54 | 55 | return $loader; 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /vendor/composer/autoload_static.php: -------------------------------------------------------------------------------- 1 | 11 | array ( 12 | 'Grav\\Plugin\\Sitemap\\' => 20, 13 | ), 14 | ); 15 | 16 | public static $prefixDirsPsr4 = array ( 17 | 'Grav\\Plugin\\Sitemap\\' => 18 | array ( 19 | 0 => __DIR__ . '/../..' . '/classes', 20 | ), 21 | ); 22 | 23 | public static $classMap = array ( 24 | 'Composer\\InstalledVersions' => __DIR__ . '/..' . '/composer/InstalledVersions.php', 25 | 'Grav\\Plugin\\SitemapPlugin' => __DIR__ . '/../..' . '/sitemap.php', 26 | ); 27 | 28 | public static function getInitializer(ClassLoader $loader) 29 | { 30 | return \Closure::bind(function () use ($loader) { 31 | $loader->prefixLengthsPsr4 = ComposerStaticInit751b26473acbe012cea5482fdb145bdc::$prefixLengthsPsr4; 32 | $loader->prefixDirsPsr4 = ComposerStaticInit751b26473acbe012cea5482fdb145bdc::$prefixDirsPsr4; 33 | $loader->classMap = ComposerStaticInit751b26473acbe012cea5482fdb145bdc::$classMap; 34 | 35 | }, null, ClassLoader::class); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /vendor/composer/installed.json: -------------------------------------------------------------------------------- 1 | { 2 | "packages": [], 3 | "dev": true, 4 | "dev-package-names": [] 5 | } 6 | -------------------------------------------------------------------------------- /vendor/composer/installed.php: -------------------------------------------------------------------------------- 1 | 3 | array ( 4 | 'pretty_version' => 'dev-develop', 5 | 'version' => 'dev-develop', 6 | 'aliases' => 7 | array ( 8 | ), 9 | 'reference' => '47c51ab15542055b42183b14a29ed0b206e7a0f7', 10 | 'name' => 'trilbymedia/sitemap', 11 | ), 12 | 'versions' => 13 | array ( 14 | 'trilbymedia/sitemap' => 15 | array ( 16 | 'pretty_version' => 'dev-develop', 17 | 'version' => 'dev-develop', 18 | 'aliases' => 19 | array ( 20 | ), 21 | 'reference' => '47c51ab15542055b42183b14a29ed0b206e7a0f7', 22 | ), 23 | ), 24 | ); 25 | -------------------------------------------------------------------------------- /vendor/composer/platform_check.php: -------------------------------------------------------------------------------- 1 | = 70103)) { 8 | $issues[] = 'Your Composer dependencies require a PHP version ">= 7.1.3". You are running ' . PHP_VERSION . '.'; 9 | } 10 | 11 | if ($issues) { 12 | if (!headers_sent()) { 13 | header('HTTP/1.1 500 Internal Server Error'); 14 | } 15 | if (!ini_get('display_errors')) { 16 | if (PHP_SAPI === 'cli' || PHP_SAPI === 'phpdbg') { 17 | fwrite(STDERR, 'Composer detected issues in your platform:' . PHP_EOL.PHP_EOL . implode(PHP_EOL, $issues) . PHP_EOL.PHP_EOL); 18 | } elseif (!headers_sent()) { 19 | echo 'Composer detected issues in your platform:' . PHP_EOL.PHP_EOL . str_replace('You are running '.PHP_VERSION.'.', '', implode(PHP_EOL, $issues)) . PHP_EOL.PHP_EOL; 20 | } 21 | } 22 | trigger_error( 23 | 'Composer detected issues in your platform: ' . implode(' ', $issues), 24 | E_USER_ERROR 25 | ); 26 | } 27 | --------------------------------------------------------------------------------