├── .gitignore ├── README.md ├── assets ├── .DS_Store ├── css │ └── admin.css ├── js │ └── admin.js └── vendor │ ├── .DS_Store │ └── fSelect │ ├── LICENSE.md │ ├── README.md │ ├── fSelect.css │ ├── fSelect.js │ └── test.html ├── readme.txt ├── sitemap-ui.php └── templates └── page-settings.php /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | .svn/ 3 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## Sitemap UI for WordPress 5.5+ 2 | 3 | WordPress 5.5 includes built-in [sitemap functionality](https://make.wordpress.org/core/2020/07/22/new-xml-sitemaps-functionality-in-wordpress-5-5/). 4 | 5 | This plugin provides a UI for some of the most common configuration settings. 6 | 7 | **NOTE:** some plugins (like Yoast SEO) replace core sitemaps with their own version. This plugin only works with core sitemaps. 8 | 9 | 10 | 11 | ### Features 12 | 13 | * Turn off WP core sitemaps entirely 14 | * Exclude all (or specific) post types 15 | * Exclude all (or specific) taxonomies 16 | * Exclude taxonomy terms by ID 17 | * Exclude post items by ID 18 | * Exclude all users 19 | -------------------------------------------------------------------------------- /assets/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mgibbs189/sitemap-ui/acf2c6334ec5df454e6b40a7b36b4b415258c546/assets/.DS_Store -------------------------------------------------------------------------------- /assets/css/admin.css: -------------------------------------------------------------------------------- 1 | .exclude-ids { 2 | width: 500px; 3 | } 4 | 5 | .fs-wrap, .fs-dropdown { 6 | width: 500px; 7 | } 8 | 9 | .hidden { 10 | display: none; 11 | } 12 | -------------------------------------------------------------------------------- /assets/js/admin.js: -------------------------------------------------------------------------------- 1 | (function($) { 2 | $(function() { 3 | 4 | // Set data 5 | $('.global-opt.opt-all').prop('checked', isset(SMUI.objects) && isset(SMUI.objects.all)); 6 | $('.global-opt.opt-post-types').prop('checked', isset(SMUI.objects) && isset(SMUI.objects.post_types)); 7 | $('.global-opt.opt-taxonomies').prop('checked', isset(SMUI.objects) && isset(SMUI.objects.taxonomies)); 8 | $('.global-opt.opt-users').prop('checked', isset(SMUI.objects) && isset(SMUI.objects.users)); 9 | $('.exclude-post-types').val(isset(SMUI.post_types) ? SMUI.post_types : []); 10 | $('.exclude-post-ids').val(isset(SMUI.post_ids) ? SMUI.post_ids : ''); 11 | $('.exclude-taxonomies').val(isset(SMUI.taxonomies) ? SMUI.taxonomies : []); 12 | $('.exclude-term-ids').val(isset(SMUI.term_ids) ? SMUI.term_ids : ''); 13 | 14 | // Setup fSelect 15 | $('.exclude-post-types').fSelect({ 16 | placeholder: 'Select post types to exclude', 17 | numDisplayed: 10 18 | }); 19 | $('.exclude-taxonomies').fSelect({ 20 | placeholder: 'Select taxonomies to exclude', 21 | numDisplayed: 10 22 | }); 23 | 24 | // Fire toggle handler 25 | $(document).on('change', '.global-opt', function() { 26 | toggleViz(); 27 | }); 28 | 29 | toggleViz(); 30 | }); 31 | 32 | function isset(val) { 33 | return 'undefined' !== typeof val; 34 | } 35 | 36 | function toggleViz() { 37 | var exclude_all = $('.global-opt.opt-all').is(':checked'); 38 | var exclude_pt = $('.global-opt.opt-post-types').is(':checked'); 39 | var exclude_tax = $('.global-opt.opt-taxonomies').is(':checked'); 40 | 41 | $('.wrap-post-types').toggleClass('hidden', exclude_all || exclude_pt); 42 | $('.wrap-taxonomies').toggleClass('hidden', exclude_all || exclude_tax); 43 | $('.global-opt.opt-post-types').closest('div').toggleClass('hidden', exclude_all); 44 | $('.global-opt.opt-taxonomies').closest('div').toggleClass('hidden', exclude_all); 45 | $('.global-opt.opt-users').closest('div').toggleClass('hidden', exclude_all); 46 | } 47 | })(jQuery); 48 | -------------------------------------------------------------------------------- /assets/vendor/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mgibbs189/sitemap-ui/acf2c6334ec5df454e6b40a7b36b4b415258c546/assets/vendor/.DS_Store -------------------------------------------------------------------------------- /assets/vendor/fSelect/LICENSE.md: -------------------------------------------------------------------------------- 1 | Copyright 2019 FacetWP, LLC 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 4 | 5 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 6 | 7 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 8 | -------------------------------------------------------------------------------- /assets/vendor/fSelect/README.md: -------------------------------------------------------------------------------- 1 | # fSelect 2 | A jQuery select box replacement library ([live demo](https://facetwp.com/wp-content/plugins/facetwp/assets/vendor/fSelect/test.html)) 3 | 4 | 5 | 6 | ### Usage 7 | 8 | ```javascript 9 | $('.your-select').fSelect(); 10 | ``` 11 | 12 | ### Available options 13 | 14 | ```js 15 | $('.your-select').fSelect({ 16 | placeholder: 'Select some options', 17 | numDisplayed: 3, 18 | overflowText: '{n} selected', 19 | noResultsText: 'No results found', 20 | searchText: 'Search', 21 | showSearch: true 22 | }); 23 | ``` 24 | 25 | * **placeholder** (str) - the default placeholder text 26 | * **numDisplayed** (int) - the number of values to show before switching to the `overflowText` 27 | * **overflowText** (str) - the text to show after exceeding the `numDisplayed` limit 28 | * **noResultsText** (str) - the text to show if no choices exist (or an empty string) 29 | * **searchText** (str) - the search box placeholder text 30 | * **showSearch** (bool) - show the search box? 31 | 32 | ### Methods 33 | 34 | ```js 35 | $('.your-select').fSelect('reload'); 36 | $('.your-select').fSelect('destroy'); 37 | ``` 38 | 39 | ### Single vs. multi-select 40 | 41 | Add the `multiple` attribute to your ` 45 | ``` 46 | -------------------------------------------------------------------------------- /assets/vendor/fSelect/fSelect.css: -------------------------------------------------------------------------------- 1 | .fs-wrap { 2 | display: inline-block; 3 | cursor: pointer; 4 | line-height: 1; 5 | width: 200px; 6 | } 7 | 8 | .fs-label-wrap { 9 | position: relative; 10 | background-color: #fff; 11 | border: 1px solid #ddd; 12 | cursor: default; 13 | } 14 | 15 | .fs-label-wrap, 16 | .fs-dropdown { 17 | -webkit-user-select: none; 18 | -moz-user-select: none; 19 | -ms-user-select: none; 20 | user-select: none; 21 | } 22 | 23 | .fs-label-wrap .fs-label { 24 | padding: 6px 22px 6px 8px; 25 | text-overflow: ellipsis; 26 | white-space: nowrap; 27 | overflow: hidden; 28 | } 29 | 30 | .fs-arrow { 31 | width: 0; 32 | height: 0; 33 | border-left: 5px solid transparent; 34 | border-right: 5px solid transparent; 35 | border-top: 5px solid #333; 36 | position: absolute; 37 | top: 0; 38 | right: 5px; 39 | bottom: 0; 40 | margin: auto; 41 | transition: ease-in 0.15s; 42 | } 43 | 44 | .fs-open .fs-arrow { 45 | transform: rotate(-180deg); 46 | } 47 | 48 | .fs-dropdown { 49 | position: absolute; 50 | background-color: #fff; 51 | border: 1px solid #ddd; 52 | width: 200px; 53 | margin-top: 5px; 54 | z-index: 1000; 55 | } 56 | 57 | .fs-dropdown .fs-options { 58 | max-height: 200px; 59 | overflow: auto; 60 | } 61 | 62 | .fs-search input { 63 | border: none !important; 64 | box-shadow: none !important; 65 | outline: none; 66 | padding: 6px 0; 67 | width: 100%; 68 | } 69 | 70 | .fs-option, 71 | .fs-search, 72 | .fs-optgroup-label { 73 | padding: 6px 8px; 74 | border-bottom: 1px solid #eee; 75 | cursor: default; 76 | } 77 | 78 | .fs-option:last-child { 79 | border-bottom: none; 80 | } 81 | 82 | .fs-search { 83 | padding: 0 8px; 84 | } 85 | 86 | .fs-no-results { 87 | padding: 6px 8px; 88 | } 89 | 90 | .fs-option { 91 | cursor: pointer; 92 | word-break: break-all; 93 | } 94 | 95 | .fs-option.disabled { 96 | opacity: 0.4; 97 | cursor: default; 98 | } 99 | 100 | .fs-option.hl { 101 | background-color: #f5f5f5; 102 | } 103 | 104 | .fs-wrap.multiple .fs-option { 105 | position: relative; 106 | padding-left: 30px; 107 | } 108 | 109 | .fs-wrap.multiple .fs-checkbox { 110 | position: absolute; 111 | display: block; 112 | width: 30px; 113 | top: 0; 114 | left: 0; 115 | bottom: 0; 116 | } 117 | 118 | .fs-wrap.multiple .fs-option .fs-checkbox i { 119 | position: absolute; 120 | margin: auto; 121 | left: 0; 122 | right: 0; 123 | top: 0; 124 | bottom: 0; 125 | width: 14px; 126 | height: 14px; 127 | border: 1px solid #aeaeae; 128 | border-radius: 2px; 129 | background-color: #fff; 130 | } 131 | 132 | .fs-wrap.multiple .fs-option.selected .fs-checkbox i { 133 | background-color: rgb(17, 169, 17); 134 | border-color: transparent; 135 | background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAgAAAAGCAYAAAD+Bd/7AAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAALEgAACxIB0t1+/AAAABx0RVh0U29mdHdhcmUAQWRvYmUgRmlyZXdvcmtzIENTNXG14zYAAABMSURBVAiZfc0xDkAAFIPhd2Kr1WRjcAExuIgzGUTIZ/AkImjSofnbNBAfHvzAHjOKNzhiQ42IDFXCDivaaxAJd0xYshT3QqBxqnxeHvhunpu23xnmAAAAAElFTkSuQmCC'); 136 | background-repeat: no-repeat; 137 | background-position: center; 138 | } 139 | 140 | .fs-optgroup-label { 141 | font-weight: bold; 142 | text-align: center; 143 | background-color: #f8f8f8; 144 | } 145 | 146 | .hidden { 147 | display: none; 148 | } 149 | -------------------------------------------------------------------------------- /assets/vendor/fSelect/fSelect.js: -------------------------------------------------------------------------------- 1 | /* fSelect 1.0.1 - https://github.com/mgibbs189/fselect */ 2 | 3 | (function($) { 4 | 5 | String.prototype.unaccented = function() { 6 | var accent = [ 7 | /[\300-\306]/g, /[\340-\346]/g, // A, a 8 | /[\310-\313]/g, /[\350-\353]/g, // E, e 9 | /[\314-\317]/g, /[\354-\357]/g, // I, i 10 | /[\322-\330]/g, /[\362-\370]/g, // O, o 11 | /[\331-\334]/g, /[\371-\374]/g, // U, u 12 | /[\321]/g, /[\361]/g, // N, n 13 | /[\307]/g, /[\347]/g, // C, c 14 | ]; 15 | var noaccent = ['A','a','E','e','I','i','O','o','U','u','N','n','C','c']; 16 | 17 | var str = this; 18 | for (var i = 0; i < accent.length; i++) { 19 | str = str.replace(accent[i], noaccent[i]); 20 | } 21 | 22 | return str; 23 | } 24 | 25 | $.fn.fSelect = function(options) { 26 | 27 | if ('string' === typeof options) { 28 | var settings = options; 29 | } 30 | else { 31 | var settings = $.extend({ 32 | placeholder: 'Select some options', 33 | numDisplayed: 3, 34 | overflowText: '{n} selected', 35 | searchText: 'Search', 36 | noResultsText: 'No results found', 37 | showSearch: true, 38 | optionFormatter: false 39 | }, options); 40 | } 41 | 42 | 43 | /** 44 | * Constructor 45 | */ 46 | function fSelect(select, settings) { 47 | this.$select = $(select); 48 | this.settings = settings; 49 | this.create(); 50 | } 51 | 52 | 53 | /** 54 | * Prototype class 55 | */ 56 | fSelect.prototype = { 57 | create: function() { 58 | this.idx = 0; 59 | this.optgroup = 0; 60 | this.selected = [].concat(this.$select.val()); // force an array 61 | this.settings.multiple = this.$select.is('[multiple]'); 62 | 63 | var search_html = ''; 64 | var no_results_html = ''; 65 | var choices_html = this.buildOptions(this.$select); 66 | 67 | if (this.settings.showSearch) { 68 | search_html = ''; 69 | } 70 | if ('' !== this.settings.noResultsText) { 71 | no_results_html = ''; 72 | } 73 | 74 | var html = '
'; 75 | html += ''; 76 | html = html.replace('{search}', search_html); 77 | html = html.replace('{no-results}', no_results_html); 78 | 79 | this.$select.wrap('
'); 80 | this.$select.addClass('hidden'); 81 | this.$select.before(html); 82 | this.$wrap = this.$select.closest('.fs-wrap'); 83 | this.$wrap.data('id', window.fSelect.num_items); 84 | window.fSelect.num_items++; 85 | 86 | this.reloadDropdownLabel(); 87 | }, 88 | 89 | reload: function() { 90 | this.destroy(); 91 | this.create(); 92 | }, 93 | 94 | destroy: function() { 95 | this.$wrap.find('.fs-label-wrap').remove(); 96 | this.$wrap.find('.fs-dropdown').remove(); 97 | this.$select.unwrap().removeClass('hidden'); 98 | }, 99 | 100 | buildOptions: function($element) { 101 | var $this = this; 102 | 103 | var choices = ''; 104 | $element.children().each(function(i, el) { 105 | var $el = $(el); 106 | 107 | if ('optgroup' == $el.prop('nodeName').toLowerCase()) { 108 | choices += '
' + $el.prop('label') + '
'; 109 | choices += $this.buildOptions($el); 110 | $this.optgroup++; 111 | } 112 | else { 113 | var val = $el.prop('value'); 114 | var classes = $el.attr('class'); 115 | classes = ('undefined' !== typeof classes) ? ' ' + classes : ''; 116 | 117 | // exclude the first option in multi-select mode 118 | if (0 < $this.idx || '' != val || ! $this.settings.multiple) { 119 | var disabled = $el.is(':disabled') ? ' disabled' : ''; 120 | var selected = -1 < $.inArray(val, $this.selected) ? ' selected' : ''; 121 | var group = ' g' + $this.optgroup; 122 | var row = '
' + $el.html() + '
'; 123 | 124 | if ('function' === typeof $this.settings.optionFormatter) { 125 | row = $this.settings.optionFormatter(row); 126 | } 127 | 128 | choices += row; 129 | $this.idx++; 130 | } 131 | } 132 | }); 133 | 134 | return choices; 135 | }, 136 | 137 | reloadDropdownLabel: function() { 138 | var settings = this.settings; 139 | var labelText = []; 140 | 141 | this.$wrap.find('.fs-option.selected').each(function(i, el) { 142 | labelText.push($(el).find('.fs-option-label').html()); 143 | }); 144 | 145 | if (labelText.length < 1) { 146 | labelText = settings.placeholder; 147 | } 148 | else if (labelText.length > settings.numDisplayed) { 149 | labelText = settings.overflowText.replace('{n}', labelText.length); 150 | } 151 | else { 152 | labelText = labelText.join(', '); 153 | } 154 | 155 | this.$wrap.find('.fs-label').html(labelText); 156 | this.$wrap.toggleClass('fs-default', labelText === settings.placeholder); 157 | } 158 | } 159 | 160 | 161 | /** 162 | * Loop through each matching element 163 | */ 164 | return this.each(function() { 165 | var data = $(this).data('fSelect'); 166 | 167 | if (!data) { 168 | data = new fSelect(this, settings); 169 | $(this).data('fSelect', data); 170 | } 171 | 172 | if ('string' === typeof settings) { 173 | data[settings](); 174 | } 175 | }); 176 | } 177 | 178 | 179 | /** 180 | * Events 181 | */ 182 | window.fSelect = { 183 | 'num_items': 0, 184 | 'active_id': null, 185 | 'active_el': null, 186 | 'last_choice': null, 187 | 'idx': -1 188 | }; 189 | 190 | $(document).on('click', '.fs-option:not(.hidden, .disabled)', function(e) { 191 | var $wrap = $(this).closest('.fs-wrap'); 192 | var $select = $wrap.find('select'); 193 | var do_close = false; 194 | 195 | // prevent selections 196 | if ($wrap.hasClass('fs-disabled')) { 197 | return; 198 | } 199 | 200 | if ($wrap.hasClass('multiple')) { 201 | var selected = []; 202 | 203 | // shift + click support 204 | if (e.shiftKey && null != window.fSelect.last_choice) { 205 | var current_choice = parseInt($(this).attr('data-index')); 206 | var addOrRemove = ! $(this).hasClass('selected'); 207 | var min = Math.min(window.fSelect.last_choice, current_choice); 208 | var max = Math.max(window.fSelect.last_choice, current_choice); 209 | 210 | for (i = min; i <= max; i++) { 211 | $wrap.find('.fs-option[data-index='+ i +']') 212 | .not('.hidden, .disabled') 213 | .each(function() { 214 | $(this).toggleClass('selected', addOrRemove); 215 | }); 216 | } 217 | } 218 | else { 219 | window.fSelect.last_choice = parseInt($(this).attr('data-index')); 220 | $(this).toggleClass('selected'); 221 | } 222 | 223 | $wrap.find('.fs-option.selected').each(function(i, el) { 224 | selected.push($(el).attr('data-value')); 225 | }); 226 | } 227 | else { 228 | var selected = $(this).attr('data-value'); 229 | $wrap.find('.fs-option').removeClass('selected'); 230 | $(this).addClass('selected'); 231 | do_close = true; 232 | } 233 | 234 | $select.val(selected); 235 | $select.fSelect('reloadDropdownLabel'); 236 | $select.change(); 237 | 238 | // fire an event 239 | $(document).trigger('fs:changed', $wrap); 240 | 241 | if (do_close) { 242 | closeDropdown($wrap); 243 | } 244 | }); 245 | 246 | $(document).on('keyup', '.fs-search input', function(e) { 247 | if (40 == e.which) { // down 248 | $(this).blur(); 249 | return; 250 | } 251 | 252 | var $wrap = $(this).closest('.fs-wrap'); 253 | var matchOperators = /[|\\{}()[\]^$+*?.]/g; 254 | var keywords = $(this).val().replace(matchOperators, '\\$&'); 255 | 256 | $wrap.find('.fs-option, .fs-optgroup-label').removeClass('hidden'); 257 | 258 | if ('' != keywords) { 259 | $wrap.find('.fs-option').each(function() { 260 | var regex = new RegExp(keywords.unaccented(), 'gi'); 261 | var formatedValue = $(this).find('.fs-option-label').text().unaccented(); 262 | 263 | if (null === formatedValue.match(regex)) { 264 | $(this).addClass('hidden'); 265 | } 266 | }); 267 | 268 | $wrap.find('.fs-optgroup-label').each(function() { 269 | var group = $(this).attr('data-group'); 270 | var num_visible = $(this).closest('.fs-options').find('.fs-option.g' + group + ':not(.hidden)').length; 271 | if (num_visible < 1) { 272 | $(this).addClass('hidden'); 273 | } 274 | }); 275 | } 276 | 277 | setIndexes($wrap); 278 | checkNoResults($wrap); 279 | }); 280 | 281 | $(document).on('click', function(e) { 282 | var $el = $(e.target); 283 | var $wrap = $el.closest('.fs-wrap'); 284 | 285 | if (0 < $wrap.length) { 286 | 287 | // user clicked another fSelect box 288 | if ($wrap.data('id') !== window.fSelect.active_id) { 289 | closeDropdown(); 290 | } 291 | 292 | // fSelect box was toggled 293 | if ($el.hasClass('fs-label') || $el.hasClass('fs-arrow')) { 294 | var is_hidden = $wrap.find('.fs-dropdown').hasClass('hidden'); 295 | 296 | if (is_hidden) { 297 | openDropdown($wrap); 298 | } 299 | else { 300 | closeDropdown($wrap); 301 | } 302 | } 303 | } 304 | // clicked outside, close all fSelect boxes 305 | else { 306 | closeDropdown(); 307 | } 308 | }); 309 | 310 | $(document).on('keydown', function(e) { 311 | var $wrap = window.fSelect.active_el; 312 | var $target = $(e.target); 313 | 314 | // toggle the dropdown on space 315 | if ($target.hasClass('fs-wrap')) { 316 | if (32 == e.which || 13 == e.which) { 317 | e.preventDefault(); 318 | $target.find('.fs-label').trigger('click'); 319 | return; 320 | } 321 | } 322 | // preserve spaces during search 323 | else if (0 < $target.closest('.fs-search').length) { 324 | if (32 == e.which) { 325 | return; 326 | } 327 | } 328 | else if (null === $wrap) { 329 | return; 330 | } 331 | 332 | if (38 == e.which) { // up 333 | e.preventDefault(); 334 | 335 | $wrap.find('.fs-option.hl').removeClass('hl'); 336 | 337 | var $current = $wrap.find('.fs-option[data-index=' + window.fSelect.idx + ']'); 338 | var $prev = $current.prevAll('.fs-option:not(.hidden, .disabled)'); 339 | 340 | if ($prev.length > 0) { 341 | window.fSelect.idx = parseInt($prev.attr('data-index')); 342 | $wrap.find('.fs-option[data-index=' + window.fSelect.idx + ']').addClass('hl'); 343 | setScroll($wrap); 344 | } 345 | else { 346 | window.fSelect.idx = -1; 347 | $wrap.find('.fs-search input').focus(); 348 | } 349 | } 350 | else if (40 == e.which) { // down 351 | e.preventDefault(); 352 | 353 | var $current = $wrap.find('.fs-option[data-index=' + window.fSelect.idx + ']'); 354 | if ($current.length < 1) { 355 | var $next = $wrap.find('.fs-option:not(.hidden, .disabled):first'); 356 | } 357 | else { 358 | var $next = $current.nextAll('.fs-option:not(.hidden, .disabled)'); 359 | } 360 | 361 | if ($next.length > 0) { 362 | window.fSelect.idx = parseInt($next.attr('data-index')); 363 | $wrap.find('.fs-option.hl').removeClass('hl'); 364 | $wrap.find('.fs-option[data-index=' + window.fSelect.idx + ']').addClass('hl'); 365 | setScroll($wrap); 366 | } 367 | } 368 | else if (32 == e.which || 13 == e.which) { // space, enter 369 | e.preventDefault(); 370 | 371 | $wrap.find('.fs-option.hl').click(); 372 | } 373 | else if (27 == e.which) { // esc 374 | closeDropdown($wrap); 375 | } 376 | }); 377 | 378 | function checkNoResults($wrap) { 379 | var addOrRemove = $wrap.find('.fs-option:not(.hidden)').length > 0; 380 | $wrap.find('.fs-no-results').toggleClass('hidden', addOrRemove); 381 | } 382 | 383 | function setIndexes($wrap) { 384 | $wrap.find('.fs-option.hl').removeClass('hl'); 385 | $wrap.find('.fs-search input').focus(); 386 | window.fSelect.idx = -1; 387 | } 388 | 389 | function setScroll($wrap) { 390 | var $container = $wrap.find('.fs-options'); 391 | var $selected = $wrap.find('.fs-option.hl'); 392 | 393 | var itemMin = $selected.offset().top + $container.scrollTop(); 394 | var itemMax = itemMin + $selected.outerHeight(); 395 | var containerMin = $container.offset().top + $container.scrollTop(); 396 | var containerMax = containerMin + $container.outerHeight(); 397 | 398 | if (itemMax > containerMax) { // scroll down 399 | var to = $container.scrollTop() + itemMax - containerMax; 400 | $container.scrollTop(to); 401 | } 402 | else if (itemMin < containerMin) { // scroll up 403 | var to = $container.scrollTop() - containerMin - itemMin; 404 | $container.scrollTop(to); 405 | } 406 | } 407 | 408 | function openDropdown($wrap) { 409 | window.fSelect.active_el = $wrap; 410 | window.fSelect.active_id = $wrap.data('id'); 411 | window.fSelect.initial_values = $wrap.find('select').val(); 412 | $(document).trigger('fs:opened', $wrap); 413 | $wrap.find('.fs-dropdown').removeClass('hidden'); 414 | $wrap.addClass('fs-open'); 415 | setIndexes($wrap); 416 | checkNoResults($wrap); 417 | } 418 | 419 | function closeDropdown($wrap) { 420 | if ('undefined' == typeof $wrap && null != window.fSelect.active_el) { 421 | $wrap = window.fSelect.active_el; 422 | } 423 | if ('undefined' !== typeof $wrap) { 424 | // only trigger if the values have changed 425 | var initial_values = window.fSelect.initial_values; 426 | var current_values = $wrap.find('select').val(); 427 | if (JSON.stringify(initial_values) != JSON.stringify(current_values)) { 428 | $(document).trigger('fs:closed', $wrap); 429 | } 430 | } 431 | 432 | $('.fs-wrap').removeClass('fs-open'); 433 | $('.fs-dropdown').addClass('hidden'); 434 | window.fSelect.active_el = null; 435 | window.fSelect.active_id = null; 436 | window.fSelect.last_choice = null; 437 | } 438 | 439 | })(jQuery); 440 | -------------------------------------------------------------------------------- /assets/vendor/fSelect/test.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | fSelect Test 4 | 5 | 6 | 7 | 14 | 15 | 16 | 31 | 32 | 33 | -------------------------------------------------------------------------------- /readme.txt: -------------------------------------------------------------------------------- 1 | === Sitemap UI === 2 | Contributors: mgibbs189 3 | Tags: sitemap, sitemaps, ui, settings, configuration 4 | Requires at least: 5.5 5 | Tested up to: 5.5 6 | Stable tag: trunk 7 | License: GPLv2 8 | 9 | Sitemap UI for WordPress 5.5+ 10 | 11 | == Description == 12 | 13 | WordPress 5.5 includes built-in [sitemap functionality](https://make.wordpress.org/core/2020/07/22/new-xml-sitemaps-functionality-in-wordpress-5-5/). 14 | 15 | This plugin provides a UI for some of the most common sitemap configuration settings. 16 | 17 | NOTE: some plugins (like Yoast SEO) replace core sitemaps with their own version. This plugin only works with core sitemaps. 18 | 19 | = Screenshots = 20 | * [Settings screen](https://i.imgur.com/KkDLTvQ.png) 21 | 22 | = Features = 23 | * Turn off WP core sitemaps entirely 24 | * Exclude all (or specific) post types 25 | * Exclude all (or specific) taxonomies 26 | * Exclude taxonomy terms by ID 27 | * Exclude post items by ID 28 | * Exclude all users 29 | 30 | = Important Links = 31 | * [Github →](https://github.com/mgibbs189/sitemap-ui) 32 | 33 | == Installation == 34 | 35 | 1. Download and activate the plugin. 36 | 2. Browse to `Settings > Sitemap UI` to configure. 37 | 38 | == Changelog == 39 | 40 | = 1.2 = 41 | * Added daily cron to ping Google of sitemap updates 42 | 43 | = 1.0 = 44 | * Initial release 45 | -------------------------------------------------------------------------------- /sitemap-ui.php: -------------------------------------------------------------------------------- 1 | . 23 | */ 24 | 25 | defined( 'ABSPATH' ) or exit; 26 | 27 | class SMUI_Plugin 28 | { 29 | 30 | public $settings; 31 | private static $instance; 32 | 33 | function __construct() { 34 | 35 | // setup variables 36 | define( 'SMUI_VERSION', '1.2' ); 37 | define( 'SMUI_DIR', dirname( __FILE__ ) ); 38 | define( 'SMUI_URL', plugins_url( '', __FILE__ ) ); 39 | define( 'SMUI_BASENAME', plugin_basename( __FILE__ ) ); 40 | 41 | add_action( 'init', [ $this, 'init' ] ); 42 | add_action( 'smui_cron', [ $this, 'send_ping' ] ); 43 | add_action( 'admin_enqueue_scripts', [ $this, 'admin_scripts' ] ); 44 | 45 | // get the gears turning 46 | $this->apply_sitemap_rules(); 47 | $this->run_cron(); 48 | } 49 | 50 | 51 | /** 52 | * Singleton 53 | */ 54 | public static function instance() { 55 | if ( ! isset( self::$instance ) ) { 56 | self::$instance = new self; 57 | } 58 | return self::$instance; 59 | } 60 | 61 | 62 | function init() { 63 | add_action( 'admin_menu', [ $this, 'admin_menu' ] ); 64 | } 65 | 66 | 67 | function admin_menu() { 68 | add_options_page( 'Sitemap UI', 'Sitemap UI', 'manage_options', 'smui', [ $this, 'settings_page' ] ); 69 | } 70 | 71 | 72 | function admin_scripts( $hook ) { 73 | if ( 'settings_page_smui' == $hook ) { 74 | $settings = get_option( 'smui_settings', '{}' ); 75 | 76 | wp_enqueue_script( 'fselect', SMUI_URL . '/assets/vendor/fSelect/fSelect.js', [ 'jquery' ], SMUI_VERSION ); 77 | wp_enqueue_script( 'smui', SMUI_URL . '/assets/js/admin.js', [], SMUI_VERSION ); 78 | wp_enqueue_style( 'fselect', SMUI_URL . '/assets/vendor/fSelect/fSelect.css', [], SMUI_VERSION ); 79 | wp_enqueue_style( 'smui', SMUI_URL . '/assets/css/admin.css', [], SMUI_VERSION ); 80 | wp_add_inline_script( 'smui', "var SMUI = $settings;" ); 81 | } 82 | } 83 | 84 | 85 | function settings_page() { 86 | include( SMUI_DIR . '/templates/page-settings.php' ); 87 | } 88 | 89 | 90 | function sanitize( $input ) { 91 | if ( is_array( $input ) ) { 92 | $output = []; 93 | 94 | foreach ( $input as $key => $val ) { 95 | $output[ $key ] = $this->sanitize( $val ); 96 | } 97 | } 98 | else { 99 | $output = sanitize_text_field( $input ); 100 | } 101 | 102 | return $output; 103 | } 104 | 105 | 106 | function is_valid_nonce( $name = 'smui_nonce' ) { 107 | return isset( $_POST[ $name ] ) && wp_verify_nonce( $_POST[ $name ], $name ); 108 | } 109 | 110 | 111 | function save_settings() { 112 | $sanitized = $this->sanitize( $_POST['data'] ); 113 | update_option( 'smui_settings', json_encode( $sanitized ) ); 114 | } 115 | 116 | 117 | function get_post_types() { 118 | $post_types = get_post_types( [ 'public' => true ] ); 119 | unset( $post_types['attachment'] ); 120 | return $post_types; 121 | } 122 | 123 | 124 | function get_taxonomies() { 125 | return get_taxonomies( [ 'public' => true ] ); 126 | } 127 | 128 | 129 | function get_settings() { 130 | $settings = get_option( 'smui_settings' ); 131 | return json_decode( $settings, true ); 132 | } 133 | 134 | 135 | function apply_sitemap_rules() { 136 | $this->settings = $this->get_settings(); 137 | 138 | // disable sitemaps 139 | if ( isset( $this->settings['objects']['all'] ) ) { 140 | add_filter( 'wp_sitemaps_enabled', '__return_false' ); 141 | return; 142 | } 143 | 144 | // post type rules 145 | if ( isset( $this->settings['objects']['post_types'] ) ) { 146 | add_filter( 'wp_sitemaps_post_types', '__return_empty_array' ); 147 | } 148 | else { 149 | if ( isset( $this->settings['post_types'] ) ) { 150 | add_filter( 'wp_sitemaps_post_types', function( $post_types ) { 151 | foreach ( $post_types as $name => $obj ) { 152 | if ( in_array( $name, SMUI()->settings['post_types'] ) ) { 153 | unset( $post_types[ $name ] ); 154 | } 155 | } 156 | return $post_types; 157 | }); 158 | } 159 | 160 | if ( isset( $this->settings['post_ids'] ) ) { 161 | add_filter( 'wp_sitemaps_posts_query_args', function( $query_args ) { 162 | $excluded_ids = preg_replace( "/\s+/", '', SMUI()->settings['post_ids'] ); 163 | $excluded_ids = explode( ',', $excluded_ids ); 164 | $query_args['post__not_in'] = $excluded_ids; 165 | return $query_args; 166 | }); 167 | } 168 | } 169 | 170 | // taxonomy rules 171 | if ( isset( $this->settings['objects']['taxonomies'] ) ) { 172 | add_filter( 'wp_sitemaps_taxonomies', '__return_empty_array' ); 173 | } 174 | else { 175 | if ( isset( $this->settings['taxonomies'] ) ) { 176 | add_filter( 'wp_sitemaps_taxonomies', function( $taxonomies ) { 177 | foreach ( $taxonomies as $name => $obj ) { 178 | if ( in_array( $name, SMUI()->settings['taxonomies'] ) ) { 179 | unset( $taxonomies[ $name ] ); 180 | } 181 | } 182 | return $taxonomies; 183 | }); 184 | } 185 | 186 | if ( isset( $this->settings['term_ids'] ) ) { 187 | add_filter( 'wp_sitemaps_taxonomies_query_args', function( $query_args ) { 188 | $excluded_ids = preg_replace( "/\s+/", '', SMUI()->settings['term_ids'] ); 189 | $excluded_ids = explode( ',', $excluded_ids ); 190 | $query_args['exclude'] = $excluded_ids; 191 | return $query_args; 192 | }); 193 | } 194 | } 195 | 196 | // user rules 197 | if ( isset( $this->settings['objects']['users'] ) ) { 198 | add_filter( 'wp_sitemaps_users_query_args', function( $query_args ) { 199 | $query_args['include'] = [ 0 ]; 200 | return $query_args; 201 | }); 202 | } 203 | } 204 | 205 | 206 | function run_cron() { 207 | if ( ! wp_next_scheduled( 'smui_cron' ) ) { 208 | wp_schedule_single_event( time() + 86400, 'smui_cron' ); 209 | } 210 | } 211 | 212 | 213 | function send_ping() { 214 | $settings = $this->get_settings(); 215 | 216 | if ( ! isset( $settings['objects']['all'] ) ) { 217 | $url = get_home_url() . '/wp-sitemap.xml'; 218 | 219 | $response = wp_remote_get( "http://www.google.com/ping?sitemap=$url", [ 220 | 'blocking' => false, 221 | 'timeout' => 0.02 222 | ] ); 223 | } 224 | } 225 | } 226 | 227 | 228 | function SMUI() { 229 | return SMUI_Plugin::instance(); 230 | } 231 | 232 | 233 | SMUI(); 234 | -------------------------------------------------------------------------------- /templates/page-settings.php: -------------------------------------------------------------------------------- 1 | get_post_types(); 5 | $taxonomies = SMUI()->get_taxonomies(); 6 | 7 | if ( SMUI()->is_valid_nonce() ) { 8 | SMUI()->save_settings(); 9 | $has_saved = true; 10 | } 11 | 12 | ?> 13 | 14 |
15 |

Sitemap UI

16 | 17 | 18 |
19 |

Settings saved.

20 |
21 | 22 | 23 |
24 |
Turn off sitemaps
25 |
Exclude all post types
26 |
Exclude all taxonomies
27 |
Exclude all users
28 | 29 |
30 |

Exclude post types

31 | 36 | 37 |

Exclude specific post IDs:

38 | 39 |
40 | 41 |
42 |

Exclude taxonomies

43 | 48 | 49 |

Exclude specific term IDs:

50 | 51 |
52 | 53 |

54 | 55 | 56 |

57 |
58 | --------------------------------------------------------------------------------