├── formwidgets ├── sitemapitems │ ├── partials │ │ ├── _itemlist.htm │ │ ├── _items.htm │ │ ├── _sitemapitems.htm │ │ ├── _editortemplate.htm │ │ └── _item.htm │ └── assets │ │ └── js │ │ └── sitemap-items-editor.js └── SitemapItems.php ├── controllers ├── definitions │ ├── index.php │ ├── config_form.yaml │ ├── _form_sitemap_url.php │ └── _cms_page_list.php └── Definitions.php ├── models ├── definition │ └── fields.yaml └── Definition.php ├── updates ├── v1.0.2 │ └── create_definitions_table.php ├── v2.0.0 │ └── rename_tables.php ├── version.yaml └── v2.0.1 │ └── rename_indexes.php ├── routes.php ├── LICENSE ├── composer.json ├── lang ├── ru │ └── lang.php ├── fa │ └── lang.php ├── es │ └── lang.php ├── fr │ └── lang.php ├── pt-br │ └── lang.php ├── en │ └── lang.php ├── hu │ └── lang.php ├── tr │ └── lang.php ├── pl │ └── lang.php ├── nl │ └── lang.php ├── cs │ └── lang.php ├── sk │ └── lang.php └── sl │ └── lang.php ├── assets ├── js │ └── sitemap-definitions.js └── sitemap.xsl ├── classes ├── definitionitem │ └── fields.yaml └── DefinitionItem.php ├── Plugin.php └── README.md /formwidgets/sitemapitems/partials/_itemlist.htm: -------------------------------------------------------------------------------- 1 | 2 | 3 | makePartial('item', ['item' => $item]) ?> 4 | 5 | -------------------------------------------------------------------------------- /controllers/definitions/index.php: -------------------------------------------------------------------------------- 1 | fatalError): ?> 2 | 3 |

fatalError)) ?>

4 |

5 | 6 | 7 | -------------------------------------------------------------------------------- /formwidgets/sitemapitems/partials/_items.htm: -------------------------------------------------------------------------------- 1 |
    2 | makePartial('itemlist', ['items' => $items]) ?> 3 |
4 | 5 | 10 | 11 | -------------------------------------------------------------------------------- /models/definition/fields.yaml: -------------------------------------------------------------------------------- 1 | # =================================== 2 | # Form Field Definitions 3 | # =================================== 4 | 5 | fields: 6 | form_sitemap_url: 7 | type: partial 8 | context: update 9 | 10 | tabs: 11 | stretch: true 12 | 13 | fields: 14 | items: 15 | stretch: true 16 | tab: winter.sitemap::lang.item.definition 17 | type: Winter\Sitemap\FormWidgets\SitemapItems 18 | -------------------------------------------------------------------------------- /controllers/definitions/config_form.yaml: -------------------------------------------------------------------------------- 1 | # =================================== 2 | # Form Behavior Config 3 | # =================================== 4 | 5 | # Record name 6 | name: winter.sitemap::lang.item.definition 7 | 8 | # Model Form Field configuration 9 | form: $/winter/sitemap/models/definition/fields.yaml 10 | 11 | # Model Class name 12 | modelClass: Winter\Sitemap\Models\Definition 13 | 14 | # Default redirect location 15 | defaultRedirect: system/settings 16 | 17 | # Update page 18 | update: 19 | title: winter.sitemap::lang.plugin.name 20 | redirect: winter/sitemap/definitions 21 | redirectClose: system/settings 22 | -------------------------------------------------------------------------------- /controllers/definitions/_form_sitemap_url.php: -------------------------------------------------------------------------------- 1 |
2 |
3 |
4 |
5 |

6 |

7 |

8 | 9 |

10 |
11 |
12 |
13 |
14 | -------------------------------------------------------------------------------- /updates/v1.0.2/create_definitions_table.php: -------------------------------------------------------------------------------- 1 | engine = 'InnoDB'; 13 | $table->increments('id'); 14 | $table->string('theme')->nullable()->index(); 15 | $table->mediumtext('data')->nullable(); 16 | $table->timestamps(); 17 | }); 18 | } 19 | 20 | public function down() 21 | { 22 | Schema::dropIfExists('rainlab_sitemap_definitions'); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /updates/v2.0.0/rename_tables.php: -------------------------------------------------------------------------------- 1 | getDirName(); 11 | 12 | try { 13 | $definition = Definition::where('theme', $themeActive)->firstOrFail(); 14 | } catch (ModelNotFound $e) { 15 | Log::info(trans('winter.sitemap::lang.definition.not_found')); 16 | return App::make(Controller::class)->setStatusCode(404)->run('/404'); 17 | } 18 | 19 | return Response::make($definition->generateSitemap()) 20 | ->header('Content-Type', 'application/xml'); 21 | }); 22 | 23 | Route::get('sitemap.xsl', function () { 24 | return Response::make(file_get_contents(plugins_path('winter/sitemap/assets/sitemap.xsl'))) 25 | ->header('Content-Type', 'text/xsl'); 26 | }); 27 | -------------------------------------------------------------------------------- /formwidgets/sitemapitems/partials/_sitemapitems.htm: -------------------------------------------------------------------------------- 1 |
8 |
9 |
13 | makePartial('items', ['items' => $items]) ?> 14 |
15 |
16 | 17 | 20 | 21 | 24 |
25 | 26 | 27 |






28 | -------------------------------------------------------------------------------- /formwidgets/sitemapitems/partials/_editortemplate.htm: -------------------------------------------------------------------------------- 1 | 5 | 14 | -------------------------------------------------------------------------------- /formwidgets/sitemapitems/partials/_item.htm: -------------------------------------------------------------------------------- 1 | 4 |
  • 8 |
    9 | 10 | getReferenceDescription($item)) ?> 11 | 12 | : priority) ?>, 13 | : changefreq)) ?> 14 | 15 | 16 | 17 | 26 |
    27 |
  • -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2014-2021.03.01 October CMS 4 | Copyright (c) 2021 Winter CMS 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in all 14 | copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | SOFTWARE. 23 | -------------------------------------------------------------------------------- /updates/version.yaml: -------------------------------------------------------------------------------- 1 | "1.0.1": First version of Sitemap 2 | "1.0.2": 3 | - Create definitions table 4 | - v1.0.2/create_definitions_table.php 5 | "1.0.3": Minor improvements to the code. 6 | "1.0.4": Fixes issue where correct headers not being sent. 7 | "1.0.5": Minor back-end styling fix. 8 | "1.0.6": Minor fix to internal API. 9 | "1.0.7": Added access premissions. 10 | "1.0.8": Minor styling updates. 11 | "1.0.9": Replaced the 500 error with 404 when no definition is found. Added Czech translation. Improved Turkish, Hungarian and Portuguese (Brazil) translations. 12 | "2.0.0": 13 | - Rebrand to Winter.sitemap 14 | - v2.0.0/rename_tables.php 15 | "2.0.1": 16 | - Rebrand table indexes 17 | - v2.0.1/rename_indexes.php 18 | "2.0.2": 19 | - "Add composer replace config." 20 | - "New events to allow extension." 21 | - "Fire 'pages.menuItem.resolveItem' as a halting event." 22 | "2.1.0": 23 | - "Fix saving sitemap definitions" 24 | - "Fix migrations on Laravel 9+" 25 | - "Invalid URLs are no longer added to the generated sitemap" 26 | - "Improve resolving the sitemap.xsl stylesheet" 27 | "2.1.1": "Fix saving sitemap definitions" 28 | "2.2.0": "Use new default backend views from Winter v1.2.8+" 29 | "2.2.1": "Fix form not saving" 30 | "2.2.2": "Fix caching issue with the js asset" 31 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "winter/wn-sitemap-plugin", 3 | "type": "winter-plugin", 4 | "description": "Sitemap plugin for Winter CMS", 5 | "homepage": "https://github.com/wintercms/wn-sitemap-plugin", 6 | "keywords": ["winter cms", "winter", "plugin", "cms", "sitemap"], 7 | "license": "MIT", 8 | "authors": [ 9 | { 10 | "name": "Alexey Bobkov", 11 | "email": "aleksey.bobkov@gmail.com", 12 | "role": "Original Author" 13 | }, 14 | { 15 | "name": "Samuel Georges", 16 | "email": "daftspunky@gmail.com", 17 | "role": "Original Author" 18 | }, 19 | { 20 | "name": "Winter CMS Maintainers", 21 | "homepage": "https://wintercms.com", 22 | "role": "Maintainer" 23 | } 24 | ], 25 | "support": { 26 | "issues": "https://github.com/wintercms/wn-sitemap-plugin/issues", 27 | "discord": "https://discord.gg/D5MFSPH6Ux", 28 | "source": "https://github.com/wintercms/wn-sitemap-plugin" 29 | }, 30 | "require": { 31 | "php": ">=8.1", 32 | "composer/installers": "~1.0", 33 | "winter/wn-backend-module": "^1.2.8 || dev-develop" 34 | }, 35 | "replace": { 36 | "rainlab/sitemap-plugin": "~1.0" 37 | }, 38 | "extra": { 39 | "installer-name": "sitemap" 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /lang/ru/lang.php: -------------------------------------------------------------------------------- 1 | [ 5 | 'name' => 'Карта сайта', 6 | 'description' => 'Создать файл sitemap.xml для вашего сайта.' 7 | ], 8 | 'item' => [ 9 | 'location' => 'расположение:', 10 | 'priority' => 'Приоритет', 11 | 'changefreq' => 'Частота изменений', 12 | 'always' => 'всегда', 13 | 'hourly' => 'ежечасно', 14 | 'daily' => 'ежедневно', 15 | 'weekly' => 'еженедельно', 16 | 'monthly' => 'ежемесячно', 17 | 'yearly' => 'годовой', 18 | 'never' => 'никогда', 19 | 'editor_title' => 'Редактирование элемента карты сайта', 20 | 'type' => 'Тип', 21 | 'allow_nested_items' => 'Разрешить вложенные элементы', 22 | 'allow_nested_items_comment' => 'Вложенные элементы могут быть созданы динамически статическими страницами и некоторыми другими типами элементов', 23 | 'url' => 'URL', 24 | 'reference' => 'Ссылка', 25 | 'title_required' => 'Заголовок обязателен для заполнения', 26 | 'unknown_type' => 'Неизвестный тип', 27 | 'unnamed' => 'Элемент без имени', 28 | 'add_item' => 'Добавить элемент', 29 | 'new_item' => 'Новый элемент', 30 | 'cms_page' => 'CMS Страница', 31 | 'cms_page_comment' => 'Выберите страницу, чтобы использовать URL-адрес.', 32 | 'reference_required' => 'Ссылка на документ обязательна', 33 | 'url_required' => 'URL обязателен для заполнения', 34 | 'cms_page_required' => 'Пожалуйста, выберите страницу CMS', 35 | 'page' => 'страница', 36 | 'check' => 'Проверить' 37 | ] 38 | ]; 39 | -------------------------------------------------------------------------------- /lang/fa/lang.php: -------------------------------------------------------------------------------- 1 | [ 5 | 'name' => 'نقشه سایت', 6 | 'description' => 'تولید فایل sitemap.xml برای سایت شما.', 7 | 'permissions' => [ 8 | 'access_settings' => 'دسترسی به تنظیمات نقشه سایت', 9 | ], 10 | ], 11 | 'item' => [ 12 | 'location' => 'محل:', 13 | 'priority' => 'اولویت', 14 | 'changefreq' => 'زمان تقریبی ایجاد تغییرات در صفحه', 15 | 'always' => 'همیشه', 16 | 'hourly' => 'هر ساعت', 17 | 'daily' => 'هر روز', 18 | 'weekly' => 'هر هفته', 19 | 'monthly' => 'هر ماه', 20 | 'yearly' => 'هر سال', 21 | 'never' => 'هرگز', 22 | 'editor_title' => 'ویرایش مورد نقشه سایت', 23 | 'type' => 'نوع', 24 | 'allow_nested_items' => 'موارد تو در تو مجاز است', 25 | 'allow_nested_items_comment' => 'موارد تو در تو توسط صفحات استاتیک و یا سایر افزونه ها ممکن است ایجاد شوند', 26 | 'url' => 'آدرس', 27 | 'reference' => 'منبع', 28 | 'title_required' => 'وارد کردن عنوان اجباریست', 29 | 'unknown_type' => 'نوع مورد ناشناخته', 30 | 'unnamed' => 'مورد بدون نام', 31 | 'add_item' => 'I افزودن مورد', 32 | 'new_item' => 'مورد جدید', 33 | 'cms_page' => 'صفحه مدیریت محتوی', 34 | 'cms_page_comment' => 'صفحه ای را جهت استفاده در آدرس انتخاب نمایید', 35 | 'reference_required' => 'وارد کردن منبع اجباریست', 36 | 'url_required' => 'وارد کردن آدرس اجباریست', 37 | 'cms_page_required' => 'لطفا یک صفحه مدیریت محتوی را انتخاب نمایید.', 38 | 'page' => 'صفحه', 39 | 'check' => 'بررسی' 40 | ] 41 | ]; 42 | -------------------------------------------------------------------------------- /lang/es/lang.php: -------------------------------------------------------------------------------- 1 | [ 5 | 'name' => 'Sitemap (mapa del sitio)', 6 | 'description' => 'Generar un fichero sitemap.xml para tu sitio web.' 7 | ], 8 | 'item' => [ 9 | 'location' => 'Localización', 10 | 'priority' => 'Prioridad', 11 | 'changefreq' => 'Frecuencia de cambio', 12 | 'always' => 'siempre', 13 | 'hourly' => 'A cada hora', 14 | 'daily' => 'Diariamente', 15 | 'weekly' => 'Semanalmente', 16 | 'monthly' => 'Mensualmente', 17 | 'yearly' => 'Anualmente', 18 | 'never' => 'nunca', 19 | 'editor_title' => 'Edita el elemento del Sitemap', 20 | 'type' => 'Tipo', 21 | 'allow_nested_items' => 'Permitir elemento anidados', 22 | 'allow_nested_items_comment' => 'Los elementos anidados podrán ser generados dinámicamente por páginas estáticas y otros tipos de elementos', 23 | 'url' => 'URL', 24 | 'reference' => 'Referencia', 25 | 'title_required' => 'El título es requerido', 26 | 'unknown_type' => 'Tipo de elemento desconocido', 27 | 'unnamed' => 'Elemento sin nombre', 28 | 'add_item' => 'Añadir Elemento', 29 | 'new_item' => 'Nuevo elemento', 30 | 'cms_page' => 'CMS Página', 31 | 'cms_page_comment' => 'Seleccione la página a usar para la dirección URL.', 32 | 'reference_required' => 'La referencia al elemento es requerida.', 33 | 'url_required' => 'La URL es requerida', 34 | 'cms_page_required' => 'Por favor seleccione una CMS página', 35 | 'page' => 'Página', 36 | 'check' => 'Verificar' 37 | ] 38 | ]; 39 | -------------------------------------------------------------------------------- /assets/js/sitemap-definitions.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Handles the Pages main page. 3 | */ 4 | +function ($) { "use strict"; 5 | var SitemapDefinitions = function () { 6 | this.init() 7 | } 8 | 9 | SitemapDefinitions.prototype.init = function() { 10 | /* 11 | * Bind event handlers 12 | */ 13 | var self = this 14 | 15 | /* 16 | * Handle the sitemap saving 17 | */ 18 | $(document).on('oc.beforeRequest', '#Form', function(e, data) { 19 | return self.onSaveSitemapItems(this, e, data) 20 | }) 21 | 22 | /* 23 | * Compact tab pane 24 | */ 25 | var $primaryPanel = $('.control-tabs.primary-tabs') 26 | $('.tab-pane', $primaryPanel).addClass('pane-compact') 27 | } 28 | 29 | /* 30 | * Triggered before sitemap is saved 31 | */ 32 | SitemapDefinitions.prototype.onSaveSitemapItems = function(form, e, data) { 33 | var items = [], 34 | $items = $('div[data-control=treeview] > ol > li', form) 35 | 36 | var iterator = function(items) { 37 | var result = [] 38 | 39 | $.each(items, function() { 40 | var item = $(this).data('sitemap-item') 41 | 42 | var $subitems = $('> ol >li', this) 43 | if ($subitems.length) 44 | item['items'] = iterator($subitems) 45 | 46 | result.push(item) 47 | }) 48 | 49 | return result 50 | } 51 | 52 | const fieldName = $('div[data-control=sitemap-item-editor]').data('fieldName') 53 | data.options.data[fieldName] = iterator($items); 54 | } 55 | 56 | $(document).ready(function(){ 57 | $.wn.sitemapDefinitions = new SitemapDefinitions() 58 | }) 59 | 60 | }(window.jQuery); 61 | -------------------------------------------------------------------------------- /lang/fr/lang.php: -------------------------------------------------------------------------------- 1 | [ 5 | 'name' => 'Sitemap (Plan du site)', 6 | 'description' => 'Générer un fichier sitemap.xml pour votre site web.', 7 | 'permissions' => [ 8 | 'access_settings' => 'Accéder aux paramètres de configuration du Sitemap', 9 | 'access_definitions' => 'Accédez à la page des définitions du sitemap', 10 | ], 11 | ], 12 | 'item' => [ 13 | 'location' => 'Emplacement:', 14 | 'priority' => 'Priorité', 15 | 'changefreq' => 'Frequence de changement', 16 | 'always' => 'toujours', 17 | 'hourly' => 'horaire', 18 | 'daily' => 'tous les jours', 19 | 'weekly' => 'hebdomadaire', 20 | 'monthly' => 'mensuelle', 21 | 'yearly' => 'annuellement', 22 | 'never' => 'jamais', 23 | 'editor_title' => 'Modifier un élément du Sitemap', 24 | 'type' => 'Type', 25 | 'allow_nested_items' => 'Autoriser les éléments imbriqués', 26 | 'allow_nested_items_comment' => 'Nested items could be generated dynamically by static page and some other item types', 27 | 'url' => 'URL', 28 | 'reference' => 'Référence', 29 | 'title_required' => 'Le titre est requis', 30 | 'unknown_type' => 'Elément de type inconnu', 31 | 'unnamed' => 'Elément sans nom', 32 | 'add_item' => 'Ajouter Elément', 33 | 'new_item' => 'Nouvel élément', 34 | 'cms_page' => 'CMS Page', 35 | 'cms_page_comment' => 'Sélectionnez la page à utiliser pour l\'adresse URL.', 36 | 'reference_required' => 'La référence de l\'élément est requise.', 37 | 'url_required' => 'L\'URL est requise', 38 | 'cms_page_required' => 'S\'il vous plaît sélectionner une page CMS', 39 | 'page' => 'Page', 40 | 'check' => 'Vérifier' 41 | ] 42 | ]; 43 | -------------------------------------------------------------------------------- /classes/definitionitem/fields.yaml: -------------------------------------------------------------------------------- 1 | # =================================== 2 | # Form Field Definitions 3 | # =================================== 4 | 5 | fields: 6 | 7 | type: 8 | label: winter.sitemap::lang.item.type 9 | type: dropdown 10 | 11 | url: 12 | label: winter.sitemap::lang.item.url 13 | 14 | reference: 15 | label: winter.sitemap::lang.item.reference 16 | type: dropdown 17 | cssClass: input-sidebar-control 18 | 19 | cmsPage: 20 | label: winter.sitemap::lang.item.cms_page 21 | comment: winter.sitemap::lang.item.cms_page_comment 22 | type: dropdown 23 | cssClass: input-sidebar-control 24 | 25 | nesting: 26 | label: winter.sitemap::lang.item.allow_nested_items 27 | comment: winter.sitemap::lang.item.allow_nested_items_comment 28 | type: checkbox 29 | default: true 30 | 31 | priority: 32 | span: left 33 | label: winter.sitemap::lang.item.priority 34 | type: dropdown 35 | options: 36 | '0.1': '0.1' 37 | '0.2': '0.2' 38 | '0.3': '0.3' 39 | '0.4': '0.4' 40 | '0.5': '0.5' 41 | '0.6': '0.6' 42 | '0.7': '0.7' 43 | '0.8': '0.8' 44 | '0.9': '0.9' 45 | '1.0': '1.0' 46 | 47 | changefreq: 48 | span: right 49 | label: winter.sitemap::lang.item.changefreq 50 | type: dropdown 51 | options: 52 | always: winter.sitemap::lang.item.always 53 | hourly: winter.sitemap::lang.item.hourly 54 | daily: winter.sitemap::lang.item.daily 55 | weekly: winter.sitemap::lang.item.weekly 56 | monthly: winter.sitemap::lang.item.monthly 57 | yearly: winter.sitemap::lang.item.yearly 58 | never: winter.sitemap::lang.item.never 59 | -------------------------------------------------------------------------------- /controllers/definitions/_cms_page_list.php: -------------------------------------------------------------------------------- 1 | 4 | 5 |
    6 |
    7 | 8 | 9 | 10 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 33 | 34 | 35 | 36 | 37 | 38 |
    11 |
    12 | 13 | 14 |
    15 |
    title) ?>url) ?>
    39 |
    40 |
    -------------------------------------------------------------------------------- /updates/v2.0.1/rename_indexes.php: -------------------------------------------------------------------------------- 1 | updateIndexNames($from, $to, $to); 19 | } 20 | } 21 | 22 | public function down() 23 | { 24 | foreach (self::TABLES as $table) { 25 | $from = 'winter_sitemap_' . $table; 26 | $to = 'rainlab_sitemap_' . $table; 27 | 28 | $this->updateIndexNames($from, $to, $from); 29 | } 30 | } 31 | 32 | public function updateIndexNames($from, $to, $table) 33 | { 34 | Schema::table($table, function ($blueprint) use ($from, $to) { 35 | foreach ($this->getIndexes($blueprint) as $index) { 36 | if (is_object($index) ? $index->isPrimary() : $index['primary']) { 37 | continue; 38 | } 39 | 40 | $old = is_object($index) ? $index->getName() : $index['name']; 41 | $new = str_replace($from, $to, $old); 42 | 43 | $blueprint->renameIndex($old, $new); 44 | } 45 | }); 46 | } 47 | 48 | public function getIndexes($blueprint) 49 | { 50 | $connection = Schema::getConnection(); 51 | $table = $blueprint->getTable(); 52 | 53 | if (method_exists($connection, 'getDoctrineSchemaManager')) { 54 | $sm = $connection->getDoctrineSchemaManager(); 55 | return $sm->listTableDetails($table)->getIndexes(); 56 | } else { 57 | return $connection->getSchemaBuilder()->getIndexes($table); 58 | } 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /Plugin.php: -------------------------------------------------------------------------------- 1 | 'winter.sitemap::lang.plugin.name', 22 | 'description' => 'winter.sitemap::lang.plugin.description', 23 | 'author' => 'Winter CMS', 24 | 'icon' => 'icon-sitemap', 25 | 'homepage' => 'https://github.com/wintercms/wn-sitemap-plugin', 26 | 'replaces' => ['RainLab.Sitemap' => '<= 1.0.9'], 27 | ]; 28 | } 29 | 30 | /** 31 | * Registers administrator permissions for this plugin. 32 | */ 33 | public function registerPermissions(): array 34 | { 35 | return [ 36 | 'winter.sitemap.access_definitions' => [ 37 | 'tab' => 'winter.sitemap::lang.plugin.name', 38 | 'label' => 'winter.sitemap::lang.plugin.permissions.access_definitions', 39 | 'roles' => [UserRole::CODE_DEVELOPER], 40 | ], 41 | ]; 42 | } 43 | 44 | /** 45 | * Registers settings for this plugin. 46 | */ 47 | public function registerSettings(): array 48 | { 49 | return [ 50 | 'definitions' => [ 51 | 'label' => 'winter.sitemap::lang.plugin.name', 52 | 'description' => 'winter.sitemap::lang.plugin.description', 53 | 'icon' => 'icon-sitemap', 54 | 'url' => Backend::url('winter/sitemap/definitions'), 55 | 'category' => SettingsManager::CATEGORY_CMS, 56 | 'permissions' => ['winter.sitemap.access_definitions'], 57 | ] 58 | ]; 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /lang/pt-br/lang.php: -------------------------------------------------------------------------------- 1 | [ 5 | 'name' => 'Sitemap', 6 | 'description' => 'Gera o arquivo sitemap.xml para seu site.', 7 | 'permissions' => [ 8 | 'access_settings' => 'Acessar as definições de configuração so sitemap', 9 | 'access_definitions' => 'Acessar a página de definições do sitemap', 10 | ], 11 | ], 12 | 'item' => [ 13 | 'location' => 'Localização:', 14 | 'priority' => 'Prioridade', 15 | 'changefreq' => 'Mudar a frequência', 16 | 'always' => 'sempre', 17 | 'hourly' => 'de hora em hora', 18 | 'daily' => 'diariamente', 19 | 'weekly' => 'semanalmente', 20 | 'monthly' => 'mensal', 21 | 'yearly' => 'anual', 22 | 'never' => 'nunca', 23 | 'editor_title' => 'Editar item no sitemap', 24 | 'type' => 'Tipo', 25 | 'allow_nested_items' => 'Permitir itens aninhados', 26 | 'allow_nested_items_comment' => 'Itens aninhados poderiam ser gerados dinamicamente por página estática e alguns outros tipos de itens', 27 | 'url' => 'URL', 28 | 'reference' => 'Referência', 29 | 'title_required' => 'O título é obrigatório', 30 | 'unknown_type' => 'Tipo de item Desconhecido', 31 | 'unnamed' => 'Sem nome', 32 | 'add_item' => 'Adicione um Item', 33 | 'new_item' => 'Novo item', 34 | 'cms_page' => 'Página do CMS', 35 | 'cms_page_comment' => 'Selecione a página a ser usado para o endereço URL.', 36 | 'reference_required' => 'A referência é obrigatória.', 37 | 'url_required' => 'A URL é obrigatória.', 38 | 'cms_page_required' => 'Por favor, selecione uma página do CMS', 39 | 'page' => 'Página', 40 | 'check' => 'Checar', 41 | 'definition' => 'Definição', 42 | 'save_definition' => 'Salvando Definição...', 43 | 'load_indicator' => 'Limpando Definição...', 44 | 'empty_confirm' => 'Limpar esta definição?' 45 | ] 46 | ]; 47 | -------------------------------------------------------------------------------- /lang/en/lang.php: -------------------------------------------------------------------------------- 1 | [ 5 | 'name' => 'Sitemap', 6 | 'description' => 'Generate a sitemap.xml file for your website.', 7 | 'permissions' => [ 8 | 'access_settings' => 'Access sitemap configuration settings', 9 | 'access_definitions' => 'Access sitemap definitions page', 10 | ], 11 | ], 12 | 'item' => [ 13 | 'location' => 'Location:', 14 | 'priority' => 'Priority', 15 | 'changefreq' => 'Change frequency', 16 | 'always' => 'always', 17 | 'hourly' => 'hourly', 18 | 'daily' => 'daily', 19 | 'weekly' => 'weekly', 20 | 'monthly' => 'monthly', 21 | 'yearly' => 'yearly', 22 | 'never' => 'never', 23 | 'editor_title' => 'Edit Sitemap Item', 24 | 'type' => 'Type', 25 | 'allow_nested_items' => 'Allow nested items', 26 | 'allow_nested_items_comment' => 'Nested items could be generated dynamically by static page and some other item types', 27 | 'url' => 'URL', 28 | 'reference' => 'Reference', 29 | 'title_required' => 'The title is required', 30 | 'unknown_type' => 'Unknown item type', 31 | 'unnamed' => 'Unnamed item', 32 | 'add_item' => 'Add Item', 33 | 'new_item' => 'New item', 34 | 'cms_page' => 'CMS Page', 35 | 'cms_page_comment' => 'Select the page to use for the URL address.', 36 | 'reference_required' => 'The item reference is required.', 37 | 'url_required' => 'The URL is required', 38 | 'cms_page_required' => 'Please select a CMS page', 39 | 'page' => 'Page', 40 | 'check' => 'Check', 41 | 'definition' => 'Definition', 42 | 'save_definition' => 'Saving Definition...', 43 | 'load_indicator' => 'Emptying Definition...', 44 | 'empty_confirm' => 'Empty this definition?' 45 | ], 46 | 'definition' => [ 47 | 'not_found' => 'No sitemap definition was found. Try creating one first.' 48 | ] 49 | ]; 50 | -------------------------------------------------------------------------------- /lang/hu/lang.php: -------------------------------------------------------------------------------- 1 | [ 5 | 'name' => 'Oldaltérkép', 6 | 'description' => 'A weboldalhoz sitemap.xml fájl generálása.', 7 | 'permissions' => [ 8 | 'access_settings' => 'Oldaltérkép beállításainak kezelése', 9 | 'access_definitions' => 'Oldaltérkép elemeinek kezelése', 10 | ], 11 | ], 12 | 'item' => [ 13 | 'location' => 'Cím:', 14 | 'priority' => 'Prioritás', 15 | 'changefreq' => 'Gyakoriság', 16 | 'always' => 'mindig', 17 | 'hourly' => 'óránkénti', 18 | 'daily' => 'napi', 19 | 'weekly' => 'heti', 20 | 'monthly' => 'havi', 21 | 'yearly' => 'évi', 22 | 'never' => 'soha', 23 | 'editor_title' => 'Módosítás', 24 | 'type' => 'Típus', 25 | 'allow_nested_items' => 'Allapok megjelenítése', 26 | 'allow_nested_items_comment' => 'A kapcsolódó lapok automatikus listázása, amennyiben támogatott ez a funkció.', 27 | 'url' => 'Webcím', 28 | 'reference' => 'Hivatkozás', 29 | 'title_required' => 'A cím megadása kötelező', 30 | 'unknown_type' => 'Ismeretlen elem típus', 31 | 'unnamed' => 'Névtelen elem', 32 | 'add_item' => 'Hozzáadás', 33 | 'new_item' => 'Új elem', 34 | 'cms_page' => 'Oldal', 35 | 'cms_page_comment' => 'Válassza ki, hogy melyik oldal tartozik a fenti hivatkozáshoz.', 36 | 'reference_required' => 'A hivatkozó elem megadása kötelező.', 37 | 'url_required' => 'A webcím megadása kötelező.', 38 | 'cms_page_required' => 'Válasszon egy oldalt.', 39 | 'page' => 'Oldal', 40 | 'check' => 'Ellenőrzés', 41 | 'definition' => 'Elemek', 42 | 'save_definition' => 'Mentés...', 43 | 'load_indicator' => 'Visszaállítás folyamatban...', 44 | 'empty_confirm' => 'Állítsuk vissza üres állapotba?' 45 | ], 46 | 'definition' => [ 47 | 'not_found' => 'Nincs elkészítve az oldaltérkép. Adjon hozzá legalább egy elemet.' 48 | ] 49 | ]; 50 | -------------------------------------------------------------------------------- /lang/tr/lang.php: -------------------------------------------------------------------------------- 1 | [ 5 | 'name' => 'Site Haritası', 6 | 'description' => 'Web sitenize sitemap.xml dosyası oluşturur.', 7 | 'permissions' => [ 8 | 'access_settings' => 'Site haritası ayarlarına erişim', 9 | 'access_definitions' => 'Site haritası tanımlamalarına erişim', 10 | ], 11 | ], 12 | 'item' => [ 13 | 'location' => 'Konum:', 14 | 'priority' => 'Öncelik', 15 | 'changefreq' => 'Değişiklik Sıklığı', 16 | 'always' => 'sürekli', 17 | 'hourly' => 'saatlik', 18 | 'daily' => 'günlük', 19 | 'weekly' => 'haftalık', 20 | 'monthly' => 'aylık', 21 | 'yearly' => 'yıllık', 22 | 'never' => 'asla', 23 | 'editor_title' => 'Site haritası kaydını düzenle', 24 | 'type' => 'Tür', 25 | 'allow_nested_items' => 'İç içe kayıtlara izin ver', 26 | 'allow_nested_items_comment' => 'İç içe kayıtlar statik sayfalar ve bazı kayıtlar tarafından otomatik olarak oluşturulabilir.', 27 | 'url' => 'URL', 28 | 'reference' => 'Sayfa', 29 | 'title_required' => 'Başlık alanı gereklidir.', 30 | 'unknown_type' => 'Bilinmeyen kayıt türü', 31 | 'unnamed' => 'İsimsiz kayıt', 32 | 'add_item' => 'Yeni kayıt', 33 | 'new_item' => 'Yeni kayıt', 34 | 'cms_page' => 'CMS Sayfası', 35 | 'cms_page_comment' => 'Select the page to use for the URL address.', 36 | 'reference_required' => 'Kayıt sayfası alanı gereklidir.', 37 | 'url_required' => 'URL alanı gereklidir.', 38 | 'cms_page_required' => 'Lütfen bir CMS sayfası seçiniz.', 39 | 'page' => 'Sayfa', 40 | 'check' => 'Kontrol et', 41 | 'definition' => 'Tanımlama', 42 | 'save_definition' => 'Tanımlama Kaydediliyor...', 43 | 'load_indicator' => 'Tanımlama Boşaltılıyor...', 44 | 'empty_confirm' => 'Tanımlama boşaltılsın mı?' 45 | ], 46 | 'definition' => [ 47 | 'not_found' => 'Site haritası tanımlaması bulunamadı. Hemen bir tane oluşturun.', 48 | ], 49 | ]; 50 | -------------------------------------------------------------------------------- /lang/pl/lang.php: -------------------------------------------------------------------------------- 1 | [ 3 | 'name' => 'Mapa strony', 4 | 'description' => 'Generuje plik sitemap.xml dla Twojej strony.', 5 | 'permissions' => [ 6 | 'access_settings' => 'Zarządzaj ustawieniami wtyczki', 7 | 'access_definitions' => 'Zarządzaj definicją mapy strony', 8 | ], 9 | ], 10 | 'item' => [ 11 | 'location' => 'Lokacja:', 12 | 'priority' => 'Priorytet', 13 | 'changefreq' => 'Częstotliwość zmian', 14 | 'always' => 'cały czas', 15 | 'hourly' => 'co godzinę', 16 | 'daily' => 'codziennie', 17 | 'weekly' => 'co tydzień', 18 | 'monthly' => 'co miesiąc', 19 | 'yearly' => 'co rok', 20 | 'never' => 'nigdy', 21 | 'editor_title' => 'Edytuj pozycję mapy', 22 | 'type' => 'Typ', 23 | 'allow_nested_items' => 'Pozwól na zagnieżdzanie pozycji', 24 | 'allow_nested_items_comment' => 'Zagnieżdżone pozycje mogą zostać wygenerowane dynamicznie przez statyczne strony i niektóre rodzaje typów pozycji.', 25 | 'url' => 'URL', 26 | 'reference' => 'Odnośnik', 27 | 'title_required' => 'Tytuł jest wymagany', 28 | 'unknown_type' => 'Nieznany typ pozycji', 29 | 'unnamed' => 'Pozycja bez nazwy', 30 | 'add_item' => 'Dodaj Pozycję', 31 | 'new_item' => 'Nowa pozycja', 32 | 'cms_page' => 'Strona CMS', 33 | 'cms_page_comment' => 'Wybierz stronę, której adres URL chcesz umieścić.', 34 | 'reference_required' => 'Odnośnik dla pozycji jest wymagany.', 35 | 'url_required' => 'Adres URL jest wymagany', 36 | 'cms_page_required' => 'Wybierz stronę CMS', 37 | 'page' => 'Strona', 38 | 'check' => 'Sprawdź', 39 | 'definition' => 'Definicje', 40 | 'save_definition' => 'Zapisywanie definicji...', 41 | 'load_indicator' => 'Czyszczenie definicji...', 42 | 'empty_confirm' => 'Wyczyścić tą definicję?', 43 | ], 44 | 'definition' => [ 45 | 'not_found' => 'Nie znaleziono definicji mapy strony. Musisz ją utworzyć.', 46 | ], 47 | ]; -------------------------------------------------------------------------------- /lang/nl/lang.php: -------------------------------------------------------------------------------- 1 | [ 5 | 'name' => 'Sitemap', 6 | 'description' => 'Genereer een sitemap.xml bestand voor je website.', 7 | 'permissions' => [ 8 | 'access_settings' => 'Toegang sitemap configuratie-instellingen', 9 | 'access_definitions' => 'Toegang sitemap-definities pagina', 10 | ], 11 | ], 12 | 'item' => [ 13 | 'location' => 'Locatie:', 14 | 'priority' => 'Prioriteit', 15 | 'changefreq' => 'Verander frequentie', 16 | 'always' => 'altijd', 17 | 'hourly' => 'uurlijks', 18 | 'daily' => 'dagelijks', 19 | 'weekly' => 'wekelijks', 20 | 'monthly' => 'maandelijks', 21 | 'yearly' => 'jaarlijks', 22 | 'never' => 'nooit', 23 | 'editor_title' => 'Bewerk Sitemap-item', 24 | 'type' => 'Type', 25 | 'allow_nested_items' => 'Geneste items toestaan', 26 | 'allow_nested_items_comment' => 'Geneste items kunnen dynamisch worden gegenereerd door statische pagina\'s en sommige andere item-typen', 27 | 'url' => 'URL', 28 | 'reference' => 'Referentie', 29 | 'title_required' => 'De titel is vereist', 30 | 'unknown_type' => 'Onbekend type item', 31 | 'unnamed' => 'Naamloos item', 32 | 'add_item' => 'Item toevoegen', 33 | 'new_item' => 'Nieuw item', 34 | 'cms_page' => 'CMS Pagina', 35 | 'cms_page_comment' => 'Selecteer de pagina die voor het URL-adres moet worden gebruikt.', 36 | 'reference_required' => 'De item-referentie is vereist.', 37 | 'url_required' => 'De URL is vereist', 38 | 'cms_page_required' => 'Selecteer een CMS-pagina', 39 | 'page' => 'Pagina', 40 | 'check' => 'Controleren', 41 | 'definition' => 'Definitie', 42 | 'save_definition' => 'Definitie opslaan...', 43 | 'load_indicator' => 'Definitie leegmaken...', 44 | 'empty_confirm' => 'Deze definitie leegmaken?' 45 | ], 46 | 'definition' => [ 47 | 'not_found' => 'Geen sitemap-definitie gevonden. Probeer er eerst één te maken.' 48 | ] 49 | ]; 50 | -------------------------------------------------------------------------------- /lang/cs/lang.php: -------------------------------------------------------------------------------- 1 | [ 5 | 'name' => 'Mapa stránek', 6 | 'description' => 'Generuje soubor sitemap.xml pro vaše stránky.', 7 | 'permissions' => [ 8 | 'access_settings' => 'Povolit nastavení mapy stránek', 9 | 'access_definitions' => 'Povolit zobrazení definice mapy stránek', // aaa 10 | ], 11 | ], 12 | 'item' => [ 13 | 'location' => 'Umístění:', 14 | 'priority' => 'Priorita', 15 | 'changefreq' => 'Četnost kontroly', 16 | 'always' => 'vždy', 17 | 'hourly' => 'každou hodinu', 18 | 'daily' => 'denně', 19 | 'weekly' => 'týdně', 20 | 'monthly' => 'měsíčně', 21 | 'yearly' => 'ročně', 22 | 'never' => 'nikdy', 23 | 'editor_title' => 'Upravit položku mapy stránek', 24 | 'type' => 'Typ', 25 | 'allow_nested_items' => 'Povolit vnořené položky', 26 | 'allow_nested_items_comment' => 'Vnořené položky mohou být generovány dynamicky statickou stránkou a nebo i jiným typem položky', 27 | 'url' => 'URL', 28 | 'reference' => 'Reference', 29 | 'title_required' => 'Nadpis je povinný', 30 | 'unknown_type' => 'Neznámy typ položky', 31 | 'unnamed' => 'Nepojmenovaná položka', 32 | 'add_item' => 'Přidat Položku', 33 | 'new_item' => 'Nová položka', 34 | 'cms_page' => 'CMS Stránka', 35 | 'cms_page_comment' => 'Vyberte stránku ze které se použije její URL adresa.', 36 | 'reference_required' => 'Je nutno vybrat referenci položky.', 37 | 'url_required' => 'URL adresa je povinná', 38 | 'cms_page_required' => 'Vyberte prosím CMS stránku', 39 | 'page' => 'Stránka', 40 | 'check' => 'Zkontrolovat', 41 | 'definition' => 'Definice', 42 | 'save_definition' => 'Ukládám mapu stránek...', 43 | 'load_indicator' => 'Mažu definici...', 44 | 'empty_confirm' => 'Opravdu chcete smazat definici mapy stránek?', 45 | ], 46 | 'definition' => [ 47 | 'not_found' => 'Žádná sitemap nebyla nalezena. Zkuste nejdříve nějakou vytvořit.', 48 | ], 49 | ]; 50 | -------------------------------------------------------------------------------- /lang/sk/lang.php: -------------------------------------------------------------------------------- 1 | [ 4 | 'name' => 'Mapa stránok', 5 | 'description' => 'Generuje súbor sitemap.xml pre vaše stránky.', 6 | 'permissions' => [ 7 | 'access_settings' => 'Prístup k nastaveniam konfigurácie mapy stránok', 8 | 'access_definitions' => 'Prístup k stránke s definíciami mapy stránok', 9 | ], 10 | ], 11 | 'item' => [ 12 | 'location' => 'Umiestnenie:', 13 | 'priority' => 'Priorita', 14 | 'changefreq' => 'Frekvencia kontroly', 15 | 'always' => 'vždy', 16 | 'hourly' => 'každú hodinu', 17 | 'daily' => 'denne', 18 | 'weekly' => 'týždenne', 19 | 'monthly' => 'mesačne', 20 | 'yearly' => 'ročne', 21 | 'never' => 'nikdy', 22 | 'editor_title' => 'Upraviť položku mapy stránok', 23 | 'type' => 'Typ', 24 | 'allow_nested_items' => 'Povoliť vnorené položky', 25 | 'allow_nested_items_comment' => 'Vnorené položky môžu byť generované dynamicky statickou stránkou a niektorými ďalšími typmi položky', 26 | 'url' => 'URL', 27 | 'reference' => 'Referencia', 28 | 'title_required' => 'Nadpis je povinný', 29 | 'unknown_type' => 'Neznámy typ položky', 30 | 'unnamed' => 'Nepomenovaná položka', 31 | 'add_item' => 'Pridať Položku', 32 | 'new_item' => 'Nová položka', 33 | 'cms_page' => 'CMS Stránka', 34 | 'cms_page_comment' => 'Vyberte stránku z ktorej sa použije jej URL adresa.', 35 | 'reference_required' => 'Vyžaduje sa odkaz na položku.', 36 | 'url_required' => 'URL adresa je povinná', 37 | 'cms_page_required' => 'Vyberte prosím CMS stránku', 38 | 'page' => 'Stránka', 39 | 'check' => 'Skontrolovať', 40 | 'definition' => 'Definícia', 41 | 'save_definition' => 'Ukladám mapu stránok...', 42 | 'load_indicator' => 'Mažem definíciu...', 43 | 'empty_confirm' => 'Určite chcete zmazať definíciu mapy stránok?', 44 | ], 45 | 'definition' => [ 46 | 'not_found' => 'Nebola nájdená žiadna mapa stránok. Najprv skúste nejakú vytvoriť.', 47 | ], 48 | ]; 49 | -------------------------------------------------------------------------------- /lang/sl/lang.php: -------------------------------------------------------------------------------- 1 | [ 5 | 'name' => 'Zemljevid strani', 6 | 'description' => 'Ustvari datoteko sitemap.xml za vaše spletno mesto.', 7 | 'permissions' => [ 8 | 'access_settings' => 'Dostop do nastavitev zemljevida strani', 9 | 'access_definitions' => 'Dostop do strani z definicijami zemljevida strani', 10 | ], 11 | ], 12 | 'item' => [ 13 | 'location' => 'Lokacija:', 14 | 'priority' => 'Prioriteta', 15 | 'changefreq' => 'Spremeni frekvenco', 16 | 'always' => 'vedno', 17 | 'hourly' => 'vsako uro', 18 | 'daily' => 'dnevno', 19 | 'weekly' => 'tedensko', 20 | 'monthly' => 'mesečno', 21 | 'yearly' => 'letno', 22 | 'never' => 'nikoli', 23 | 'editor_title' => 'Urejanje elementa zemljevida strani', 24 | 'type' => 'Vrsta', 25 | 'allow_nested_items' => 'Dovoli gnezdene elemente', 26 | 'allow_nested_items_comment' => 'Gnezdene elemente lahko dinamično ustvarijo statične strani in nekatere druge vrste elementov.', 27 | 'url' => 'URL', 28 | 'reference' => 'Referenca', 29 | 'title_required' => 'Naslov je obvezen', 30 | 'unknown_type' => 'Neznana vrsta elementa', 31 | 'unnamed' => 'Neimenovan element', 32 | 'add_item' => 'Dodaj element', 33 | 'new_item' => 'Nov element', 34 | 'cms_page' => 'CMS stran', 35 | 'cms_page_comment' => 'Izberite stran, ki jo želite uporabiti za URL naslov.', 36 | 'reference_required' => 'Referenca elementa je obvezna', 37 | 'url_required' => 'URL povezava je obvezna', 38 | 'cms_page_required' => 'Prosimo, izberite CMS stran', 39 | 'page' => 'Stran', 40 | 'check' => 'Preveri', 41 | 'definition' => 'Definicija', 42 | 'save_definition' => 'Shranjujem definicijo...', 43 | 'load_indicator' => 'Praznim definicijo...', 44 | 'empty_confirm' => 'Izpraznim to definicijo?', 45 | ], 46 | 'definition' => [ 47 | 'not_found' => 'Definicije zemljevida strani ni mogoče najti. Poskusite najprej eno ustvariti.', 48 | ], 49 | ]; 50 | -------------------------------------------------------------------------------- /controllers/Definitions.php: -------------------------------------------------------------------------------- 1 | addJs('/modules/backend/assets/js/winter.treeview.js', 'core'); 36 | $this->addJs('/plugins/winter/sitemap/assets/js/sitemap-definitions.js', 'Winter.Sitemap'); 37 | } 38 | 39 | /** 40 | * Index action. Find or create a new Definition model, 41 | * then redirect to the update form. 42 | */ 43 | public function index() 44 | { 45 | try { 46 | if (!$theme = Theme::getEditTheme()) { 47 | throw new ApplicationException('Unable to find the active theme.'); 48 | } 49 | 50 | return $this->redirectToThemeSitemap($theme); 51 | } 52 | catch (Exception $ex) { 53 | $this->handleError($ex); 54 | } 55 | } 56 | 57 | /** 58 | * Update action. Add the theme object to the page vars. 59 | */ 60 | public function update($recordId = null, $context = null) 61 | { 62 | try { 63 | if (!$editTheme = Theme::getEditTheme()) { 64 | throw new ApplicationException('Unable to find the active theme.'); 65 | } 66 | 67 | $result = $this->asExtension('FormController')->update($recordId, $context); 68 | 69 | $model = $this->formGetModel(); 70 | $theme = Theme::load($model->theme); 71 | 72 | /* 73 | * Not editing the active sitemap definition 74 | */ 75 | if ($editTheme->getDirName() != $theme->getDirName()) { 76 | return $this->redirectToThemeSitemap($editTheme); 77 | } 78 | 79 | $this->vars['theme'] = $theme; 80 | $this->vars['themeName'] = $theme->getConfigValue('name', $theme->getDirName()); 81 | $this->vars['sitemapUrl'] = Url::to('/sitemap.xml'); 82 | 83 | return $result; 84 | } 85 | catch (Exception $ex) { 86 | $this->handleError($ex); 87 | } 88 | } 89 | 90 | public function onGetItemTypeInfo(): array 91 | { 92 | $type = Request::input('type'); 93 | 94 | return [ 95 | 'sitemapItemTypeInfo' => SitemapItem::getTypeInfo($type) 96 | ]; 97 | } 98 | 99 | // 100 | // Helpers 101 | // 102 | 103 | protected function redirectToThemeSitemap($theme) 104 | { 105 | $model = Definition::firstOrCreate(['theme' => $theme->getDirName()]); 106 | $updateUrl = sprintf('winter/sitemap/definitions/update/%s', $model->getKey()); 107 | 108 | return Backend::redirect($updateUrl); 109 | } 110 | } 111 | -------------------------------------------------------------------------------- /formwidgets/SitemapItems.php: -------------------------------------------------------------------------------- 1 | prepareVars(); 41 | 42 | return $this->makePartial('sitemapitems'); 43 | } 44 | 45 | /** 46 | * Prepares the list data 47 | */ 48 | public function prepareVars() 49 | { 50 | $sitemapItem = new SitemapItem; 51 | 52 | $this->vars['itemProperties'] = json_encode($sitemapItem->fillable); 53 | $this->vars['items'] = $this->model->items; 54 | 55 | $emptyItem = new SitemapItem; 56 | $emptyItem->type = 'url'; 57 | $emptyItem->url = '/'; 58 | $emptyItem->changefreq = 'always'; 59 | $emptyItem->priority = '0.5'; 60 | 61 | $this->vars['emptyItem'] = $emptyItem; 62 | 63 | $widgetConfig = $this->makeConfig('$/winter/sitemap/classes/definitionitem/fields.yaml'); 64 | $widgetConfig->model = $sitemapItem; 65 | $widgetConfig->alias = $this->alias.'SitemapItem'; 66 | 67 | $this->vars['itemFormWidget'] = $this->makeWidget('Backend\Widgets\Form', $widgetConfig); 68 | } 69 | 70 | /** 71 | * {@inheritDoc} 72 | */ 73 | protected function loadAssets() 74 | { 75 | $this->addJs('js/sitemap-items-editor.js', 'core'); 76 | } 77 | 78 | // 79 | // Methods for the internal use 80 | // 81 | 82 | /** 83 | * Returns the item reference description. 84 | */ 85 | protected function getReferenceDescription(SitemapItem $item): string 86 | { 87 | if ($this->typeListCache === false) { 88 | $this->typeListCache = $item->getTypeOptions(); 89 | } 90 | 91 | if (!isset($this->typeInfoCache[$item->type])) { 92 | $this->typeInfoCache[$item->type] = SitemapItem::getTypeInfo($item->type); 93 | } 94 | 95 | if (isset($this->typeListCache[$item->type])) { 96 | $result = Lang::get($this->typeListCache[$item->type]); 97 | 98 | if ($item->type !== 'url') { 99 | if (isset($this->typeInfoCache[$item->type]['references'])) { 100 | $result .= ': '.$this->findReferenceName($item->reference, $this->typeInfoCache[$item->type]['references']); 101 | } 102 | } 103 | else { 104 | $result .= ': '.$item->url; 105 | } 106 | } 107 | else { 108 | $result = Lang::get('winter.sitemap::lang.item.unknown_type'); 109 | } 110 | 111 | return $result; 112 | } 113 | 114 | protected function findReferenceName($search, $typeOptionList) 115 | { 116 | $iterator = function ($optionList, $path) use ($search, &$iterator) 117 | { 118 | foreach ($optionList as $reference => $info) { 119 | if ($reference == $search) { 120 | $result = $this->getSitemapItemTitle($info); 121 | 122 | return strlen($path) ? $path.' / '.$result : $result; 123 | } 124 | 125 | if (is_array($info) && isset($info['items'])) { 126 | $result = $iterator($info['items'], $path.' / '.$this->getSitemapItemTitle($info)); 127 | 128 | if (strlen($result)) { 129 | return strlen($path) ? $path.' / '.$result : $result; 130 | } 131 | } 132 | } 133 | }; 134 | 135 | $result = $iterator($typeOptionList, null); 136 | if (!strlen($result)) { 137 | $result = Lang::get('winter.sitemap::lang.item.unnamed'); 138 | } 139 | 140 | $result = preg_replace('|^\s+\/|', '', $result); 141 | 142 | return $result; 143 | } 144 | 145 | protected function getSitemapItemTitle($itemInfo) 146 | { 147 | if (is_array($itemInfo)) { 148 | if (!array_key_exists('title', $itemInfo) || !strlen($itemInfo['title'])) { 149 | return Lang::get('winter.sitemap::lang.item.unnamed'); 150 | } 151 | 152 | return $itemInfo['title']; 153 | } 154 | 155 | return strlen($itemInfo) ? $itemInfo : Lang::get('winter.sitemap::lang.item.unnamed'); 156 | } 157 | } 158 | -------------------------------------------------------------------------------- /classes/DefinitionItem.php: -------------------------------------------------------------------------------- 1 | $value) { 88 | if (property_exists($obj, $name)) { 89 | $obj->$name = $value; 90 | } 91 | } 92 | 93 | $result[] = $obj; 94 | } 95 | 96 | return $result; 97 | } 98 | 99 | /** 100 | * Returns a list of registered item types 101 | */ 102 | public function getTypeOptions(): array 103 | { 104 | $result = ['url' => Lang::get('winter.sitemap::lang.item.url')]; 105 | $apiResult = Event::fire('pages.menuitem.listTypes'); 106 | 107 | if (is_array($apiResult)) { 108 | foreach ($apiResult as $typeList) { 109 | if (!is_array($typeList)) { 110 | continue; 111 | } 112 | 113 | foreach ($typeList as $typeCode => $typeName) { 114 | $result[$typeCode] = $typeName; 115 | } 116 | } 117 | } 118 | 119 | return $result; 120 | } 121 | 122 | public function getCmsPageOptions() 123 | { 124 | return []; // CMS Pages are loaded client-side 125 | } 126 | 127 | public function getReferenceOptions() 128 | { 129 | return []; // References are loaded client-side 130 | } 131 | 132 | public static function getTypeInfo(string $type): array 133 | { 134 | $result = []; 135 | $apiResult = Event::fire('pages.menuitem.getTypeInfo', [$type]); 136 | 137 | if (is_array($apiResult)) { 138 | foreach ($apiResult as $typeInfo) { 139 | if (!is_array($typeInfo)) { 140 | continue; 141 | } 142 | 143 | foreach ($typeInfo as $name => $value) { 144 | if ($name == 'cmsPages') { 145 | $cmsPages = []; 146 | 147 | foreach ($value as $page) { 148 | $baseName = $page->getBaseFileName(); 149 | $pos = strrpos($baseName, '/'); 150 | 151 | $dir = $pos !== false ? substr($baseName, 0, $pos).' / ' : null; 152 | $cmsPages[$baseName] = strlen($page->title) 153 | ? $dir.$page->title 154 | : $baseName; 155 | } 156 | 157 | $value = $cmsPages; 158 | } 159 | 160 | $result[$name] = $value; 161 | } 162 | } 163 | } 164 | 165 | return $result; 166 | } 167 | 168 | /** 169 | * Converts the item data to an array 170 | */ 171 | public function toArray(): array 172 | { 173 | $result = []; 174 | 175 | foreach ($this->fillable as $property) { 176 | $result[$property] = $this->$property; 177 | } 178 | 179 | return $result; 180 | } 181 | } 182 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Sitemap Plugin 2 | 3 | Dynamically generates `sitemap.xml` files in Winter CMS based user-configured settings. 4 | 5 | Supports: 6 | - Single sitemap definition per theme 7 | - Sitemaps are automatically styled for human consumption using the default `sitemap.xsl` file. 8 | - Supports item types as registered by [Winter.Pages](https://github.com/wintercms/wn-pages-plugin) and compatible plugins (Static URL, CMS Page, Single Record, All Records in type, etc) 9 | - Supports custom sitemap item types 10 | 11 | ## Installation 12 | 13 | This plugin is available for installation via [Composer](http://getcomposer.org/). 14 | 15 | ```bash 16 | composer require winter/wn-sitemap-plugin 17 | ``` 18 | 19 | After installing the plugin you will need to run the migrations and (if you are using a [public folder](https://wintercms.com/docs/develop/docs/setup/configuration#using-a-public-folder)) [republish your public directory](https://wintercms.com/docs/develop/docs/console/setup-maintenance#mirror-public-files). 20 | 21 | ```bash 22 | php artisan migrate 23 | ``` 24 | 25 | ## Viewing the sitemap 26 | 27 | Once this plugin is installed and the sitemap has been configured. The sitemap can be viewed by accessing the file relative to the website base path. For example, if the website is hosted at https://wintercms.com it can be viewed by opening this URL: 28 | 29 | https://wintercms.com/sitemap.xml 30 | 31 | ## Managing a sitemap definition 32 | 33 | The sitemap is managed by selecting Sitemap from the Settings area of the back-end. There is a single sitemap definition for each theme and it will be created automatically. 34 | 35 | A sitemap definition can contain multiple items and each item has a number of properties. There are common properties for all item types, and some properties depend on the item type. The common item properties are **Priority** and **Change frequency**. The Priority defines the priority of this item relative to other items in the sitemap. The Change frequency defines how frequently the page is likely to change. 36 | 37 | #### Standard item types 38 | The available item types depend on the installed plugins, but there are three basic item types that are supported out of the box. 39 | 40 | ###### URL {.subheader} 41 | Items of this type are links to a specific fixed URL. That could be an URL of an or internal page. Items of this type don't have any other properties - just the title and URL. 42 | 43 | ###### Static page {.subheader} 44 | Items of this type refer to static pages. The static page should be selected in the **Reference** drop-down list described below. 45 | 46 | ###### All static pages {.subheader} 47 | Items of this type expand to create links to all static pages defined in the theme. 48 | 49 | #### Custom item types 50 | Other plugins can supply new item types. For example, the [Blog plugin](https://github.com/wintercms/wn-blog-plugin) supplies two more types: 51 | 52 | ###### Blog category {.subheader} 53 | An item of this type represents a link to a specific blog category. The category should be selected in the **Reference** drop-down. This type also requires selecting a **CMS page** that outputs a blog category. 54 | 55 | ###### All blog categories {.subheader} 56 | An item of this time expands into multiple items representing all blog existing categories. This type also requires selecting a **CMS page**. 57 | 58 | #### Definition item properties 59 | Depending on the selected item time you might need to provide other properties of the item. The available properties are described below. 60 | 61 | ###### Reference {.subheader} 62 | A drop-down list of objects the item should refer to. The list content depends on the item type. For the **Static page** item type the list displays all static pages defined in the system. For the **Blog category** item type the list displays a list of blog categories. 63 | 64 | ###### Allow nested items {.subheader} 65 | This checkbox is available only for item types that suppose nested objects. For example, static pages are hierarchical, and this property is available for the **Static page** item type. On the other hand, blog categories are not hierarchical, and the checkbox is hidden. 66 | 67 | ###### CMS Page {.subheader} 68 | This drop-down is available for item types that require a special CMS page to refer to. For example, the **Blog category** item type requires a CMS page that hosts the `blogPosts` component. The CMS Page drop-down for this item type will only display pages that include this component. 69 | 70 | --- 71 | 72 | The Sitemap plugin works *out of the box* and does not require any direct development to operate. 73 | 74 | ##### Registering new sitemap definition item types 75 | 76 | The Sitemap plugin shares the same events for registering item types as the [Pages plugin](https://github.com/wintercms/wn-pages-plugin). See the documentation provided by this plugin for more information. 77 | 78 | A small addition is required when resolving items, via the following event: 79 | 80 | * `pages.menuitem.resolveItem` event handler "resolves" a menu item information and returns the actual item URL, title, an indicator whether the item is currently active, and subitems, if any. 81 | 82 | ##### Resolving items 83 | 84 | When resolving an item, each item should return an extra key in the array called `mtime`. This should be a Date object (see `Carbon\Carbon`) or a timestamp value compatible with PHP's `date()` function and represent the last time the link was modified. 85 | 86 | Expected result format: 87 | 88 | ``` 89 | Array ( 90 | [url] => https://example.com/blog/category/another-category 91 | [mtime] => Carbon::now(), 92 | [items] => Array ( 93 | [0] => Array ( 94 | [url] => https://example.com/blog/category/another-category 95 | [mtime] => Carbon::now(), 96 | ) 97 | 98 | [1] => Array ( 99 | [url] => https://example.com/blog/category/news 100 | [mtime] => Carbon::now(), 101 | ) 102 | ) 103 | ) 104 | ``` 105 | -------------------------------------------------------------------------------- /models/Definition.php: -------------------------------------------------------------------------------- 1 | data = (array) $this->items; 73 | } 74 | 75 | public function afterFetch() 76 | { 77 | $this->items = DefinitionItem::initFromArray($this->data); 78 | } 79 | 80 | /** 81 | * Generate the sitemap's XML from the definition data 82 | */ 83 | public function generateSitemap(): string 84 | { 85 | if (!$this->items) { 86 | return ''; 87 | } 88 | 89 | $currentUrl = Request::path(); 90 | $theme = Theme::load($this->theme); 91 | 92 | /* 93 | * Cycle each page and add its URL 94 | */ 95 | foreach ($this->items as $item) { 96 | 97 | /* 98 | * Explicit URL 99 | */ 100 | if ($item->type === 'url') { 101 | $this->addItemToSet($item); 102 | } 103 | /* 104 | * Registered sitemap type 105 | */ 106 | else { 107 | /** 108 | * @NOTE: 109 | * - This passes (string, DefinititionItem, string, Theme) 110 | * - Winter.Pages passes (string, MenuItem, string, Theme) 111 | */ 112 | $menuItemInfo = Event::fire('pages.menuitem.resolveItem', [$item->type, $item, $currentUrl, $theme], true); 113 | if (!is_array($menuItemInfo)) { 114 | continue; 115 | } 116 | 117 | /* 118 | * Single item 119 | */ 120 | if (isset($menuItemInfo['url'])) { 121 | $this->addItemToSet($item, $menuItemInfo); 122 | } 123 | 124 | /* 125 | * Multiple items 126 | */ 127 | if (isset($menuItemInfo['items'])) { 128 | $menuItemIterator = function ($menuItems) use (&$menuItemIterator, $item) { 129 | foreach ($menuItems as $menuItem) { 130 | if (isset($menuItem['url'])) { 131 | $this->addItemToSet($item, $menuItem); 132 | } 133 | 134 | if (isset($menuItem['items'])) { 135 | $menuItemIterator($menuItem['items']); 136 | } 137 | } 138 | }; 139 | 140 | $menuItemIterator($menuItemInfo['items']); 141 | } 142 | } 143 | } 144 | 145 | $urlSet = $this->getUrlSet(); 146 | $xml = $this->getXmlObject(); 147 | $xml->appendChild($urlSet); 148 | 149 | return $xml->saveXML(); 150 | } 151 | 152 | /** 153 | * Gets the DomDocument object, creating it if it doesn't exist 154 | */ 155 | protected function getXmlObject(): DOMDocument 156 | { 157 | if ($this->xmlObject !== null) { 158 | return $this->xmlObject; 159 | } 160 | 161 | $xml = new DOMDocument; 162 | $xml->encoding = 'UTF-8'; 163 | $xss = $xml->createProcessingInstruction('xml-stylesheet', 164 | 'type="text/xsl" href="' . Url::buildUrl(Url::current(), ['path' => 'sitemap.xsl']) . '"' 165 | ); 166 | $xml->appendChild($xss); 167 | 168 | return $this->xmlObject = $xml; 169 | } 170 | 171 | /** 172 | * Gets the urlset XML element, creating it if it doesn't exist 173 | */ 174 | protected function getUrlSet(): DOMElement 175 | { 176 | if ($this->urlSet !== null) { 177 | return $this->urlSet; 178 | } 179 | 180 | $xml = $this->getXmlObject(); 181 | $urlSet = $xml->createElement('urlset'); 182 | $urlSet->setAttribute('xmlns', 'http://www.sitemaps.org/schemas/sitemap/0.9'); 183 | $urlSet->setAttribute('xmlns:xhtml', 'http://www.w3.org/1999/xhtml'); 184 | $urlSet->setAttribute('xmlns:xsi', 'http://www.w3.org/2001/XMLSchema-instance'); 185 | $urlSet->setAttribute('xsi:schemaLocation', 'http://www.sitemaps.org/schemas/sitemap/0.9 http://www.sitemaps.org/schemas/sitemap/0.9/sitemap.xsd'); 186 | 187 | return $this->urlSet = $urlSet; 188 | } 189 | 190 | /** 191 | * Adds the provided item to the sitemap's urlset DOMElement 192 | */ 193 | protected function addItemToSet(DefinitionItem $item, ?array $itemInfo = null): void 194 | { 195 | $xml = $this->getXmlObject(); 196 | $urlSet = $this->getUrlSet(); 197 | 198 | if ($this->urlCount >= self::MAX_URLS) { 199 | return; 200 | } 201 | 202 | if (!isset($itemInfo['url'])) { 203 | $itemInfo['url'] = Url::to($item->url); 204 | } 205 | 206 | $lastModified = $itemInfo['mtime'] ?? null; 207 | if ($lastModified instanceof \DateTime) { 208 | $lastModified = $lastModified->getTimestamp(); 209 | } 210 | $lastModified = $lastModified ? date('c', $lastModified) : date('c'); 211 | $itemInfo['lastModified'] = $lastModified; 212 | 213 | /** 214 | * @event winter.sitemap.beforeAddItem 215 | * Provides an opportunity to prevent an element from being produced 216 | * 217 | * Example usage (stops the generation process): 218 | * 219 | * Event::listen('winter.sitemap.beforeAddItem', function (DefinitionItem $item, array $itemInfo, Definition $definition, DOMDocument $xml, DOMElement $urlSet) { 220 | * if ($itemInfo['url'] === '/ignore-this-specific-page') { 221 | * return false; 222 | * } 223 | * }); 224 | * 225 | */ 226 | if (Event::fire('winter.sitemap.beforeAddItem', [$item, $itemInfo, $this, $xml, $urlSet], true) === false) { 227 | return; 228 | } 229 | 230 | // Ensure that only items with valid absolute URLs are added to the generated sitemap. 231 | if (empty($itemInfo['url'])) { 232 | return; 233 | } else { 234 | $itemInfo['url'] = Url::to($itemInfo['url']); 235 | } 236 | 237 | $this->urlCount++; 238 | 239 | $urlElement = $xml->createElement('url'); 240 | 241 | $urlElement->appendChild($xml->createElement('loc', $itemInfo['url'])); 242 | $urlElement->appendChild($xml->createElement('lastmod', $itemInfo['lastModified'])); 243 | $urlElement->appendChild($xml->createElement('changefreq', $item->changefreq)); 244 | $urlElement->appendChild($xml->createElement('priority', $item->priority)); 245 | 246 | $urlSet->appendChild($urlElement); 247 | 248 | /** 249 | * @event winter.sitemap.addItem 250 | * Provides an opportunity to interact with a sitemap element after it has been generated. 251 | * 252 | * Example usage: 253 | * 254 | * Event::listen('winter.sitemap.addItem', function (DefinitionItem $item, array $itemInfo, Definition $definition, DOMDocument $xml, DOMElement $urlSet, DOMElement $itemElement) { 255 | * $urlElement->appendChild($xml->createElement('bestcmsever', 'WinterCMS'); 256 | * }); 257 | * 258 | */ 259 | Event::fire('winter.sitemap.addItem', [$item, $itemInfo, $this, $xml, $urlSet, $urlElement]); 260 | 261 | return; 262 | } 263 | } 264 | -------------------------------------------------------------------------------- /assets/sitemap.xsl: -------------------------------------------------------------------------------- 1 | 2 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | Sitemap 17 | <xsl:if test="sitemap:sitemapindex">Index</xsl:if> 18 | 19 | 20 | 21 | 22 |
    23 |
    24 |

    Sitemap

    25 | 26 | Index 27 | 28 | 29 | Images 30 | 31 | 32 | Video 33 | 34 | 35 | Xhtml 36 | 37 |
    38 |

    39 | 40 | 41 | This index contains 42 | 43 | sitemaps. 44 | 45 | 46 | This index contains 47 | 48 | URLs. 49 | 50 | 51 |

    52 |

    53 | This is an XML sitemap, meant for consumption by search engines. 54 | You can find more information about XML sitemaps on sitemaps.org. 55 |

    56 |
    57 | 58 | 59 | 60 |
    61 |
    62 | 63 | 64 | 65 |
    66 | 67 | 68 | 69 |
    70 |
    71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 91 | 96 | 97 | 100 | 101 | 102 | 103 | 104 | 105 |
    URLLast Modified
    89 | 90 | 92 | 93 | 94 | 95 | 98 | 99 |
    106 |
    107 |
    108 |
    109 | 110 | 111 |
    112 |
    113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 141 | 151 | 152 | 153 | 154 | 157 | 158 | 159 | 160 | 161 |
    URLChange Freq.PriorityLast Modified
    139 | 140 | 142 |

    143 | 144 | 145 | 146 |

    147 | 148 | 149 | 150 |
    155 | 156 |
    162 |
    163 |
    164 |
    165 | 166 | 167 | 168 | 169 | 170 | 171 | 172 | 173 | 174 | 175 | 176 | 177 | 178 | 179 |

    180 | 181 | 182 | 183 | 184 | 185 | 186 | 187 | 188 | 189 | 190 | 191 | 192 | 193 | 194 | 195 | 196 | 197 | 198 | 199 | 200 | 201 |

    202 | 203 |
    204 | 205 | 206 | 207 | 208 | 209 |

    210 | Image: 211 | 212 | 213 | 214 | 215 | 216 | 217 | 218 | 219 | 220 |

    221 |
    222 | 223 | 224 | 225 | 226 | 227 | 228 | 229 | 230 | 231 | 232 | 233 | 234 | 235 | 236 | 237 |

    238 | Video: 239 | 240 | 241 | 242 | 243 | 244 | 245 | 246 | 247 | 248 | 249 | 250 | thumb 251 | 252 | 253 | 254 | 255 | 256 | 257 | 258 |

    259 |
    260 | 261 |
    262 | -------------------------------------------------------------------------------- /formwidgets/sitemapitems/assets/js/sitemap-items-editor.js: -------------------------------------------------------------------------------- 1 | /* 2 | * The sitemap item editor. Provides tools for managing the 3 | * sitemap items. 4 | */ 5 | +function ($) { "use strict"; 6 | var SitemapItemsEditor = function (el, options) { 7 | this.$el = $(el) 8 | this.options = options 9 | 10 | this.init() 11 | } 12 | 13 | SitemapItemsEditor.prototype.init = function() { 14 | var self = this 15 | 16 | this.alias = this.$el.data('alias') 17 | this.$treevView = this.$el.find('div[data-control="treeview"]') 18 | 19 | this.typeInfo = {} 20 | 21 | // Sitemap item is clicked 22 | this.$el.on('open.oc.treeview', function(e) { 23 | return self.onItemClick(e.relatedTarget) 24 | }) 25 | 26 | // Sub item is clicked in the master tabs 27 | this.$el.on('submenu.oc.treeview', $.proxy(this.onSubItemClick, this)) 28 | 29 | this.$el.on('click', 'a[data-control="add-item"]', function(e) { 30 | self.onCreateItem(e.target) 31 | return false 32 | }) 33 | } 34 | 35 | /* 36 | * Triggered when a sub item is clicked in the editor. 37 | */ 38 | SitemapItemsEditor.prototype.onSubItemClick = function(e) { 39 | if ($(e.relatedTarget).data('control') == 'delete-sitemap-item') 40 | this.onDeleteItem(e.relatedTarget) 41 | 42 | return false 43 | } 44 | 45 | /* 46 | * Removes an item 47 | */ 48 | SitemapItemsEditor.prototype.onDeleteItem = function(link) { 49 | if (!confirm('Do you really want to delete this sitemap definition?')) 50 | return 51 | 52 | $(link).trigger('change') 53 | $(link).closest('li[data-sitemap-item]').remove() 54 | 55 | $(window).trigger('oc.updateUi') 56 | 57 | this.$treevView.treeView('update') 58 | this.$treevView.treeView('fixSubItems') 59 | } 60 | 61 | /* 62 | * Opens the item editor 63 | */ 64 | SitemapItemsEditor.prototype.onItemClick = function(item, newItemMode) { 65 | var $item = $(item), 66 | $container = $('> div', $item), 67 | self = this 68 | 69 | $container.one('show.oc.popup', function(e){ 70 | $(document).trigger('render') 71 | 72 | self.$popupContainer = $(e.relatedTarget) 73 | self.$itemDataContainer = $container.closest('li') 74 | 75 | $('input[type=checkbox]', self.$popupContainer).removeAttr('checked') 76 | 77 | self.loadProperties(self.$popupContainer, self.$itemDataContainer.data('sitemap-item')) 78 | self.$popupForm = self.$popupContainer.find('form') 79 | self.itemSaved = false 80 | 81 | $('select[name=type]', self.$popupContainer).change(function(){ 82 | self.loadTypeInfo(false, true) 83 | }) 84 | 85 | self.$popupContainer.on('keydown', function(e) { 86 | if (e.which == 13) 87 | self.applySitemapItem() 88 | }) 89 | 90 | $('button[data-control="apply-btn"]', self.$popupContainer).click($.proxy(self.applySitemapItem, self)) 91 | 92 | var $updateTypeOptionsBtn = $('') 93 | $('div[data-field-name=reference]').addClass('input-sidebar-control').append($updateTypeOptionsBtn) 94 | 95 | $updateTypeOptionsBtn.click(function(){ 96 | self.loadTypeInfo(true) 97 | 98 | return false 99 | }) 100 | 101 | $updateTypeOptionsBtn.keydown(function(ev){ 102 | if (ev.which == 13 || ev.which == 32) { 103 | self.loadTypeInfo(true) 104 | return false 105 | } 106 | }) 107 | 108 | var $updateCmsPagesBtn = $updateTypeOptionsBtn.clone(true) 109 | $('div[data-field-name=cmsPage]').addClass('input-sidebar-control').append($updateCmsPagesBtn) 110 | 111 | self.loadTypeInfo() 112 | }) 113 | 114 | $container.one('hide.oc.popup', function(e) { 115 | if (!self.itemSaved && newItemMode) 116 | $item.remove() 117 | 118 | self.$treevView.treeView('update') 119 | self.$treevView.treeView('fixSubItems') 120 | 121 | $container.removeClass('popover-highlight') 122 | }) 123 | 124 | $container.popup({ 125 | content: $('script[data-editor-template]', this.$el).html() 126 | }) 127 | 128 | /* 129 | * Highlight modal target 130 | */ 131 | $container.addClass('popover-highlight') 132 | $container.blur() 133 | 134 | return false 135 | } 136 | 137 | SitemapItemsEditor.prototype.loadProperties = function($popupContainer, properties) { 138 | this.properties = properties 139 | 140 | var self = this 141 | 142 | $.each(properties, function(property) { 143 | var $input = $('[name="'+property+'"]', $popupContainer).not('[type=hidden]') 144 | 145 | if ($input.prop('type') !== 'checkbox' ) { 146 | $input.val(this) 147 | $input.change() 148 | } else { 149 | var checked = !(this == '0' || this == 'false' || this == 0 || this == undefined || this == null) 150 | 151 | checked ? $input.prop('checked', 'checked') : $input.removeAttr('checked') 152 | } 153 | }) 154 | } 155 | 156 | SitemapItemsEditor.prototype.loadTypeInfo = function(force, focusList) { 157 | var type = $('select[name=type]', this.$popupContainer).val() 158 | 159 | var self = this 160 | 161 | if (!force && this.typeInfo[type] !== undefined) { 162 | self.applyTypeInfo(this.typeInfo[type], type, focusList) 163 | return 164 | } 165 | 166 | $.wn.stripeLoadIndicator.show() 167 | this.$popupForm.request('onGetItemTypeInfo') 168 | .always(function(){ 169 | $.wn.stripeLoadIndicator.hide() 170 | }) 171 | .done(function(data){ 172 | self.typeInfo[type] = data.sitemapItemTypeInfo 173 | self.applyTypeInfo(data.sitemapItemTypeInfo, type, focusList) 174 | }) 175 | } 176 | 177 | SitemapItemsEditor.prototype.applyTypeInfo = function(typeInfo, type, focusList) { 178 | var $referenceFormGroup = $('div[data-field-name="reference"]', this.$popupContainer), 179 | $optionSelector = $('select', $referenceFormGroup), 180 | $nestingFormGroup = $('div[data-field-name="nesting"]', this.$popupContainer), 181 | $urlFormGroup = $('div[data-field-name="url"]', this.$popupContainer), 182 | $cmsPageFormGroup = $('div[data-field-name="cmsPage"]', this.$popupContainer), 183 | $cmsPageSelector = $('select', $cmsPageFormGroup), 184 | prevSelectedReference = $optionSelector.val(), 185 | prevSelectedPage = $cmsPageSelector.val() 186 | 187 | if (typeInfo.references) { 188 | $optionSelector.find('option').remove() 189 | $referenceFormGroup.show() 190 | 191 | var iterator = function(options, level, path) { 192 | $.each(options, function(code) { 193 | var $option = $('').attr('value', code), 194 | offset = Array(level*4).join(' '), 195 | isObject = $.type(this) == 'object' 196 | 197 | $option.text(isObject ? this.title : this) 198 | 199 | var optionPath = path.length > 0 200 | ? (path + ' / ' + $option.text()) 201 | : $option.text() 202 | 203 | $option.data('path', optionPath) 204 | 205 | $option.html(offset + $option.html()) 206 | 207 | $optionSelector.append($option) 208 | 209 | if (isObject) 210 | iterator(this.items, level+1, optionPath) 211 | }) 212 | } 213 | 214 | iterator(typeInfo.references, 0, '') 215 | 216 | $optionSelector.val(prevSelectedReference ? prevSelectedReference : this.properties.reference) 217 | } 218 | else { 219 | $referenceFormGroup.hide() 220 | } 221 | 222 | if (typeInfo.cmsPages) { 223 | $cmsPageSelector.find('option').remove() 224 | $cmsPageFormGroup.show() 225 | 226 | $.each(typeInfo.cmsPages, function(code) { 227 | var $option = $('').attr('value', code) 228 | 229 | $option.text(this).val(code) 230 | $cmsPageSelector.append($option) 231 | }) 232 | 233 | $cmsPageSelector.val(prevSelectedPage ? prevSelectedPage : this.properties.cmsPage) 234 | } 235 | else { 236 | $cmsPageFormGroup.hide() 237 | } 238 | 239 | $nestingFormGroup.toggle(typeInfo.nesting !== undefined && typeInfo.nesting) 240 | $urlFormGroup.toggle(type == 'url') 241 | 242 | $(document).trigger('render') 243 | 244 | if (focusList) { 245 | var focusElements = [ 246 | $referenceFormGroup, 247 | $cmsPageFormGroup, 248 | $('div.custom-checkbox', $nestingFormGroup), 249 | $('input', $urlFormGroup) 250 | ] 251 | 252 | $.each(focusElements, function(){ 253 | if (this.is(':visible')) { 254 | var $self = this 255 | 256 | window.setTimeout(function() { 257 | if ($self.hasClass('dropdown-field')) 258 | $('select', $self).select2('focus', 100) 259 | else $self.focus() 260 | }) 261 | 262 | return false; 263 | } 264 | }) 265 | } 266 | } 267 | 268 | SitemapItemsEditor.prototype.applySitemapItem = function() { 269 | var self = this, 270 | data = {}, 271 | propertyNames = this.$el.data('item-properties'), 272 | basicProperties = { 273 | 'priority': 1, 274 | 'changefreq': 1, 275 | 'type': 1 276 | }, 277 | typeInfoPropertyMap = { 278 | reference: 'references', 279 | cmsPage: 'cmsPages' 280 | }, 281 | typeInfo = {}, 282 | validationErrorFound = false 283 | 284 | $.each(propertyNames, function() { 285 | var propertyName = this, 286 | $input = $('[name="'+propertyName+'"]', self.$popupContainer).not('[type=hidden]') 287 | 288 | if ($input.prop('type') !== 'checkbox') { 289 | data[propertyName] = $.trim($input.val()) 290 | 291 | if (propertyName == 'type') 292 | typeInfo = self.typeInfo[data.type] 293 | 294 | if (data[propertyName].length == 0) { 295 | var typeInfoProperty = typeInfoPropertyMap[propertyName] !== undefined ? typeInfoPropertyMap[propertyName] : propertyName 296 | 297 | if (typeInfo[typeInfoProperty] !== undefined) { 298 | 299 | $.wn.flashMsg({ 300 | class: 'error', 301 | text: self.$popupForm.attr('data-message-'+propertyName+'-required') 302 | }) 303 | 304 | if ($input.prop("tagName") == 'SELECT') 305 | $input.select2('focus') 306 | else 307 | $input.focus() 308 | 309 | validationErrorFound = true 310 | 311 | return false 312 | } 313 | } 314 | } else { 315 | data[propertyName] = $input.prop('checked') ? 1 : 0 316 | } 317 | }) 318 | 319 | if (validationErrorFound) 320 | return 321 | 322 | if (data.type !== 'url') { 323 | delete data['url'] 324 | 325 | $.each(data, function(property) { 326 | if (property == 'type') 327 | return 328 | 329 | var typeInfoProperty = typeInfoPropertyMap[property] !== undefined ? typeInfoPropertyMap[property] : property 330 | if ((typeInfo[typeInfoProperty] === undefined || typeInfo[typeInfoProperty] === false) 331 | && basicProperties[property] === undefined) 332 | delete data[property] 333 | }) 334 | } 335 | else { 336 | $.each(propertyNames, function(){ 337 | if (this != 'url' && basicProperties[this] === undefined) 338 | delete data[this] 339 | }) 340 | } 341 | 342 | if (data.type == 'url' && $.trim(data.url).length == 0) { 343 | $.wn.flashMsg({ 344 | class: 'error', 345 | text: self.$popupForm.data('messageUrlRequired') 346 | }) 347 | 348 | $('[name=url]', self.$popupContainer).focus() 349 | 350 | return 351 | } 352 | 353 | var referenceDescription = $.trim($('select[name=type] option:selected', self.$popupContainer).text()) 354 | 355 | if (data.type == 'url') { 356 | referenceDescription += ': ' + $('input[name=url]', self.$popupContainer).val() 357 | } 358 | else if (typeInfo.references) { 359 | referenceDescription += ': ' + $.trim($('select[name=reference] option:selected', self.$popupContainer).data('path')) 360 | } 361 | 362 | $('> div span.title', self.$itemDataContainer).text(referenceDescription) 363 | 364 | var changeFreqValue = $.trim($('select[name=changefreq] option:selected', self.$popupContainer).text()), 365 | changePriority = $.trim($('select[name=priority] option:selected', self.$popupContainer).text()) 366 | 367 | $('> div span.priority', self.$itemDataContainer).text(changePriority) 368 | $('> div span.changefreq', self.$itemDataContainer).text(changeFreqValue) 369 | 370 | this.$itemDataContainer.data('sitemap-item', data) 371 | this.itemSaved = true 372 | this.$popupContainer.trigger('close.oc.popup') 373 | this.$el.trigger('change') 374 | } 375 | 376 | SitemapItemsEditor.prototype.onCreateItem = function(target) { 377 | var parentList = $(target).closest('li[data-sitemap-item]').find(' > ol'), 378 | item = $($('script[data-item-template]', this.$el).html()) 379 | 380 | if (!parentList.length) 381 | parentList = $(target).closest('div[data-control=treeview]').find(' > ol') 382 | 383 | parentList.append(item) 384 | this.$treevView.treeView('update') 385 | $(window).trigger('oc.updateUi') 386 | 387 | this.onItemClick(item, true) 388 | } 389 | 390 | SitemapItemsEditor.DEFAULTS = { 391 | } 392 | 393 | // MENUITEMSEDITOR PLUGIN DEFINITION 394 | // ============================ 395 | 396 | var old = $.fn.sitemapItemsEditor 397 | 398 | $.fn.sitemapItemsEditor = function (option) { 399 | var args = Array.prototype.slice.call(arguments, 1) 400 | return this.each(function () { 401 | var $this = $(this) 402 | var data = $this.data('oc.sitemapitemseditor') 403 | var options = $.extend({}, SitemapItemsEditor.DEFAULTS, $this.data(), typeof option == 'object' && option) 404 | if (!data) $this.data('oc.sitemapitemseditor', (data = new SitemapItemsEditor(this, options))) 405 | else if (typeof option == 'string') data[option].apply(data, args) 406 | }) 407 | } 408 | 409 | $.fn.sitemapItemsEditor.Constructor = SitemapItemsEditor 410 | 411 | // MENUITEMSEDITOR NO CONFLICT 412 | // ================= 413 | 414 | $.fn.sitemapItemsEditor.noConflict = function () { 415 | $.fn.sitemapItemsEditor = old 416 | return this 417 | } 418 | 419 | // MENUITEMSEDITOR DATA-API 420 | // =============== 421 | 422 | $(document).on('render', function() { 423 | $('[data-control="sitemap-item-editor"]').sitemapItemsEditor() 424 | }); 425 | }(window.jQuery); --------------------------------------------------------------------------------