Plugin Directory

Changeset 3357461


Ignore:
Timestamp:
09/07/2025 05:02:02 PM (5 months ago)
Author:
ehtmlu
Message:

Version 3.2.0

Location:
advanced-settings
Files:
4 added
1 deleted
10 edited
15 copied

Legend:

Unmodified
Added
Removed
  • advanced-settings/tags/3.2.0/admin-ui/admin-ui.css

    r3323103 r3357461  
    128128    height: 80vh;
    129129    background-color: #fff;
    130     overflow: hidden;
     130    overflow: visible;
    131131}
    132132
     
    149149    flex-direction: column;
    150150    height: 100%;
     151    border-radius: .5em;
     152    overflow: hidden;
    151153}
    152154
     
    248250    flex-direction: column;
    249251    height: 100%;
     252}
     253
     254.advset-modal-madeby {
     255    padding: 0;
     256    display: flex;
     257    gap: 1em;
     258    position: absolute;
     259    top: 100%;
     260    right: 0px;
     261    left: 0px;
     262    color: #fff;
     263    opacity: .8;
     264    justify-content: center;
     265    margin: 0 auto;
     266    width: fit-content;
     267}
     268
     269.advset-modal-madeby img {
     270    flex-shrink: 0;
     271    flex-grow: 0;
     272    width: 50px;
     273    height: auto;
     274}
     275
     276.advset-modal-madeby p {
     277    font-size: var(--advset-font-size-small);
     278}
     279
     280.advset-modal-madeby a {
     281    color: inherit !important;
    250282}
    251283
  • advanced-settings/tags/3.2.0/admin-ui/admin-ui.php

    r3323109 r3357461  
    207207            </div>
    208208        </div>
     209        <div class="advset-modal-madeby">
     210            <img src="<?php echo plugins_url('', ADVSET_FILE); ?>/admin-ui/images/wppeak.com-logo-vectorized-white.svg" alt="Advanced Settings">
     211            <p>
     212                Advanced Settings is made by <a href="https://www.wppeak.com/" target="_blank" rel="noopener noreferrer">wppeak.com</a>.<br />
     213                We make plugins and other useful WordPress tools.
     214            </p>
     215        </div>
    209216    </dialog>
    210217    <?php
  • advanced-settings/tags/3.2.0/advanced-settings.php

    r3323109 r3357461  
    66Author: Helmut Wandl
    77Author URI: https://ehtmlu.com/
    8 Version: 3.1.1
     8Version: 3.2.0
    99Requires at least: 5.0.0
    1010Requires PHP: 7.4
  • advanced-settings/tags/3.2.0/feature-setup/features/developer.php

    r3323103 r3357461  
    135135    'id' => 'developer.settings_pages.post_types',
    136136    'category' => 'developer',
    137     'experimental' => true,
    138137    'ui_config' => fn() => [
    139138        'tags' => [
     
    145144            'enable' => [
    146145                'type' => 'toggle',
    147                 'label' => __('Post types settings', 'advanced-settings'),
    148                 'description' => __('These post types settings are currently under review and may be changed or removed in the future.', 'advanced-settings'),
     146                'label' => __('Custom post types', 'advanced-settings'),
    149147            ],
    150148            'info' => [
     
    157155    'execution_handler' => function() {
    158156        require_once ADVSET_DIR.'/feature-setup/features/includes/developer.settings_pages.php';
    159 
    160         add_action('init', function() {
    161 
    162             $post_types = (array) get_option( 'advset_post_types', array() );
    163        
    164             if( is_admin() && current_user_can('manage_options') && isset($_GET['delete_posttype']) ) {
    165                 unset($post_types[$_GET['delete_posttype']]);
    166                 update_option( 'advset_post_types', $post_types );
    167             }
    168        
    169             if( is_admin() && current_user_can('manage_options') && isset($_POST['advset_action_posttype']) ) {
    170        
    171                 extract($_POST);
    172        
    173                 $labels = array(
    174                     'name' => $label,
    175                     #'singular_name' => @$singular_name,
    176                     #'add_new' => @$add_new,
    177                     #'add_new_item' => @$add_new_item,
    178                     #'edit_item' => @$edit_item,
    179                     #'new_item' => @$new_item,
    180                     #'all_items' => @$all_items,
    181                     #'view_item' => @$view_item,
    182                     #'search_items' => @$search_items,
    183                     #'not_found' =>  @$not_found,
    184                     #'not_found_in_trash' => @$not_found_in_trash,
    185                     #'parent_item_colon' => @$parent_item_colon,
    186                     #'menu_name' => @$menu_name
    187                 );
    188        
    189                 $typename = sanitize_key( $type );
    190        
    191                 $post_types[$type] = array(
    192                     'labels'              => $labels,
    193                     'public'              => (bool) (isset($public) ? $public : false),
    194                     'publicly_queryable'  => (bool) (isset($publicly_queryable) ? $publicly_queryable : false),
    195                     'show_ui'             => (bool) (isset($show_ui) ? $show_ui : false),
    196                     'show_in_menu'        => (bool) (isset($show_in_menu) ? $show_in_menu : false),
    197                     'query_var'           => (bool) (isset($query_var) ? $query_var : false),
    198                     #'rewrite'             => array( 'slug' => 'book' ),
    199                     #'capability_type'     => 'post',
    200                     'has_archive'         => (bool) (isset($has_archive) ? $has_archive : false),
    201                     'hierarchical'        => (bool) (isset($hierarchical) ? $hierarchical : false),
    202                     #'menu_position'       => (int)@$menu_position,
    203                     'supports'            => (array) (empty($supports) ? [] : $supports),
    204                     'taxonomies'          => (array) (empty($taxonomies) ? [] : $taxonomies),
    205                 );
    206        
    207                 update_option( 'advset_post_types', $post_types );
    208        
    209             }
    210             #print_r($post_types);
    211             if( sizeof($post_types)>0 )
    212                 foreach( $post_types as $post_type=>$args ) {
    213                     register_post_type( $post_type, $args );
    214                     if( in_array( 'thumbnail', $args['supports'] ) ) {
    215                         add_theme_support( 'post-thumbnails', array( $post_type, 'post' ) );
    216                     }
    217                 }
    218        
    219         });
    220 
    221         add_action('admin_menu', function() {
    222             add_options_page(
    223                 __('Post Types', 'advanced-settings'),
    224                 __('Post Types', 'advanced-settings'),
    225                 'manage_options',
    226                 'advanced-settings-post-types',
    227                 function() {
    228                     include ADVSET_DIR.'/feature-setup/features/includes/developer.settings_pages.post_types--admin-post-types.php';
    229                 }
    230             );
    231         });
     157        require_once ADVSET_DIR.'/feature-setup/features/includes/developer.settings_pages.post_types--init.php';
     158        Advset__Feature__Post_Types::init();
    232159    },
    233160    'priority' => 10,
  • advanced-settings/tags/3.2.0/feature-setup/features/editing.php

    r3323103 r3357461  
    355355    ],
    356356    'execution_handler' => function() {
    357         require_once ADVSET_DIR . '/feature-setup/features/includes/frontend.auto_thumbs.php';
    358         add_action('transition_post_status', 'advset__feature__auto_thumbs', 10, 3);
     357        add_action('transition_post_status', function($new_status, $old_status, $post) {
     358            if ($new_status !== 'publish') {
     359                return;
     360            }
     361            require_once ADVSET_DIR . '/feature-setup/features/includes/frontend.auto_thumbs.php';
     362            Advset__Feature__Auto_Thumbs::init()->transition_post_status($new_status, $old_status, $post);
     363        }, 10, 3);
    359364    },
    360365    'priority' => 70,
  • advanced-settings/tags/3.2.0/feature-setup/features/includes/developer.settings_pages.post_types--admin-post-types.php

    r3286786 r3357461  
    1 <?php defined('ABSPATH') or exit;
    2 
    3     global $_wp_post_type_features;
    4 
    5     $post_types = get_post_types( '', 'objects' );
    6 
    7     unset( $post_types['attachment'], $post_types['revision'], $post_types['nav_menu_item'] );
    8 
    9     $advset_post_types = (array) get_option( 'advset_post_types', array() );
    10 
    11     ?>
    12     <script>
    13     <?php echo 'posttype_data='.json_encode($post_types).';'; ?>
    14     <?php echo 'supports='.json_encode($_wp_post_type_features).';'; ?>
    15     show_form=function(type) {
    16 
    17         $=jQuery;
    18 
    19         if( type ) {
    20 
    21             data = posttype_data[type];
    22 
    23             data.type=type;
    24             populate( '#posttype_form', data );
    25 
    26             var checks = $('[name=supports\\[\\]]', '#posttype_form');
    27             for( i=0; i<checks.length;i++ )
    28                 if( supports[type][$(checks[i]).attr('value')] )
    29                     $(checks[i]).attr("checked", "checked");
    30                 else
    31                     $(checks[i]).attr("checked", null);
    32 
    33             var checks = $('[name=taxonomies\\[\\]]', '#posttype_form');
    34             for( i=0; i<checks.length;i++ )
    35                 if( data['taxonomies'].join(',').indexOf($(checks[i]).attr('value'))>-1 )
    36                     $(checks[i]).attr("checked", "checked");
    37                 else
    38                     $(checks[i]).attr("checked", null);
    39 
    40         }
    41 
    42         $('#post_type_list').hide();
    43         $('#post_type_form').fadeIn();
    44         //$('#post_type_form').reset(); // dont works
    45         $('#namefield').focus();
     1<?php
     2
     3if (!defined('ABSPATH')) exit;
     4
     5
     6$advset_posttype_nonce = wp_create_nonce('advset_posttype_nonce');
     7
     8$advset_posttypes_data = get_option('advset_post_types', []);
     9
     10$advset_posttypes_data_default = [
     11    'supports' => [
     12        'title',
     13        'editor',
     14    ],
     15    'public' => true,
     16    'publicly_queryable' => true,
     17    'show_ui' => true,
     18    'show_in_menu' => true,
     19    'query_var' => true,
     20    'has_archive' => false,
     21    'hierarchical' => false,
     22    'taxonomies' => [
     23        'category',
     24        'post_tag',
     25    ],
     26];
     27
     28
     29
     30
     31?>
     32<script>
     33
     34document.addEventListener('DOMContentLoaded', function() {
     35    const formContainerElement = document.getElementById('advset_posttype_form_container');
     36    const listContainerElement = document.getElementById('advset_posttype_list_container');
     37    const formElement = formContainerElement?.querySelector('#advset_posttype_form');
     38    const labelInputElement = formContainerElement?.querySelector('[name="label"]');
     39    const typeInputElement = formContainerElement?.querySelector('[name="type"]');
     40    const typeStoredInputElement = formContainerElement?.querySelector('[name="type_stored"]');
     41    const typeAvailableIndicatorElement = formContainerElement?.querySelector('#advset_type_available_indicator');
     42    const submitButtonElement = formContainerElement?.querySelector('[type="submit"]');
     43
     44    const posttypes_data = <?php echo json_encode($advset_posttypes_data); ?>;
     45    const posttypes_data_default = <?php echo json_encode($advset_posttypes_data_default); ?>;
     46
     47    document.addEventListener('click', function(event) {
     48        if (event.target.closest('.advset-posttype-edit-link')) {
     49            event.preventDefault();
     50            showForm(event.target.getAttribute('data-type'));
     51        }
     52        if (event.target.closest('.advset-posttype-cancel-link')) {
     53            event.preventDefault();
     54            showList();
     55        }
     56        if (event.target.closest('.advset-posttype-delete-link')) {
     57            event.preventDefault();
     58            if (confirm('<?php _e('Are you sure you want to delete this post type?') ?>')) {
     59                advset_submit_delete_posttype(event.target.getAttribute('data-type'));
     60            }
     61        }
     62    });
     63
     64    labelInputElement.addEventListener('blur', function() {
     65        if (typeInputElement.value === '') {
     66            checkType(true);
     67        }
     68    });
     69
     70    typeInputElement.addEventListener('input', function() {
     71        checkType();
     72    });
     73
     74    function checkType(generate_from_label = false) {
     75        setTypeValidity(null);
     76        if (!generate_from_label && typeInputElement.value === '') {
     77            return;
     78        }
     79        fetch('<?php echo rest_url('advset_posttypes/v1/check-type'); ?>', {
     80            method: 'POST',
     81            body: JSON.stringify({
     82                label_input: labelInputElement.value,
     83                type_input: typeInputElement.value,
     84                type_stored: typeStoredInputElement.value,
     85                generate_from_label,
     86            }),
     87            headers: {
     88                'Content-Type': 'application/json',
     89                'X-WP-Nonce': '<?php echo wp_create_nonce('wp_rest'); ?>',
     90            },
     91        })
     92        .then(response => response.json())
     93        .then(data => {
     94            if (generate_from_label && data.type_available) {
     95                typeInputElement.value = data.type_available;
     96                setTypeValidity(true);
     97            }
     98            else if (data.is_taken) {
     99                setTypeValidity(false);
     100            }
     101            else if (data.is_valid) {
     102                setTypeValidity(true);
     103            }
     104        });
     105    }
     106
     107    function setTypeValidity(validity) {
     108        let color, icon, text;
     109        switch (validity) {
     110            case true:
     111                color = 'green';
     112                icon = '✅';
     113                text = 'Type is available';
     114                break;
     115            case false:
     116                color = 'red';
     117                icon = '❌';
     118                text = 'Type already exists';
     119                break;
     120            default:
     121                color = 'gray';
     122                icon = '';
     123                text = '';
     124                break;
     125        }
     126        typeInputElement.setCustomValidity(validity === false ? text : '');
     127        typeAvailableIndicatorElement.textContent = icon + ' ' + text;
     128        typeAvailableIndicatorElement.style.color = color;
     129    }
     130
     131    function showForm(type) {
     132
     133        const data = type ? posttypes_data[type] : posttypes_data_default;
     134        data.label = data?.labels?.name ?? type;
     135        data.type = type ?? '';
     136        data.type_stored = type ?? '';
     137
     138        formElement.querySelectorAll('[name]').forEach(element => {
     139            if (element.name.slice(0, 1) === '_') {
     140                return;
     141            }
     142           
     143            if (element.type === 'checkbox') {
     144                if (element.name.slice(-2) === '[]') {
     145                    element.checked = data[element.name.slice(0, -2)]?.includes(element.value) ?? element.defaultChecked;
     146                } else {
     147                    element.checked = data[element.name] ?? element.defaultChecked;
     148                }
     149            } else if (['text', 'hidden', 'textarea'].includes(element.type)) {
     150                element.value = data[element.name] ?? element.defaultValue;
     151            }
     152        });
     153
     154        submitButtonElement.value = type ? submitButtonElement.dataset.saveChanges : submitButtonElement.dataset.create;
     155
     156        setTypeValidity(null);
     157
     158        listContainerElement.style.display = 'none';
     159        formContainerElement.style.display = '';
     160        labelInputElement.focus();
     161    }
     162
     163    function showList() {
     164        formContainerElement.style.display = 'none';
     165        listContainerElement.style.display = '';
     166    };
     167
     168    // Submit delete via hidden POST form
     169    function advset_submit_delete_posttype(slug) {
     170        const form = document.getElementById('advset_delete_posttype_form');
     171        const input = document.getElementById('advset_delete_posttype_slug');
     172        if (!slug || !form || !input) return false;
     173        input.value = slug;
     174        form.submit();
    46175        return false;
    47176    };
    48     populate=function (frm, data) {
    49         $=jQuery;
    50         $.each(data, function(key, value){
    51             var $ctrl = $('[name='+key+']', frm);
    52             switch($ctrl.attr("type")) {
    53                 case "text" :
    54                 case "hidden":
    55                 case "textarea":
    56                 $ctrl.val(value);
    57                 break;
    58                 case "radio" : case "checkbox":
    59                 $ctrl.each(function(){
    60                     if( $(this).attr('value') == value
    61                         || $(this).attr('name')=='query_var' && $(this).attr('value')!='' )
    62                         $(this).attr("checked",value);
    63                     else
    64                         $(this).attr("checked",null);
    65                 });
    66                 break;
    67             }
    68         });
    69     };
    70     show_list=function() {
    71         $('#post_type_form').hide();
    72         $('#post_type_list').fadeIn();
    73     };
    74     str2slug=function(str) {
    75         var new_str = '';
    76         str=str.toLowerCase();
    77         str=str.replace(/[aáàãâä]/g,'a');
    78         str=str.replace(/[éèêë]/g,'e');
    79         str=str.replace(/[íìîï]/g,'i');
    80         str=str.replace(/[óòõôö]/g,'o');
    81         str=str.replace(/[úùûü]/g,'u');
    82         str=str.replace('ç','c');
    83         str=str.replace(/[^a-z0-9_\-]/g,'');
    84         str=str.substring(0, 20);
    85         return str;
    86     };
    87     function in_array(needle, haystack) {
    88         var length = haystack.length;
    89         for(var i = 0; i < length; i++) {
    90             if(haystack[i] == needle) return true;
    91         }
    92         return false;
    93     }
    94     </script>
    95 
    96     <div class="wrap">
    97             <?php advset_page_header() ?>
    98             <br />
    99             <?php echo advset_page_experimental(); ?>
    100             <br />
    101             <br />
    102 
    103 
    104             <div style="padding: 1rem; background: #fff; border: #f00 solid 2px; border-radius: .5rem; ">
    105                 <h3 style="margin: 0; ">WARNING</h3>
    106                 <p style="margin: 0; "><?php _e('Be careful, changing a post type can significantly impact the functionality of the website.') ?></p>
    107             </div>
    108             <br />
    109             <a href="#" onclick="return show_form();" class="add-new-h2">Add New Post Type</a>
    110             <br />
    111             <br />
    112 
    113         <div id="post_type_form" style="display:none">
    114 
    115             <h3>Add New Post Type</h3>
    116 
    117             <form id="posttype_form" action="" method="post">
    118                 <?php #settings_fields( 'advanced-settings-post-types' ); ?>
    119 
    120                 <input type="hidden" name="advset_action_posttype" value="1" />
    121 
    122                 <table class="form-table">
    123                     <tr valign="top">
    124                         <th scope="row"><?php _e('Label'); ?></th>
    125                         <td>
    126                             <input id="namefield" name="label" type="text" value="" onblur="
    127                             const input = document.getElementById('typefield');
    128                             if( input.value === '' )
    129                                 input.value = str2slug(this.value);
    130                             " />
    131 
    132                             <!--p><a href="#">+ show more labels</a></p-->
    133 
    134                         </td>
    135                     </tr>
    136 
    137                     <tr valign="top">
    138                         <th scope="row"><?php _e('Type Name'); ?></th>
    139                         <td>
    140                             <input id="typefield" name="type" type="text" value="" pattern="^[a-z0-9_\-]{1,20}$" required /><br />
    141                             <i style="color:#999">Post type names must be between 1 and 20 characters in length. Lowercase alphanumeric characters, dashes, and underscores are allowed. </i>
    142                         </td>
    143                     </tr>
    144 
    145                     <tr valign="top">
    146                         <th scope="row"><?php _e('Supports'); ?></th>
    147                         <td>
    148                             <input name="supports[]" id="posttype-support-title" value="title" type="checkbox" checked="checked">
    149                             <label for="posttype-support-title">title</label><br />
    150                             <input name="supports[]" id="posttype-support-editor" value="editor" type="checkbox" checked="checked">
    151                             <label for="posttype-support-editor">editor</label><br />
    152                             <input name="supports[]" id="posttype-support-author" value="author" type="checkbox">
    153                             <label for="posttype-support-author">author</label><br />
    154                             <input name="supports[]" id="posttype-support-thumbnail" value="thumbnail" type="checkbox">
    155                             <label for="posttype-support-thumbnail">thumbnail</label><br />
    156                             <input name="supports[]" id="posttype-support-excerpt" value="excerpt" type="checkbox">
    157                             <label for="posttype-support-excerpt">excerpt</label><br />
    158                             <input name="supports[]" id="posttype-support-trackbacks" value="trackbacks" type="checkbox">
    159                             <label for="posttype-support-trackbacks">trackbacks</label><br />
    160                             <input name="supports[]" id="posttype-support-custom-fields" value="custom-fields" type="checkbox">
    161                             <label for="posttype-support-custom-fields">custom fields</label><br />
    162                             <input name="supports[]" id="posttype-support-comments" value="comments" type="checkbox">
    163                             <label for="posttype-support-comments">comments</label><br />
    164                             <input name="supports[]" id="posttype-support-revisions" value="revisions" type="checkbox">
    165                             <label for="posttype-support-revisions">revisions</label> <br />
    166                             <input name="supports[]" id="posttype-support-page-attributes" value="page-attributes" type="checkbox">
    167                             <label for="posttype-support-page-attributes">page attributes</label>
    168                         </td>
    169                     </tr>
    170 
    171                     <tr valign="top">
    172                         <th scope="row"><?php _e('Settings'); ?></th>
    173                         <td>
    174                             <label><input name="public" value="1" type="checkbox" checked="checked">
    175                                 public</label><br />
    176                             <label><input name="publicly_queryable" value="1" type="checkbox" checked="checked">
    177                                 publicly_queryable</label><br />
    178                             <label><input name="show_ui" value="1" type="checkbox" checked="checked">
    179                                 show_ui</label><br />
    180                             <label><input name="show_in_menu" value="1" type="checkbox" checked="checked">
    181                                 show_in_menu</label><br />
    182                             <label><input name="query_var" value="1" type="checkbox" checked="checked">
    183                                 query_var</label><br />
    184                             <!--label><input name="rewrite" value="1" type="checkbox">
    185                                 rewrite</label><br /-->
    186                             <!--label><input name="capability_type" value="1" type="checkbox">
    187                                 capability_type</label><br /-->
    188                             <label><input name="has_archive" value="1" type="checkbox">
    189                                 has_archive</label><br />
    190                             <label><input name="hierarchical" value="1" type="checkbox">
    191                                 hierarchical</label> <br />
    192                             <!--label><input name="menu_position" value="1" type="checkbox">
    193                                 menu_position</label-->
    194                         </td>
    195                     </tr>
    196 
    197                     <tr valign="top">
    198                         <th scope="row"><?php _e('Taxonomies'); ?></th>
    199                         <td>
    200                             <label><input name="taxonomies[]" value="category" type="checkbox" checked="checked">
    201                                 category</label><br />
    202                             <label><input name="taxonomies[]" value="post_tag" type="checkbox" checked="checked">
    203                                 post_tag</label>
    204                         </td>
    205                     </tr>
    206 
    207                 </table>
    208                 <p class="submit">
    209                     <input type="submit" name="submit" id="submit" class="button button-primary" value="<?php _e('Save Changes') ?>" />
    210                     <input type="button" onclick="show_list();" class="button" value="<?php _e('Cancel') ?>" />
    211                 </p>
    212             </form>
    213         </div>
    214 
    215         <form id="post_type_list" action="options.php" method="post">
    216 
    217             <table class="widefat fixed" cellspacing="0">
     177});
     178</script>
     179
     180<div class="wrap">
     181    <?php advset_page_header() ?>
     182    <br />
     183    <!-- <?php echo advset_page_experimental(); ?>
     184    <br />
     185    <br /> -->
     186
     187
     188    <!-- <div style="padding: 1rem; background: #fff; border: #f00 solid 2px; border-radius: .5rem; ">
     189        <h3 style="margin: 0; ">WARNING</h3>
     190        <p style="margin: 0; "><?php _e('Be careful, changing a post type can significantly impact the functionality of the website.') ?></p>
     191    </div>
     192    <br /> -->
     193    <a class="advset-posttype-edit-link add-new-h2" href="#" data-type=""><?php _e('Add') ?></a>
     194    <br />
     195    <br />
     196
     197    <div id="advset_posttype_form_container" style="display:none">
     198
     199        <h3><?php _e('Add') ?></h3>
     200
     201        <form id="advset_posttype_form" action="" method="post">
     202            <?php #settings_fields( 'advanced-settings-post-types' ); ?>
     203
     204            <input type="hidden" name="_advset_posttype_nonce" value="<?php echo $advset_posttype_nonce; ?>" />
     205
     206            <input type="hidden" name="_advset_posttype_action_save" value="1" />
     207
     208            <input type="hidden" name="type_stored" value="" />
     209
     210            <table class="form-table">
     211                <tr valign="top">
     212                    <th scope="row"><?php _e('Label'); ?></th>
     213                    <td>
     214                        <input name="label" type="text" value="" />
     215
     216                        <!--p><a href="#">+ show more labels</a></p-->
     217
     218                    </td>
     219                </tr>
     220
     221                <tr valign="top">
     222                    <th scope="row"><?php _e('Type Name'); ?></th>
     223                    <td>
     224                        <input name="type" type="text" value="" pattern="^[a-z0-9_\-]{1,20}$" required /> <span id="advset_type_available_indicator"></span><br />
     225                        <i style="color:#999">Post type names must be between 1 and 20 characters in length. Lowercase alphanumeric characters, dashes, and underscores are allowed. </i>
     226                    </td>
     227                </tr>
     228
     229                <tr valign="top">
     230                    <th scope="row"><?php _e('Description'); ?></th>
     231                    <td>
     232                        <!-- <textarea name="description" rows="3" style="width: 100%;"></textarea> -->
     233                            <input name="description" type="text" value="" />
     234                    </td>
     235                </tr>
     236
     237                <tr valign="top">
     238                    <th scope="row"><?php _e('Supports'); ?></th>
     239                    <td>
     240                        <input name="supports[]" id="posttype-support-title" value="title" type="checkbox">
     241                        <label for="posttype-support-title">title</label><br />
     242                        <input name="supports[]" id="posttype-support-editor" value="editor" type="checkbox">
     243                        <label for="posttype-support-editor">editor</label><br />
     244                        <input name="supports[]" id="posttype-support-author" value="author" type="checkbox">
     245                        <label for="posttype-support-author">author</label><br />
     246                        <input name="supports[]" id="posttype-support-thumbnail" value="thumbnail" type="checkbox">
     247                        <label for="posttype-support-thumbnail">thumbnail</label><br />
     248                        <input name="supports[]" id="posttype-support-excerpt" value="excerpt" type="checkbox">
     249                        <label for="posttype-support-excerpt">excerpt</label><br />
     250                        <input name="supports[]" id="posttype-support-trackbacks" value="trackbacks" type="checkbox">
     251                        <label for="posttype-support-trackbacks">trackbacks</label><br />
     252                        <input name="supports[]" id="posttype-support-custom-fields" value="custom-fields" type="checkbox">
     253                        <label for="posttype-support-custom-fields">custom fields</label><br />
     254                        <input name="supports[]" id="posttype-support-comments" value="comments" type="checkbox">
     255                        <label for="posttype-support-comments">comments</label><br />
     256                        <input name="supports[]" id="posttype-support-revisions" value="revisions" type="checkbox">
     257                        <label for="posttype-support-revisions">revisions</label> <br />
     258                        <input name="supports[]" id="posttype-support-page-attributes" value="page-attributes" type="checkbox">
     259                        <label for="posttype-support-page-attributes">page attributes</label>
     260                    </td>
     261                </tr>
     262
     263                <tr valign="top">
     264                    <th scope="row"><?php _e('Settings'); ?></th>
     265                    <td>
     266                        <label><input name="public" value="1" type="checkbox">
     267                            public</label><br />
     268                        <label><input name="publicly_queryable" value="1" type="checkbox">
     269                            publicly_queryable</label><br />
     270                        <label><input name="show_ui" value="1" type="checkbox">
     271                            show_ui</label><br />
     272                        <label><input name="show_in_menu" value="1" type="checkbox">
     273                            show_in_menu</label><br />
     274                        <label><input name="query_var" value="1" type="checkbox">
     275                            query_var</label><br />
     276                        <!--label><input name="rewrite" value="1" type="checkbox">
     277                            rewrite</label><br /-->
     278                        <!--label><input name="capability_type" value="1" type="checkbox">
     279                            capability_type</label><br /-->
     280                        <label><input name="has_archive" value="1" type="checkbox">
     281                            has_archive</label><br />
     282                        <label><input name="hierarchical" value="1" type="checkbox">
     283                            hierarchical</label> <br />
     284                        <!--label><input name="menu_position" value="1" type="checkbox">
     285                            menu_position</label-->
     286                    </td>
     287                </tr>
     288
     289                <tr valign="top">
     290                    <th scope="row"><?php _e('Taxonomies'); ?></th>
     291                    <td>
     292                        <label><input name="taxonomies[]" value="category" type="checkbox">
     293                            category</label><br />
     294                        <label><input name="taxonomies[]" value="post_tag" type="checkbox">
     295                            post_tag</label>
     296                    </td>
     297                </tr>
     298
     299            </table>
     300            <p class="submit">
     301                <input type="submit" id="submit" class="button button-primary" value="<?php _e('Save Changes') ?>" data-save-changes="<?php _e('Save Changes') ?>" data-create="<?php _e('Create') ?>" />
     302                <input type="button" class="button advset-posttype-cancel-link" value="<?php _e('Cancel') ?>" />
     303            </p>
     304        </form>
     305    </div>
     306
     307    <div id="advset_posttype_list_container">
     308        <?php if (!empty($advset_posttypes_data)) { ?>
     309        <form id="advset_posttype_list" action="options.php" method="post">
     310
     311            <table class="widefat fixed striped" cellspacing="0">
    218312                <thead>
    219313                    <tr>
    220                         <th scope="col" id="cb" class="manage-column column-cb check-column" style=""><label class="screen-reader-text" for="cb-select-all-1">Selecionar Tudo</label><input id="cb-select-all-1" type="checkbox"></th>
    221                         <th scope="col" id="title" class="manage-column column-title" width="40%">Label <small>(Menu name)</small></th>
    222                         <th scope="col" id="type_name" class="manage-column column-title" width="30%">Type</th>
    223                         <th scope="col" id="type_desc" class="manage-column column-title" width="30%">Description</th>
     314                        <th scope="col" id="title" class="manage-column column-title" width="40%"><?php _e('Label') ?></th>
     315                        <th scope="col" id="type_name" class="manage-column column-title" width="30%"><?php _e('Type') ?></th>
     316                        <th scope="col" id="type_desc" class="manage-column column-title" width="30%"><?php _e('Description') ?></th>
    224317                    </tr>
    225318                </thead>
    226                 <?php foreach($post_types as $typename=>$post_type) { ?>
     319                <?php foreach($advset_posttypes_data as $typename => $post_type) { ?>
    227320                <tr class=" iedit">
    228                     <th scope="row" class="check-column">
    229 
    230                         <input id="cb-select-1" type="checkbox" name="post[]" value="1">
    231                         <div class="locked-indicator"></div>
    232                     </th>
    233                     <td>
    234                         <strong><?php echo $post_type->label ?></strong>
     321                    <td>
     322                        <strong><?php echo $post_type['labels']['name'] ?></strong>
    235323                        <div class="row-actions">
    236                             <?php if( !in_array( $post_type->name, array('post', 'page') ) ) { ?>
    237324                        <span class="edit">
    238                             <a href="#" onclick="show_form('<?php echo $post_type->name ?>');">Edit</a>
     325                            <a class="advset-posttype-edit-link" href="#" data-type="<?php echo esc_attr($typename) ?>"><?php _e('Edit') ?></a>
     326                            |
    239327                        </span>
    240                                 | <a href="options-general.php?page=advanced-settings-post-types&delete_posttype=<?php echo $post_type->name ?>" title="default categories">delete</a>
    241                             <?php } else echo '&nbsp;'; ?>
    242 
     328                        <span class="trash">
     329                            <a class="advset-posttype-delete-link" href="#" data-type="<?php echo esc_attr($typename) ?>"><?php _e('Delete') ?></a>
     330                        </span>
    243331                        </div>
    244332                    </td>
    245                     <td><?php echo $post_type->name ?></td>
    246                     <td></td>
     333                    <td><?php echo $typename ?></td>
     334                    <td><?php echo str_replace("\n", '<br />', strip_tags($post_type['description'] ?? '')) ?></td>
    247335                </tr>
    248336                <?php } ?>
     
    250338                <tfoot>
    251339                    <tr>
    252                         <th scope="col" class="manage-column column-cb check-column" style=""><label class="screen-reader-text" for="cb-select-all-2">Selecionar Tudo</label><input id="cb-select-all-2" type="checkbox"></th>
    253                         <th scope="col" id="title" class="manage-column column-title" width="40%">Label <small>(Menu name)</small></th>
    254                         <th scope="col" id="type_name" class="manage-column column-title" width="30%">Type</th>
    255                         <th scope="col" id="type_desc" class="manage-column column-title" width="30%">Description</th>
     340                        <th scope="col" id="title" class="manage-column column-title" width="40%"><?php _e('Label') ?></th>
     341                        <th scope="col" id="type_name" class="manage-column column-title" width="30%"><?php _e('Type') ?></th>
     342                        <th scope="col" id="type_desc" class="manage-column column-title" width="30%"><?php _e('Description') ?></th>
    256343                    </tr>
    257344                </tfoot>
    258345
    259346            </table>
    260 
    261             <!--p class="submit"><input type="submit" name="submit" id="submit" class="button-primary" value="<?php _e('Save changes') ?>"></p-->
     347        </form>
     348        <?php } else { ?>
     349        <div>
     350            <p><?php _e('No custom post types found.') ?></p>
     351        </div>
     352        <?php } ?>
     353
     354        <form id="advset_delete_posttype_form" action="<?php echo esc_url( admin_url('options-general.php?page=advanced-settings-post-types') ); ?>" method="post" style="display: none;">
     355            <input type="hidden" name="_advset_posttype_nonce" value="<?php echo esc_attr($advset_posttype_nonce); ?>" />
     356            <input type="hidden" name="_advset_posttype_action_delete" id="advset_delete_posttype_slug" value="" />
    262357        </form>
    263358    </div>
     359</div>
  • advanced-settings/tags/3.2.0/feature-setup/features/includes/frontend.auto_thumbs.php

    r3281733 r3357461  
    11<?php
    22
    3 if( !defined('ABSPATH') ) exit;
     3if (!defined('ABSPATH')) exit;
    44
    55
    6 function advset__feature__auto_thumbs( $new_status, $old_status, $post ) {
    7     if ('publish' == $new_status) {
    8         advset__feature__auto_thumbs__publish_post($post);
     6/**
     7 * Auto Thumbs feature class.
     8 */
     9class Advset__Feature__Auto_Thumbs {
     10
     11    private static $instance = null;
     12
     13    private function __construct() {}
     14   
     15    /**
     16     * Initialize the feature.
     17     *
     18     * @return Advset__Feature__Auto_Thumbs The instance of the feature.
     19     */
     20    public static function init() {
     21        if (self::$instance === null) {
     22            self::$instance = new self();
     23        }
     24        return self::$instance;
    925    }
    10 }
     26   
     27    /**
     28     * Transition the post status.
     29     *
     30     * @param string $new_status The new status of the post.
     31     * @param string $old_status The old status of the post.
     32     * @param object $post The post object.
     33     * @return bool True if the post thumbnail was generated, false otherwise.
     34     */
     35    public function transition_post_status( string $new_status, string $old_status, object $post ) {
     36        global $wpdb;
     37       
     38        // If the post is not published, return
     39        if ($new_status !== 'publish') {
     40            return false;
     41        }
    1142
    12 //
    13 function advset__feature__auto_thumbs__publish_post( $post ) {
    14     global $wpdb;
    15 
    16     // First check whether Post Thumbnail is already set for this post.
    17     if (get_post_meta($post->ID, '_thumbnail_id', true) || get_post_meta($post->ID, 'skip_post_thumb', true))
    18         return;
    19 
    20     // Initialize variable used to store list of matched images as per provided regular expression
    21     $matches = array();
    22 
    23     // Get all images from post's body
    24     preg_match_all('/<\s*img [^\>]*src\s*=\s*[\""\']?([^\""\'>]*)[^\>]*/i', empty($post->post_content) ? '' : $post->post_content, $matches);
    25 
    26     if (count($matches)) {
    27         foreach ($matches[0] as $key => $image) {
    28             /**
    29              * If the image is from wordpress's own media gallery, then it appends the thumbmail id to a css class.
    30              * Look for this id in the IMG tag.
    31              */
    32             preg_match('/wp-image-([\d]*)/i', $image, $thumb_id);
    33             $thumb_id = empty($thumb_id[1]) ? null : $thumb_id[1];
    34 
     43        // First check whether Post Thumbnail is already set for this post.
     44        if (get_post_meta($post->ID, '_thumbnail_id', true) || get_post_meta($post->ID, 'skip_post_thumb', true))
     45            return false;
     46   
     47        // Initialize variable used to store the thumbnail id
     48        $thumb_id = null;
     49   
     50        // Initialize variable used to store list of matched images as per provided regular expression
     51        $images = [];
     52   
     53        // Get all images from post's body
     54        preg_match_all('/<\s*img [^\>]*src\s*=\s*[\""\']?([^\""\'>]*)[^\>]*/i', $post->post_content ?? '', $images, PREG_SET_ORDER);
     55   
     56        // If no images are found, return
     57        if (empty($images)) {
     58            return false;
     59        }
     60   
     61        // Loop through all images
     62        foreach ($images as $image) {
     63            $img_html = $image[0];
     64            $image_url = $image[1];
     65   
     66            // If the image is from wordpress's own media gallery, then it appends the thumbmail id to a css class.
     67            preg_match('/\s+class\s*=\s*[\""\']?([^\""\'>]*)/i', $img_html, $matches_class);
     68            preg_match('/(?:^|\s+)wp-image-([\d]*)(?:\s+|$)/i', $matches_class[1] ?? '', $matches_thumb_id);
     69            if (!empty($matches_thumb_id[1]) && (int) $matches_thumb_id[1] > 0) {
     70                $thumb_id = (int) $matches_thumb_id[1];
     71                break;
     72            }
     73   
     74            // Get the image URL
     75            if (!wp_http_validate_url($image_url)) {
     76                continue;
     77            }
     78   
    3579            // If thumb id is not found, try to look for the image in DB. Thanks to "Erwin Vrolijk" for providing this code.
    36             if (!$thumb_id) {
    37                 $image = $matches[1][$key];
    38                 $result = $wpdb->get_results($wpdb->prepare("SELECT ID FROM {$wpdb->posts} WHERE guid = %s", $image));
    39                 $thumb_id = empty($result[0]->ID) ? null : $result[0]->ID;
     80            $attachment = $wpdb->get_results($wpdb->prepare("SELECT ID FROM {$wpdb->posts} WHERE guid = %s", $image_url));
     81            if (!empty($attachment[0]->ID) && (int) $attachment[0]->ID > 0) {
     82                $thumb_id = (int) $attachment[0]->ID;
     83                break;
    4084            }
    41 
     85   
    4286            // Ok. Still no id found. Some other way used to insert the image in post. Now we must fetch the image from URL and do the needful.
    43             if (!$thumb_id) {
    44                 $thumb_id = advset__feature__auto_thumbs__generate_post_thumbnail($matches, $key, $post);
    45             }
    46 
    47             // If we succeed in generating thumg, let's update post meta
    48             if ($thumb_id) {
    49                 update_post_meta( $post->ID, '_thumbnail_id', $thumb_id );
     87            preg_match('/\s+alt\s*=\s*[\""\']?([^\""\'>]*)/i', $img_html, $matches_alt);
     88            preg_match('/\s+title\s*=\s*[\""\']?([^\""\'>]*)/i', $img_html, $matches_title);
     89            $imported_thumb_id = $this->import_image_from_url($image_url, $post->ID, [
     90                'alt' => $matches_alt[1] ?? '',
     91                'title' => $matches_title[1] ?? '',
     92            ]);
     93            if (!is_wp_error($imported_thumb_id) && (int) $imported_thumb_id > 0) {
     94                $thumb_id = (int) $imported_thumb_id;
    5095                break;
    5196            }
    5297        }
     98   
     99        // If we succeed in generating thumg, let's update post meta
     100        if ($thumb_id) {
     101            update_post_meta( $post->ID, '_thumbnail_id', $thumb_id );
     102            return true;
     103        }
     104
     105        return false;
     106    }
     107   
     108    /**
     109     * Imports an image from an external URL into the media library.
     110     *
     111     * @param string $url The URL of the image to import.
     112     * @param int $post_id The ID of the post to import the image to.
     113     * @param array $opts Optional arguments for the import.
     114     * @return int|\WP_Error Attachment-ID oder Fehler
     115     */
     116    private function import_image_from_url( string $url, int $post_id = 0, array $opts = [] ) {
     117       
     118        // Validate the URL
     119        if (!wp_http_validate_url($url)) {
     120            return new WP_Error('invalid_url', __('Invalid image URL.'));
     121        }
     122
     123        // Parse the options
     124        $opts = wp_parse_args($opts, [
     125            'alt'     => '',
     126            'title'   => '',
     127        ]);
     128
     129        // Require the necessary files
     130        require_once ABSPATH.'wp-admin/includes/file.php';
     131        require_once ABSPATH.'wp-admin/includes/image.php';
     132        require_once ABSPATH.'wp-admin/includes/media.php';
     133
     134        // Download the image
     135        $tmp = download_url($url);
     136        if ( is_wp_error($tmp) ) {
     137            return $tmp;
     138        }
     139
     140        // Get the name and mime type of the image
     141        $name = wp_basename(parse_url($url, PHP_URL_PATH) ?: 'image');
     142        $wp_filetype = wp_check_filetype_and_ext( $tmp, $name );
     143
     144        // Check if the image is a valid image
     145        if (empty($wp_filetype['type']) || !wp_match_mime_types('image', $wp_filetype['type'])) {
     146            return new WP_Error('invalid_image', __('The uploaded file is not a valid image. Please try again.'));
     147        }
     148
     149        // Create the file array
     150        $file_array = [
     151            'name'     => $name,
     152            'tmp_name' => $tmp,
     153            'type'     => $wp_filetype['type'],
     154        ];
     155
     156        // Create the post data
     157        $post_data = [];
     158        if (!empty($opts['title'])) {
     159            $post_data['post_title'] = $opts['title'];
     160        }
     161
     162        // Create the description
     163        $desc = null;
     164
     165        // Sideload the image
     166        $attachment_id = media_handle_sideload($file_array, $post_id, $desc, $post_data);
     167
     168        // Unlink the temporary file if it exists
     169        @unlink($tmp);
     170
     171        // If there is an error, return the error
     172        if ( is_wp_error($attachment_id) ) {
     173            return $attachment_id;
     174        }
     175
     176        // Update the attachment meta if the alt text is provided
     177        if (!empty($opts['alt'])) {
     178            update_post_meta($attachment_id, '_wp_attachment_image_alt', $opts['alt']);
     179        }
     180
     181        // Return the attachment ID (as integer)
     182        return (int) $attachment_id;
    53183    }
    54184}
    55 
    56 
    57 function advset__feature__auto_thumbs__generate_post_thumbnail( $matches, $key, $post ) {
    58     // Make sure to assign correct title to the image. Extract it from img tag
    59     $imageTitle = '';
    60     preg_match_all('/<\s*img [^\>]*title\s*=\s*[\""\']?([^\""\'>]*)/i', empty($post->post_content) ? '' : $post->post_content, $matchesTitle);
    61 
    62     if (count($matchesTitle) && isset($matchesTitle[1])) {
    63         $imageTitle = empty($matchesTitle[1][$key]) ? '' : $matchesTitle[1][$key];
    64     }
    65 
    66     // Get the URL now for further processing
    67     $imageUrl = $matches[1][$key];
    68 
    69     // Get the file name
    70     $filename = substr($imageUrl, (strrpos($imageUrl, '/'))+1);
    71 
    72     if ( !(($uploads = wp_upload_dir(current_time('mysql')) ) && false === $uploads['error']) )
    73         return null;
    74 
    75     // Generate unique file name
    76     $filename = wp_unique_filename( $uploads['path'], $filename );
    77 
    78     // Move the file to the uploads dir
    79     $new_file = $uploads['path'] . "/$filename";
    80 
    81     if (!ini_get('allow_url_fopen'))
    82         $file_data = advset__feature__auto_thumbs__curl_get_file_contents($imageUrl);
    83     else
    84         $file_data = @file_get_contents($imageUrl);
    85 
    86     if (!$file_data) {
    87         return null;
    88     }
    89 
    90     file_put_contents($new_file, $file_data);
    91 
    92     // Set correct file permissions
    93     $stat = stat( dirname( $new_file ));
    94     $perms = $stat['mode'] & 0000666;
    95     @ chmod( $new_file, $perms );
    96 
    97     // Get the file type. Must to use it as a post thumbnail.
    98     $wp_filetype = wp_check_filetype( $filename );
    99 
    100     extract( $wp_filetype );
    101 
    102     // No file type! No point to proceed further
    103     if ( ( !$type || !$ext ) && !current_user_can( 'unfiltered_upload' ) ) {
    104         return null;
    105     }
    106 
    107     // Compute the URL
    108     $url = $uploads['url'] . "/$filename";
    109 
    110     // Construct the attachment array
    111     $attachment = array(
    112         'post_mime_type' => $type,
    113         'guid' => $url,
    114         'post_parent' => null,
    115         'post_title' => $imageTitle,
    116         'post_content' => '',
    117     );
    118 
    119     $thumb_id = wp_insert_attachment($attachment, false, $post->ID);
    120     if ( !is_wp_error($thumb_id) ) {
    121         require_once(ABSPATH . '/wp-admin/includes/image.php');
    122 
    123         // Added fix by misthero as suggested
    124         wp_update_attachment_metadata( $thumb_id, wp_generate_attachment_metadata( $thumb_id, $new_file ) );
    125         update_attached_file( $thumb_id, $new_file );
    126 
    127         return $thumb_id;
    128     }
    129 
    130     return null;
    131 }
    132 
    133 function advset__feature__auto_thumbs__curl_get_file_contents($URL) {
    134     $c = curl_init();
    135     curl_setopt($c, CURLOPT_RETURNTRANSFER, 1);
    136     curl_setopt($c, CURLOPT_URL, $URL);
    137     $contents = curl_exec($c);
    138     curl_close($c);
    139 
    140     if ($contents) {
    141         return $contents;
    142     }
    143 
    144     return FALSE;
    145 }
  • advanced-settings/tags/3.2.0/readme.txt

    r3323109 r3357461  
    88Requires at least: 5.0.0
    99Tested up to: 6.8
    10 Stable tag: 3.1.1
     10Stable tag: 3.2.0
    1111License: GPLv3 or later
    1212License URI: http://www.gnu.org/licenses/gpl-3.0.html
     
    2222[→ details in FAQ](https://wordpress.org/plugins/advanced-settings#will%20the%20plugin%20become%20slower%20with%20more%20features%3F)
    2323
     24**🪶 LIGHTWEIGHT**
     25Advanced Settings 3 is lightweight (only about 0.5 MB) and discreet (no dashboard hijacking).
     26[→ details in FAQ](https://wordpress.org/plugins/advanced-settings#how%20can%20the%20plugin%20be%20lightweight%20despite%20so%20many%20features%3F)
     27
    2428**🔒 SECURITY**
    2529Advanced Settings 3 has been independently reviewed for security vulnerabilities via Patchstack.
     
    2731
    2832**✳️ INFO ABOUT THE 2 BAD RATINGS**
    29 2 bad ratings occurred in 2017 because outdated PHP versions were used, but can't happen again.
     33Bad ratings occurred in 2017 because users used outdated PHP versions, but can't happen again.
    3034[→ details in FAQ](https://wordpress.org/plugins/advanced-settings#what%20caused%20the%20two%20bad%20ratings%3F)
    3135
     
    9094
    9195* Display SQL queries and page load time
     96* Create custom post types 💥 renewed
    9297
    9398= Configuration =
     
    118123Advanced Settings 3 works as follows: Only when you open the plugin's settings window does the plugin recognise which settings are actually available. When saving, only the activated settings are written to a separate cache file. Only this cache file is then taken into account during operation. This means that slowdowns can only occur due to **active** features. However, as some features deactivate WordPress functions, the plugin can actually make WordPress even faster than in the standard configuration.
    119124
     125= How can the plugin be lightweight despite so many features? =
     126
     127**Short answer:**
     128Typically, graphical user interfaces represent the largest chunk of data. Since we have largely standardized the user interface for all features, additional features are of little importance.
     129
     130**Detailed answer:**
     131The plugin is currently only 0.5 MB in size. Only about a third of that is used for the actual functionality of the features. The rest is used for the graphical user interface, backward compatibility, performance optimization, and the like. So if we doubled the number of features, the plugin would still be smaller than 0.7 MB. Theoretically, we could quadruple the number of features and still stay under 1 MB. So the number of features isn't crucial. The trick is to standardize everything around the features so that each additional feature has minimal impact.
     132
     133We are passionate about lightweight plugins that remain politely in the background while you work, so we can assure you that this will continue to be the case.
     134
    120135= How is the security of the plugin ensured? =
    121136
     
    152167== Changelog ==
    153168
     169= 3.2.0 - 2025-09-07 =
     170* Fixed a medium priority security issue which appeared on activated feature "Automatically generate the Post Thumbnail"
     171* Thoroughly revised feature "Automatically generate the Post Thumbnail"
     172* Fixed a low priority security issue which appeared on activated experimental feature "Post types settings" (now "Custom post types")
     173* Thoroughly revised feature "Post types settings", renamed it to "Custom post types" and removed the "experimental" flag
     174
    154175= 3.1.1 - 2025-07-06 =
    155176* Fixed minor cache problem
     
    161182= 3.0.2 - 2025-06-04 =
    162183* Fixed order of features
    163 * Fixed a minor security issue in old code (rated as low priority by [Patchstack](https://patchstack.com/))
     184* Fixed a low priority security issue which appeared on activated experimental feature "Filters and actions settings"
    164185
    165186= 3.0.1 - 2025-05-03 =
  • advanced-settings/trunk/admin-ui/admin-ui.css

    r3323103 r3357461  
    128128    height: 80vh;
    129129    background-color: #fff;
    130     overflow: hidden;
     130    overflow: visible;
    131131}
    132132
     
    149149    flex-direction: column;
    150150    height: 100%;
     151    border-radius: .5em;
     152    overflow: hidden;
    151153}
    152154
     
    248250    flex-direction: column;
    249251    height: 100%;
     252}
     253
     254.advset-modal-madeby {
     255    padding: 0;
     256    display: flex;
     257    gap: 1em;
     258    position: absolute;
     259    top: 100%;
     260    right: 0px;
     261    left: 0px;
     262    color: #fff;
     263    opacity: .8;
     264    justify-content: center;
     265    margin: 0 auto;
     266    width: fit-content;
     267}
     268
     269.advset-modal-madeby img {
     270    flex-shrink: 0;
     271    flex-grow: 0;
     272    width: 50px;
     273    height: auto;
     274}
     275
     276.advset-modal-madeby p {
     277    font-size: var(--advset-font-size-small);
     278}
     279
     280.advset-modal-madeby a {
     281    color: inherit !important;
    250282}
    251283
  • advanced-settings/trunk/admin-ui/admin-ui.php

    r3323109 r3357461  
    207207            </div>
    208208        </div>
     209        <div class="advset-modal-madeby">
     210            <img src="<?php echo plugins_url('', ADVSET_FILE); ?>/admin-ui/images/wppeak.com-logo-vectorized-white.svg" alt="Advanced Settings">
     211            <p>
     212                Advanced Settings is made by <a href="https://www.wppeak.com/" target="_blank" rel="noopener noreferrer">wppeak.com</a>.<br />
     213                We make plugins and other useful WordPress tools.
     214            </p>
     215        </div>
    209216    </dialog>
    210217    <?php
  • advanced-settings/trunk/advanced-settings.php

    r3323109 r3357461  
    66Author: Helmut Wandl
    77Author URI: https://ehtmlu.com/
    8 Version: 3.1.1
     8Version: 3.2.0
    99Requires at least: 5.0.0
    1010Requires PHP: 7.4
  • advanced-settings/trunk/feature-setup/features/developer.php

    r3323103 r3357461  
    135135    'id' => 'developer.settings_pages.post_types',
    136136    'category' => 'developer',
    137     'experimental' => true,
    138137    'ui_config' => fn() => [
    139138        'tags' => [
     
    145144            'enable' => [
    146145                'type' => 'toggle',
    147                 'label' => __('Post types settings', 'advanced-settings'),
    148                 'description' => __('These post types settings are currently under review and may be changed or removed in the future.', 'advanced-settings'),
     146                'label' => __('Custom post types', 'advanced-settings'),
    149147            ],
    150148            'info' => [
     
    157155    'execution_handler' => function() {
    158156        require_once ADVSET_DIR.'/feature-setup/features/includes/developer.settings_pages.php';
    159 
    160         add_action('init', function() {
    161 
    162             $post_types = (array) get_option( 'advset_post_types', array() );
    163        
    164             if( is_admin() && current_user_can('manage_options') && isset($_GET['delete_posttype']) ) {
    165                 unset($post_types[$_GET['delete_posttype']]);
    166                 update_option( 'advset_post_types', $post_types );
    167             }
    168        
    169             if( is_admin() && current_user_can('manage_options') && isset($_POST['advset_action_posttype']) ) {
    170        
    171                 extract($_POST);
    172        
    173                 $labels = array(
    174                     'name' => $label,
    175                     #'singular_name' => @$singular_name,
    176                     #'add_new' => @$add_new,
    177                     #'add_new_item' => @$add_new_item,
    178                     #'edit_item' => @$edit_item,
    179                     #'new_item' => @$new_item,
    180                     #'all_items' => @$all_items,
    181                     #'view_item' => @$view_item,
    182                     #'search_items' => @$search_items,
    183                     #'not_found' =>  @$not_found,
    184                     #'not_found_in_trash' => @$not_found_in_trash,
    185                     #'parent_item_colon' => @$parent_item_colon,
    186                     #'menu_name' => @$menu_name
    187                 );
    188        
    189                 $typename = sanitize_key( $type );
    190        
    191                 $post_types[$type] = array(
    192                     'labels'              => $labels,
    193                     'public'              => (bool) (isset($public) ? $public : false),
    194                     'publicly_queryable'  => (bool) (isset($publicly_queryable) ? $publicly_queryable : false),
    195                     'show_ui'             => (bool) (isset($show_ui) ? $show_ui : false),
    196                     'show_in_menu'        => (bool) (isset($show_in_menu) ? $show_in_menu : false),
    197                     'query_var'           => (bool) (isset($query_var) ? $query_var : false),
    198                     #'rewrite'             => array( 'slug' => 'book' ),
    199                     #'capability_type'     => 'post',
    200                     'has_archive'         => (bool) (isset($has_archive) ? $has_archive : false),
    201                     'hierarchical'        => (bool) (isset($hierarchical) ? $hierarchical : false),
    202                     #'menu_position'       => (int)@$menu_position,
    203                     'supports'            => (array) (empty($supports) ? [] : $supports),
    204                     'taxonomies'          => (array) (empty($taxonomies) ? [] : $taxonomies),
    205                 );
    206        
    207                 update_option( 'advset_post_types', $post_types );
    208        
    209             }
    210             #print_r($post_types);
    211             if( sizeof($post_types)>0 )
    212                 foreach( $post_types as $post_type=>$args ) {
    213                     register_post_type( $post_type, $args );
    214                     if( in_array( 'thumbnail', $args['supports'] ) ) {
    215                         add_theme_support( 'post-thumbnails', array( $post_type, 'post' ) );
    216                     }
    217                 }
    218        
    219         });
    220 
    221         add_action('admin_menu', function() {
    222             add_options_page(
    223                 __('Post Types', 'advanced-settings'),
    224                 __('Post Types', 'advanced-settings'),
    225                 'manage_options',
    226                 'advanced-settings-post-types',
    227                 function() {
    228                     include ADVSET_DIR.'/feature-setup/features/includes/developer.settings_pages.post_types--admin-post-types.php';
    229                 }
    230             );
    231         });
     157        require_once ADVSET_DIR.'/feature-setup/features/includes/developer.settings_pages.post_types--init.php';
     158        Advset__Feature__Post_Types::init();
    232159    },
    233160    'priority' => 10,
  • advanced-settings/trunk/feature-setup/features/editing.php

    r3323103 r3357461  
    355355    ],
    356356    'execution_handler' => function() {
    357         require_once ADVSET_DIR . '/feature-setup/features/includes/frontend.auto_thumbs.php';
    358         add_action('transition_post_status', 'advset__feature__auto_thumbs', 10, 3);
     357        add_action('transition_post_status', function($new_status, $old_status, $post) {
     358            if ($new_status !== 'publish') {
     359                return;
     360            }
     361            require_once ADVSET_DIR . '/feature-setup/features/includes/frontend.auto_thumbs.php';
     362            Advset__Feature__Auto_Thumbs::init()->transition_post_status($new_status, $old_status, $post);
     363        }, 10, 3);
    359364    },
    360365    'priority' => 70,
  • advanced-settings/trunk/feature-setup/features/includes/developer.settings_pages.post_types--admin-post-types.php

    r3286786 r3357461  
    1 <?php defined('ABSPATH') or exit;
    2 
    3     global $_wp_post_type_features;
    4 
    5     $post_types = get_post_types( '', 'objects' );
    6 
    7     unset( $post_types['attachment'], $post_types['revision'], $post_types['nav_menu_item'] );
    8 
    9     $advset_post_types = (array) get_option( 'advset_post_types', array() );
    10 
    11     ?>
    12     <script>
    13     <?php echo 'posttype_data='.json_encode($post_types).';'; ?>
    14     <?php echo 'supports='.json_encode($_wp_post_type_features).';'; ?>
    15     show_form=function(type) {
    16 
    17         $=jQuery;
    18 
    19         if( type ) {
    20 
    21             data = posttype_data[type];
    22 
    23             data.type=type;
    24             populate( '#posttype_form', data );
    25 
    26             var checks = $('[name=supports\\[\\]]', '#posttype_form');
    27             for( i=0; i<checks.length;i++ )
    28                 if( supports[type][$(checks[i]).attr('value')] )
    29                     $(checks[i]).attr("checked", "checked");
    30                 else
    31                     $(checks[i]).attr("checked", null);
    32 
    33             var checks = $('[name=taxonomies\\[\\]]', '#posttype_form');
    34             for( i=0; i<checks.length;i++ )
    35                 if( data['taxonomies'].join(',').indexOf($(checks[i]).attr('value'))>-1 )
    36                     $(checks[i]).attr("checked", "checked");
    37                 else
    38                     $(checks[i]).attr("checked", null);
    39 
    40         }
    41 
    42         $('#post_type_list').hide();
    43         $('#post_type_form').fadeIn();
    44         //$('#post_type_form').reset(); // dont works
    45         $('#namefield').focus();
     1<?php
     2
     3if (!defined('ABSPATH')) exit;
     4
     5
     6$advset_posttype_nonce = wp_create_nonce('advset_posttype_nonce');
     7
     8$advset_posttypes_data = get_option('advset_post_types', []);
     9
     10$advset_posttypes_data_default = [
     11    'supports' => [
     12        'title',
     13        'editor',
     14    ],
     15    'public' => true,
     16    'publicly_queryable' => true,
     17    'show_ui' => true,
     18    'show_in_menu' => true,
     19    'query_var' => true,
     20    'has_archive' => false,
     21    'hierarchical' => false,
     22    'taxonomies' => [
     23        'category',
     24        'post_tag',
     25    ],
     26];
     27
     28
     29
     30
     31?>
     32<script>
     33
     34document.addEventListener('DOMContentLoaded', function() {
     35    const formContainerElement = document.getElementById('advset_posttype_form_container');
     36    const listContainerElement = document.getElementById('advset_posttype_list_container');
     37    const formElement = formContainerElement?.querySelector('#advset_posttype_form');
     38    const labelInputElement = formContainerElement?.querySelector('[name="label"]');
     39    const typeInputElement = formContainerElement?.querySelector('[name="type"]');
     40    const typeStoredInputElement = formContainerElement?.querySelector('[name="type_stored"]');
     41    const typeAvailableIndicatorElement = formContainerElement?.querySelector('#advset_type_available_indicator');
     42    const submitButtonElement = formContainerElement?.querySelector('[type="submit"]');
     43
     44    const posttypes_data = <?php echo json_encode($advset_posttypes_data); ?>;
     45    const posttypes_data_default = <?php echo json_encode($advset_posttypes_data_default); ?>;
     46
     47    document.addEventListener('click', function(event) {
     48        if (event.target.closest('.advset-posttype-edit-link')) {
     49            event.preventDefault();
     50            showForm(event.target.getAttribute('data-type'));
     51        }
     52        if (event.target.closest('.advset-posttype-cancel-link')) {
     53            event.preventDefault();
     54            showList();
     55        }
     56        if (event.target.closest('.advset-posttype-delete-link')) {
     57            event.preventDefault();
     58            if (confirm('<?php _e('Are you sure you want to delete this post type?') ?>')) {
     59                advset_submit_delete_posttype(event.target.getAttribute('data-type'));
     60            }
     61        }
     62    });
     63
     64    labelInputElement.addEventListener('blur', function() {
     65        if (typeInputElement.value === '') {
     66            checkType(true);
     67        }
     68    });
     69
     70    typeInputElement.addEventListener('input', function() {
     71        checkType();
     72    });
     73
     74    function checkType(generate_from_label = false) {
     75        setTypeValidity(null);
     76        if (!generate_from_label && typeInputElement.value === '') {
     77            return;
     78        }
     79        fetch('<?php echo rest_url('advset_posttypes/v1/check-type'); ?>', {
     80            method: 'POST',
     81            body: JSON.stringify({
     82                label_input: labelInputElement.value,
     83                type_input: typeInputElement.value,
     84                type_stored: typeStoredInputElement.value,
     85                generate_from_label,
     86            }),
     87            headers: {
     88                'Content-Type': 'application/json',
     89                'X-WP-Nonce': '<?php echo wp_create_nonce('wp_rest'); ?>',
     90            },
     91        })
     92        .then(response => response.json())
     93        .then(data => {
     94            if (generate_from_label && data.type_available) {
     95                typeInputElement.value = data.type_available;
     96                setTypeValidity(true);
     97            }
     98            else if (data.is_taken) {
     99                setTypeValidity(false);
     100            }
     101            else if (data.is_valid) {
     102                setTypeValidity(true);
     103            }
     104        });
     105    }
     106
     107    function setTypeValidity(validity) {
     108        let color, icon, text;
     109        switch (validity) {
     110            case true:
     111                color = 'green';
     112                icon = '✅';
     113                text = 'Type is available';
     114                break;
     115            case false:
     116                color = 'red';
     117                icon = '❌';
     118                text = 'Type already exists';
     119                break;
     120            default:
     121                color = 'gray';
     122                icon = '';
     123                text = '';
     124                break;
     125        }
     126        typeInputElement.setCustomValidity(validity === false ? text : '');
     127        typeAvailableIndicatorElement.textContent = icon + ' ' + text;
     128        typeAvailableIndicatorElement.style.color = color;
     129    }
     130
     131    function showForm(type) {
     132
     133        const data = type ? posttypes_data[type] : posttypes_data_default;
     134        data.label = data?.labels?.name ?? type;
     135        data.type = type ?? '';
     136        data.type_stored = type ?? '';
     137
     138        formElement.querySelectorAll('[name]').forEach(element => {
     139            if (element.name.slice(0, 1) === '_') {
     140                return;
     141            }
     142           
     143            if (element.type === 'checkbox') {
     144                if (element.name.slice(-2) === '[]') {
     145                    element.checked = data[element.name.slice(0, -2)]?.includes(element.value) ?? element.defaultChecked;
     146                } else {
     147                    element.checked = data[element.name] ?? element.defaultChecked;
     148                }
     149            } else if (['text', 'hidden', 'textarea'].includes(element.type)) {
     150                element.value = data[element.name] ?? element.defaultValue;
     151            }
     152        });
     153
     154        submitButtonElement.value = type ? submitButtonElement.dataset.saveChanges : submitButtonElement.dataset.create;
     155
     156        setTypeValidity(null);
     157
     158        listContainerElement.style.display = 'none';
     159        formContainerElement.style.display = '';
     160        labelInputElement.focus();
     161    }
     162
     163    function showList() {
     164        formContainerElement.style.display = 'none';
     165        listContainerElement.style.display = '';
     166    };
     167
     168    // Submit delete via hidden POST form
     169    function advset_submit_delete_posttype(slug) {
     170        const form = document.getElementById('advset_delete_posttype_form');
     171        const input = document.getElementById('advset_delete_posttype_slug');
     172        if (!slug || !form || !input) return false;
     173        input.value = slug;
     174        form.submit();
    46175        return false;
    47176    };
    48     populate=function (frm, data) {
    49         $=jQuery;
    50         $.each(data, function(key, value){
    51             var $ctrl = $('[name='+key+']', frm);
    52             switch($ctrl.attr("type")) {
    53                 case "text" :
    54                 case "hidden":
    55                 case "textarea":
    56                 $ctrl.val(value);
    57                 break;
    58                 case "radio" : case "checkbox":
    59                 $ctrl.each(function(){
    60                     if( $(this).attr('value') == value
    61                         || $(this).attr('name')=='query_var' && $(this).attr('value')!='' )
    62                         $(this).attr("checked",value);
    63                     else
    64                         $(this).attr("checked",null);
    65                 });
    66                 break;
    67             }
    68         });
    69     };
    70     show_list=function() {
    71         $('#post_type_form').hide();
    72         $('#post_type_list').fadeIn();
    73     };
    74     str2slug=function(str) {
    75         var new_str = '';
    76         str=str.toLowerCase();
    77         str=str.replace(/[aáàãâä]/g,'a');
    78         str=str.replace(/[éèêë]/g,'e');
    79         str=str.replace(/[íìîï]/g,'i');
    80         str=str.replace(/[óòõôö]/g,'o');
    81         str=str.replace(/[úùûü]/g,'u');
    82         str=str.replace('ç','c');
    83         str=str.replace(/[^a-z0-9_\-]/g,'');
    84         str=str.substring(0, 20);
    85         return str;
    86     };
    87     function in_array(needle, haystack) {
    88         var length = haystack.length;
    89         for(var i = 0; i < length; i++) {
    90             if(haystack[i] == needle) return true;
    91         }
    92         return false;
    93     }
    94     </script>
    95 
    96     <div class="wrap">
    97             <?php advset_page_header() ?>
    98             <br />
    99             <?php echo advset_page_experimental(); ?>
    100             <br />
    101             <br />
    102 
    103 
    104             <div style="padding: 1rem; background: #fff; border: #f00 solid 2px; border-radius: .5rem; ">
    105                 <h3 style="margin: 0; ">WARNING</h3>
    106                 <p style="margin: 0; "><?php _e('Be careful, changing a post type can significantly impact the functionality of the website.') ?></p>
    107             </div>
    108             <br />
    109             <a href="#" onclick="return show_form();" class="add-new-h2">Add New Post Type</a>
    110             <br />
    111             <br />
    112 
    113         <div id="post_type_form" style="display:none">
    114 
    115             <h3>Add New Post Type</h3>
    116 
    117             <form id="posttype_form" action="" method="post">
    118                 <?php #settings_fields( 'advanced-settings-post-types' ); ?>
    119 
    120                 <input type="hidden" name="advset_action_posttype" value="1" />
    121 
    122                 <table class="form-table">
    123                     <tr valign="top">
    124                         <th scope="row"><?php _e('Label'); ?></th>
    125                         <td>
    126                             <input id="namefield" name="label" type="text" value="" onblur="
    127                             const input = document.getElementById('typefield');
    128                             if( input.value === '' )
    129                                 input.value = str2slug(this.value);
    130                             " />
    131 
    132                             <!--p><a href="#">+ show more labels</a></p-->
    133 
    134                         </td>
    135                     </tr>
    136 
    137                     <tr valign="top">
    138                         <th scope="row"><?php _e('Type Name'); ?></th>
    139                         <td>
    140                             <input id="typefield" name="type" type="text" value="" pattern="^[a-z0-9_\-]{1,20}$" required /><br />
    141                             <i style="color:#999">Post type names must be between 1 and 20 characters in length. Lowercase alphanumeric characters, dashes, and underscores are allowed. </i>
    142                         </td>
    143                     </tr>
    144 
    145                     <tr valign="top">
    146                         <th scope="row"><?php _e('Supports'); ?></th>
    147                         <td>
    148                             <input name="supports[]" id="posttype-support-title" value="title" type="checkbox" checked="checked">
    149                             <label for="posttype-support-title">title</label><br />
    150                             <input name="supports[]" id="posttype-support-editor" value="editor" type="checkbox" checked="checked">
    151                             <label for="posttype-support-editor">editor</label><br />
    152                             <input name="supports[]" id="posttype-support-author" value="author" type="checkbox">
    153                             <label for="posttype-support-author">author</label><br />
    154                             <input name="supports[]" id="posttype-support-thumbnail" value="thumbnail" type="checkbox">
    155                             <label for="posttype-support-thumbnail">thumbnail</label><br />
    156                             <input name="supports[]" id="posttype-support-excerpt" value="excerpt" type="checkbox">
    157                             <label for="posttype-support-excerpt">excerpt</label><br />
    158                             <input name="supports[]" id="posttype-support-trackbacks" value="trackbacks" type="checkbox">
    159                             <label for="posttype-support-trackbacks">trackbacks</label><br />
    160                             <input name="supports[]" id="posttype-support-custom-fields" value="custom-fields" type="checkbox">
    161                             <label for="posttype-support-custom-fields">custom fields</label><br />
    162                             <input name="supports[]" id="posttype-support-comments" value="comments" type="checkbox">
    163                             <label for="posttype-support-comments">comments</label><br />
    164                             <input name="supports[]" id="posttype-support-revisions" value="revisions" type="checkbox">
    165                             <label for="posttype-support-revisions">revisions</label> <br />
    166                             <input name="supports[]" id="posttype-support-page-attributes" value="page-attributes" type="checkbox">
    167                             <label for="posttype-support-page-attributes">page attributes</label>
    168                         </td>
    169                     </tr>
    170 
    171                     <tr valign="top">
    172                         <th scope="row"><?php _e('Settings'); ?></th>
    173                         <td>
    174                             <label><input name="public" value="1" type="checkbox" checked="checked">
    175                                 public</label><br />
    176                             <label><input name="publicly_queryable" value="1" type="checkbox" checked="checked">
    177                                 publicly_queryable</label><br />
    178                             <label><input name="show_ui" value="1" type="checkbox" checked="checked">
    179                                 show_ui</label><br />
    180                             <label><input name="show_in_menu" value="1" type="checkbox" checked="checked">
    181                                 show_in_menu</label><br />
    182                             <label><input name="query_var" value="1" type="checkbox" checked="checked">
    183                                 query_var</label><br />
    184                             <!--label><input name="rewrite" value="1" type="checkbox">
    185                                 rewrite</label><br /-->
    186                             <!--label><input name="capability_type" value="1" type="checkbox">
    187                                 capability_type</label><br /-->
    188                             <label><input name="has_archive" value="1" type="checkbox">
    189                                 has_archive</label><br />
    190                             <label><input name="hierarchical" value="1" type="checkbox">
    191                                 hierarchical</label> <br />
    192                             <!--label><input name="menu_position" value="1" type="checkbox">
    193                                 menu_position</label-->
    194                         </td>
    195                     </tr>
    196 
    197                     <tr valign="top">
    198                         <th scope="row"><?php _e('Taxonomies'); ?></th>
    199                         <td>
    200                             <label><input name="taxonomies[]" value="category" type="checkbox" checked="checked">
    201                                 category</label><br />
    202                             <label><input name="taxonomies[]" value="post_tag" type="checkbox" checked="checked">
    203                                 post_tag</label>
    204                         </td>
    205                     </tr>
    206 
    207                 </table>
    208                 <p class="submit">
    209                     <input type="submit" name="submit" id="submit" class="button button-primary" value="<?php _e('Save Changes') ?>" />
    210                     <input type="button" onclick="show_list();" class="button" value="<?php _e('Cancel') ?>" />
    211                 </p>
    212             </form>
    213         </div>
    214 
    215         <form id="post_type_list" action="options.php" method="post">
    216 
    217             <table class="widefat fixed" cellspacing="0">
     177});
     178</script>
     179
     180<div class="wrap">
     181    <?php advset_page_header() ?>
     182    <br />
     183    <!-- <?php echo advset_page_experimental(); ?>
     184    <br />
     185    <br /> -->
     186
     187
     188    <!-- <div style="padding: 1rem; background: #fff; border: #f00 solid 2px; border-radius: .5rem; ">
     189        <h3 style="margin: 0; ">WARNING</h3>
     190        <p style="margin: 0; "><?php _e('Be careful, changing a post type can significantly impact the functionality of the website.') ?></p>
     191    </div>
     192    <br /> -->
     193    <a class="advset-posttype-edit-link add-new-h2" href="#" data-type=""><?php _e('Add') ?></a>
     194    <br />
     195    <br />
     196
     197    <div id="advset_posttype_form_container" style="display:none">
     198
     199        <h3><?php _e('Add') ?></h3>
     200
     201        <form id="advset_posttype_form" action="" method="post">
     202            <?php #settings_fields( 'advanced-settings-post-types' ); ?>
     203
     204            <input type="hidden" name="_advset_posttype_nonce" value="<?php echo $advset_posttype_nonce; ?>" />
     205
     206            <input type="hidden" name="_advset_posttype_action_save" value="1" />
     207
     208            <input type="hidden" name="type_stored" value="" />
     209
     210            <table class="form-table">
     211                <tr valign="top">
     212                    <th scope="row"><?php _e('Label'); ?></th>
     213                    <td>
     214                        <input name="label" type="text" value="" />
     215
     216                        <!--p><a href="#">+ show more labels</a></p-->
     217
     218                    </td>
     219                </tr>
     220
     221                <tr valign="top">
     222                    <th scope="row"><?php _e('Type Name'); ?></th>
     223                    <td>
     224                        <input name="type" type="text" value="" pattern="^[a-z0-9_\-]{1,20}$" required /> <span id="advset_type_available_indicator"></span><br />
     225                        <i style="color:#999">Post type names must be between 1 and 20 characters in length. Lowercase alphanumeric characters, dashes, and underscores are allowed. </i>
     226                    </td>
     227                </tr>
     228
     229                <tr valign="top">
     230                    <th scope="row"><?php _e('Description'); ?></th>
     231                    <td>
     232                        <!-- <textarea name="description" rows="3" style="width: 100%;"></textarea> -->
     233                            <input name="description" type="text" value="" />
     234                    </td>
     235                </tr>
     236
     237                <tr valign="top">
     238                    <th scope="row"><?php _e('Supports'); ?></th>
     239                    <td>
     240                        <input name="supports[]" id="posttype-support-title" value="title" type="checkbox">
     241                        <label for="posttype-support-title">title</label><br />
     242                        <input name="supports[]" id="posttype-support-editor" value="editor" type="checkbox">
     243                        <label for="posttype-support-editor">editor</label><br />
     244                        <input name="supports[]" id="posttype-support-author" value="author" type="checkbox">
     245                        <label for="posttype-support-author">author</label><br />
     246                        <input name="supports[]" id="posttype-support-thumbnail" value="thumbnail" type="checkbox">
     247                        <label for="posttype-support-thumbnail">thumbnail</label><br />
     248                        <input name="supports[]" id="posttype-support-excerpt" value="excerpt" type="checkbox">
     249                        <label for="posttype-support-excerpt">excerpt</label><br />
     250                        <input name="supports[]" id="posttype-support-trackbacks" value="trackbacks" type="checkbox">
     251                        <label for="posttype-support-trackbacks">trackbacks</label><br />
     252                        <input name="supports[]" id="posttype-support-custom-fields" value="custom-fields" type="checkbox">
     253                        <label for="posttype-support-custom-fields">custom fields</label><br />
     254                        <input name="supports[]" id="posttype-support-comments" value="comments" type="checkbox">
     255                        <label for="posttype-support-comments">comments</label><br />
     256                        <input name="supports[]" id="posttype-support-revisions" value="revisions" type="checkbox">
     257                        <label for="posttype-support-revisions">revisions</label> <br />
     258                        <input name="supports[]" id="posttype-support-page-attributes" value="page-attributes" type="checkbox">
     259                        <label for="posttype-support-page-attributes">page attributes</label>
     260                    </td>
     261                </tr>
     262
     263                <tr valign="top">
     264                    <th scope="row"><?php _e('Settings'); ?></th>
     265                    <td>
     266                        <label><input name="public" value="1" type="checkbox">
     267                            public</label><br />
     268                        <label><input name="publicly_queryable" value="1" type="checkbox">
     269                            publicly_queryable</label><br />
     270                        <label><input name="show_ui" value="1" type="checkbox">
     271                            show_ui</label><br />
     272                        <label><input name="show_in_menu" value="1" type="checkbox">
     273                            show_in_menu</label><br />
     274                        <label><input name="query_var" value="1" type="checkbox">
     275                            query_var</label><br />
     276                        <!--label><input name="rewrite" value="1" type="checkbox">
     277                            rewrite</label><br /-->
     278                        <!--label><input name="capability_type" value="1" type="checkbox">
     279                            capability_type</label><br /-->
     280                        <label><input name="has_archive" value="1" type="checkbox">
     281                            has_archive</label><br />
     282                        <label><input name="hierarchical" value="1" type="checkbox">
     283                            hierarchical</label> <br />
     284                        <!--label><input name="menu_position" value="1" type="checkbox">
     285                            menu_position</label-->
     286                    </td>
     287                </tr>
     288
     289                <tr valign="top">
     290                    <th scope="row"><?php _e('Taxonomies'); ?></th>
     291                    <td>
     292                        <label><input name="taxonomies[]" value="category" type="checkbox">
     293                            category</label><br />
     294                        <label><input name="taxonomies[]" value="post_tag" type="checkbox">
     295                            post_tag</label>
     296                    </td>
     297                </tr>
     298
     299            </table>
     300            <p class="submit">
     301                <input type="submit" id="submit" class="button button-primary" value="<?php _e('Save Changes') ?>" data-save-changes="<?php _e('Save Changes') ?>" data-create="<?php _e('Create') ?>" />
     302                <input type="button" class="button advset-posttype-cancel-link" value="<?php _e('Cancel') ?>" />
     303            </p>
     304        </form>
     305    </div>
     306
     307    <div id="advset_posttype_list_container">
     308        <?php if (!empty($advset_posttypes_data)) { ?>
     309        <form id="advset_posttype_list" action="options.php" method="post">
     310
     311            <table class="widefat fixed striped" cellspacing="0">
    218312                <thead>
    219313                    <tr>
    220                         <th scope="col" id="cb" class="manage-column column-cb check-column" style=""><label class="screen-reader-text" for="cb-select-all-1">Selecionar Tudo</label><input id="cb-select-all-1" type="checkbox"></th>
    221                         <th scope="col" id="title" class="manage-column column-title" width="40%">Label <small>(Menu name)</small></th>
    222                         <th scope="col" id="type_name" class="manage-column column-title" width="30%">Type</th>
    223                         <th scope="col" id="type_desc" class="manage-column column-title" width="30%">Description</th>
     314                        <th scope="col" id="title" class="manage-column column-title" width="40%"><?php _e('Label') ?></th>
     315                        <th scope="col" id="type_name" class="manage-column column-title" width="30%"><?php _e('Type') ?></th>
     316                        <th scope="col" id="type_desc" class="manage-column column-title" width="30%"><?php _e('Description') ?></th>
    224317                    </tr>
    225318                </thead>
    226                 <?php foreach($post_types as $typename=>$post_type) { ?>
     319                <?php foreach($advset_posttypes_data as $typename => $post_type) { ?>
    227320                <tr class=" iedit">
    228                     <th scope="row" class="check-column">
    229 
    230                         <input id="cb-select-1" type="checkbox" name="post[]" value="1">
    231                         <div class="locked-indicator"></div>
    232                     </th>
    233                     <td>
    234                         <strong><?php echo $post_type->label ?></strong>
     321                    <td>
     322                        <strong><?php echo $post_type['labels']['name'] ?></strong>
    235323                        <div class="row-actions">
    236                             <?php if( !in_array( $post_type->name, array('post', 'page') ) ) { ?>
    237324                        <span class="edit">
    238                             <a href="#" onclick="show_form('<?php echo $post_type->name ?>');">Edit</a>
     325                            <a class="advset-posttype-edit-link" href="#" data-type="<?php echo esc_attr($typename) ?>"><?php _e('Edit') ?></a>
     326                            |
    239327                        </span>
    240                                 | <a href="options-general.php?page=advanced-settings-post-types&delete_posttype=<?php echo $post_type->name ?>" title="default categories">delete</a>
    241                             <?php } else echo '&nbsp;'; ?>
    242 
     328                        <span class="trash">
     329                            <a class="advset-posttype-delete-link" href="#" data-type="<?php echo esc_attr($typename) ?>"><?php _e('Delete') ?></a>
     330                        </span>
    243331                        </div>
    244332                    </td>
    245                     <td><?php echo $post_type->name ?></td>
    246                     <td></td>
     333                    <td><?php echo $typename ?></td>
     334                    <td><?php echo str_replace("\n", '<br />', strip_tags($post_type['description'] ?? '')) ?></td>
    247335                </tr>
    248336                <?php } ?>
     
    250338                <tfoot>
    251339                    <tr>
    252                         <th scope="col" class="manage-column column-cb check-column" style=""><label class="screen-reader-text" for="cb-select-all-2">Selecionar Tudo</label><input id="cb-select-all-2" type="checkbox"></th>
    253                         <th scope="col" id="title" class="manage-column column-title" width="40%">Label <small>(Menu name)</small></th>
    254                         <th scope="col" id="type_name" class="manage-column column-title" width="30%">Type</th>
    255                         <th scope="col" id="type_desc" class="manage-column column-title" width="30%">Description</th>
     340                        <th scope="col" id="title" class="manage-column column-title" width="40%"><?php _e('Label') ?></th>
     341                        <th scope="col" id="type_name" class="manage-column column-title" width="30%"><?php _e('Type') ?></th>
     342                        <th scope="col" id="type_desc" class="manage-column column-title" width="30%"><?php _e('Description') ?></th>
    256343                    </tr>
    257344                </tfoot>
    258345
    259346            </table>
    260 
    261             <!--p class="submit"><input type="submit" name="submit" id="submit" class="button-primary" value="<?php _e('Save changes') ?>"></p-->
     347        </form>
     348        <?php } else { ?>
     349        <div>
     350            <p><?php _e('No custom post types found.') ?></p>
     351        </div>
     352        <?php } ?>
     353
     354        <form id="advset_delete_posttype_form" action="<?php echo esc_url( admin_url('options-general.php?page=advanced-settings-post-types') ); ?>" method="post" style="display: none;">
     355            <input type="hidden" name="_advset_posttype_nonce" value="<?php echo esc_attr($advset_posttype_nonce); ?>" />
     356            <input type="hidden" name="_advset_posttype_action_delete" id="advset_delete_posttype_slug" value="" />
    262357        </form>
    263358    </div>
     359</div>
  • advanced-settings/trunk/feature-setup/features/includes/frontend.auto_thumbs.php

    r3281733 r3357461  
    11<?php
    22
    3 if( !defined('ABSPATH') ) exit;
     3if (!defined('ABSPATH')) exit;
    44
    55
    6 function advset__feature__auto_thumbs( $new_status, $old_status, $post ) {
    7     if ('publish' == $new_status) {
    8         advset__feature__auto_thumbs__publish_post($post);
     6/**
     7 * Auto Thumbs feature class.
     8 */
     9class Advset__Feature__Auto_Thumbs {
     10
     11    private static $instance = null;
     12
     13    private function __construct() {}
     14   
     15    /**
     16     * Initialize the feature.
     17     *
     18     * @return Advset__Feature__Auto_Thumbs The instance of the feature.
     19     */
     20    public static function init() {
     21        if (self::$instance === null) {
     22            self::$instance = new self();
     23        }
     24        return self::$instance;
    925    }
    10 }
     26   
     27    /**
     28     * Transition the post status.
     29     *
     30     * @param string $new_status The new status of the post.
     31     * @param string $old_status The old status of the post.
     32     * @param object $post The post object.
     33     * @return bool True if the post thumbnail was generated, false otherwise.
     34     */
     35    public function transition_post_status( string $new_status, string $old_status, object $post ) {
     36        global $wpdb;
     37       
     38        // If the post is not published, return
     39        if ($new_status !== 'publish') {
     40            return false;
     41        }
    1142
    12 //
    13 function advset__feature__auto_thumbs__publish_post( $post ) {
    14     global $wpdb;
    15 
    16     // First check whether Post Thumbnail is already set for this post.
    17     if (get_post_meta($post->ID, '_thumbnail_id', true) || get_post_meta($post->ID, 'skip_post_thumb', true))
    18         return;
    19 
    20     // Initialize variable used to store list of matched images as per provided regular expression
    21     $matches = array();
    22 
    23     // Get all images from post's body
    24     preg_match_all('/<\s*img [^\>]*src\s*=\s*[\""\']?([^\""\'>]*)[^\>]*/i', empty($post->post_content) ? '' : $post->post_content, $matches);
    25 
    26     if (count($matches)) {
    27         foreach ($matches[0] as $key => $image) {
    28             /**
    29              * If the image is from wordpress's own media gallery, then it appends the thumbmail id to a css class.
    30              * Look for this id in the IMG tag.
    31              */
    32             preg_match('/wp-image-([\d]*)/i', $image, $thumb_id);
    33             $thumb_id = empty($thumb_id[1]) ? null : $thumb_id[1];
    34 
     43        // First check whether Post Thumbnail is already set for this post.
     44        if (get_post_meta($post->ID, '_thumbnail_id', true) || get_post_meta($post->ID, 'skip_post_thumb', true))
     45            return false;
     46   
     47        // Initialize variable used to store the thumbnail id
     48        $thumb_id = null;
     49   
     50        // Initialize variable used to store list of matched images as per provided regular expression
     51        $images = [];
     52   
     53        // Get all images from post's body
     54        preg_match_all('/<\s*img [^\>]*src\s*=\s*[\""\']?([^\""\'>]*)[^\>]*/i', $post->post_content ?? '', $images, PREG_SET_ORDER);
     55   
     56        // If no images are found, return
     57        if (empty($images)) {
     58            return false;
     59        }
     60   
     61        // Loop through all images
     62        foreach ($images as $image) {
     63            $img_html = $image[0];
     64            $image_url = $image[1];
     65   
     66            // If the image is from wordpress's own media gallery, then it appends the thumbmail id to a css class.
     67            preg_match('/\s+class\s*=\s*[\""\']?([^\""\'>]*)/i', $img_html, $matches_class);
     68            preg_match('/(?:^|\s+)wp-image-([\d]*)(?:\s+|$)/i', $matches_class[1] ?? '', $matches_thumb_id);
     69            if (!empty($matches_thumb_id[1]) && (int) $matches_thumb_id[1] > 0) {
     70                $thumb_id = (int) $matches_thumb_id[1];
     71                break;
     72            }
     73   
     74            // Get the image URL
     75            if (!wp_http_validate_url($image_url)) {
     76                continue;
     77            }
     78   
    3579            // If thumb id is not found, try to look for the image in DB. Thanks to "Erwin Vrolijk" for providing this code.
    36             if (!$thumb_id) {
    37                 $image = $matches[1][$key];
    38                 $result = $wpdb->get_results($wpdb->prepare("SELECT ID FROM {$wpdb->posts} WHERE guid = %s", $image));
    39                 $thumb_id = empty($result[0]->ID) ? null : $result[0]->ID;
     80            $attachment = $wpdb->get_results($wpdb->prepare("SELECT ID FROM {$wpdb->posts} WHERE guid = %s", $image_url));
     81            if (!empty($attachment[0]->ID) && (int) $attachment[0]->ID > 0) {
     82                $thumb_id = (int) $attachment[0]->ID;
     83                break;
    4084            }
    41 
     85   
    4286            // Ok. Still no id found. Some other way used to insert the image in post. Now we must fetch the image from URL and do the needful.
    43             if (!$thumb_id) {
    44                 $thumb_id = advset__feature__auto_thumbs__generate_post_thumbnail($matches, $key, $post);
    45             }
    46 
    47             // If we succeed in generating thumg, let's update post meta
    48             if ($thumb_id) {
    49                 update_post_meta( $post->ID, '_thumbnail_id', $thumb_id );
     87            preg_match('/\s+alt\s*=\s*[\""\']?([^\""\'>]*)/i', $img_html, $matches_alt);
     88            preg_match('/\s+title\s*=\s*[\""\']?([^\""\'>]*)/i', $img_html, $matches_title);
     89            $imported_thumb_id = $this->import_image_from_url($image_url, $post->ID, [
     90                'alt' => $matches_alt[1] ?? '',
     91                'title' => $matches_title[1] ?? '',
     92            ]);
     93            if (!is_wp_error($imported_thumb_id) && (int) $imported_thumb_id > 0) {
     94                $thumb_id = (int) $imported_thumb_id;
    5095                break;
    5196            }
    5297        }
     98   
     99        // If we succeed in generating thumg, let's update post meta
     100        if ($thumb_id) {
     101            update_post_meta( $post->ID, '_thumbnail_id', $thumb_id );
     102            return true;
     103        }
     104
     105        return false;
     106    }
     107   
     108    /**
     109     * Imports an image from an external URL into the media library.
     110     *
     111     * @param string $url The URL of the image to import.
     112     * @param int $post_id The ID of the post to import the image to.
     113     * @param array $opts Optional arguments for the import.
     114     * @return int|\WP_Error Attachment-ID oder Fehler
     115     */
     116    private function import_image_from_url( string $url, int $post_id = 0, array $opts = [] ) {
     117       
     118        // Validate the URL
     119        if (!wp_http_validate_url($url)) {
     120            return new WP_Error('invalid_url', __('Invalid image URL.'));
     121        }
     122
     123        // Parse the options
     124        $opts = wp_parse_args($opts, [
     125            'alt'     => '',
     126            'title'   => '',
     127        ]);
     128
     129        // Require the necessary files
     130        require_once ABSPATH.'wp-admin/includes/file.php';
     131        require_once ABSPATH.'wp-admin/includes/image.php';
     132        require_once ABSPATH.'wp-admin/includes/media.php';
     133
     134        // Download the image
     135        $tmp = download_url($url);
     136        if ( is_wp_error($tmp) ) {
     137            return $tmp;
     138        }
     139
     140        // Get the name and mime type of the image
     141        $name = wp_basename(parse_url($url, PHP_URL_PATH) ?: 'image');
     142        $wp_filetype = wp_check_filetype_and_ext( $tmp, $name );
     143
     144        // Check if the image is a valid image
     145        if (empty($wp_filetype['type']) || !wp_match_mime_types('image', $wp_filetype['type'])) {
     146            return new WP_Error('invalid_image', __('The uploaded file is not a valid image. Please try again.'));
     147        }
     148
     149        // Create the file array
     150        $file_array = [
     151            'name'     => $name,
     152            'tmp_name' => $tmp,
     153            'type'     => $wp_filetype['type'],
     154        ];
     155
     156        // Create the post data
     157        $post_data = [];
     158        if (!empty($opts['title'])) {
     159            $post_data['post_title'] = $opts['title'];
     160        }
     161
     162        // Create the description
     163        $desc = null;
     164
     165        // Sideload the image
     166        $attachment_id = media_handle_sideload($file_array, $post_id, $desc, $post_data);
     167
     168        // Unlink the temporary file if it exists
     169        @unlink($tmp);
     170
     171        // If there is an error, return the error
     172        if ( is_wp_error($attachment_id) ) {
     173            return $attachment_id;
     174        }
     175
     176        // Update the attachment meta if the alt text is provided
     177        if (!empty($opts['alt'])) {
     178            update_post_meta($attachment_id, '_wp_attachment_image_alt', $opts['alt']);
     179        }
     180
     181        // Return the attachment ID (as integer)
     182        return (int) $attachment_id;
    53183    }
    54184}
    55 
    56 
    57 function advset__feature__auto_thumbs__generate_post_thumbnail( $matches, $key, $post ) {
    58     // Make sure to assign correct title to the image. Extract it from img tag
    59     $imageTitle = '';
    60     preg_match_all('/<\s*img [^\>]*title\s*=\s*[\""\']?([^\""\'>]*)/i', empty($post->post_content) ? '' : $post->post_content, $matchesTitle);
    61 
    62     if (count($matchesTitle) && isset($matchesTitle[1])) {
    63         $imageTitle = empty($matchesTitle[1][$key]) ? '' : $matchesTitle[1][$key];
    64     }
    65 
    66     // Get the URL now for further processing
    67     $imageUrl = $matches[1][$key];
    68 
    69     // Get the file name
    70     $filename = substr($imageUrl, (strrpos($imageUrl, '/'))+1);
    71 
    72     if ( !(($uploads = wp_upload_dir(current_time('mysql')) ) && false === $uploads['error']) )
    73         return null;
    74 
    75     // Generate unique file name
    76     $filename = wp_unique_filename( $uploads['path'], $filename );
    77 
    78     // Move the file to the uploads dir
    79     $new_file = $uploads['path'] . "/$filename";
    80 
    81     if (!ini_get('allow_url_fopen'))
    82         $file_data = advset__feature__auto_thumbs__curl_get_file_contents($imageUrl);
    83     else
    84         $file_data = @file_get_contents($imageUrl);
    85 
    86     if (!$file_data) {
    87         return null;
    88     }
    89 
    90     file_put_contents($new_file, $file_data);
    91 
    92     // Set correct file permissions
    93     $stat = stat( dirname( $new_file ));
    94     $perms = $stat['mode'] & 0000666;
    95     @ chmod( $new_file, $perms );
    96 
    97     // Get the file type. Must to use it as a post thumbnail.
    98     $wp_filetype = wp_check_filetype( $filename );
    99 
    100     extract( $wp_filetype );
    101 
    102     // No file type! No point to proceed further
    103     if ( ( !$type || !$ext ) && !current_user_can( 'unfiltered_upload' ) ) {
    104         return null;
    105     }
    106 
    107     // Compute the URL
    108     $url = $uploads['url'] . "/$filename";
    109 
    110     // Construct the attachment array
    111     $attachment = array(
    112         'post_mime_type' => $type,
    113         'guid' => $url,
    114         'post_parent' => null,
    115         'post_title' => $imageTitle,
    116         'post_content' => '',
    117     );
    118 
    119     $thumb_id = wp_insert_attachment($attachment, false, $post->ID);
    120     if ( !is_wp_error($thumb_id) ) {
    121         require_once(ABSPATH . '/wp-admin/includes/image.php');
    122 
    123         // Added fix by misthero as suggested
    124         wp_update_attachment_metadata( $thumb_id, wp_generate_attachment_metadata( $thumb_id, $new_file ) );
    125         update_attached_file( $thumb_id, $new_file );
    126 
    127         return $thumb_id;
    128     }
    129 
    130     return null;
    131 }
    132 
    133 function advset__feature__auto_thumbs__curl_get_file_contents($URL) {
    134     $c = curl_init();
    135     curl_setopt($c, CURLOPT_RETURNTRANSFER, 1);
    136     curl_setopt($c, CURLOPT_URL, $URL);
    137     $contents = curl_exec($c);
    138     curl_close($c);
    139 
    140     if ($contents) {
    141         return $contents;
    142     }
    143 
    144     return FALSE;
    145 }
  • advanced-settings/trunk/readme.txt

    r3323109 r3357461  
    88Requires at least: 5.0.0
    99Tested up to: 6.8
    10 Stable tag: 3.1.1
     10Stable tag: 3.2.0
    1111License: GPLv3 or later
    1212License URI: http://www.gnu.org/licenses/gpl-3.0.html
     
    2222[→ details in FAQ](https://wordpress.org/plugins/advanced-settings#will%20the%20plugin%20become%20slower%20with%20more%20features%3F)
    2323
     24**🪶 LIGHTWEIGHT**
     25Advanced Settings 3 is lightweight (only about 0.5 MB) and discreet (no dashboard hijacking).
     26[→ details in FAQ](https://wordpress.org/plugins/advanced-settings#how%20can%20the%20plugin%20be%20lightweight%20despite%20so%20many%20features%3F)
     27
    2428**🔒 SECURITY**
    2529Advanced Settings 3 has been independently reviewed for security vulnerabilities via Patchstack.
     
    2731
    2832**✳️ INFO ABOUT THE 2 BAD RATINGS**
    29 2 bad ratings occurred in 2017 because outdated PHP versions were used, but can't happen again.
     33Bad ratings occurred in 2017 because users used outdated PHP versions, but can't happen again.
    3034[→ details in FAQ](https://wordpress.org/plugins/advanced-settings#what%20caused%20the%20two%20bad%20ratings%3F)
    3135
     
    9094
    9195* Display SQL queries and page load time
     96* Create custom post types 💥 renewed
    9297
    9398= Configuration =
     
    118123Advanced Settings 3 works as follows: Only when you open the plugin's settings window does the plugin recognise which settings are actually available. When saving, only the activated settings are written to a separate cache file. Only this cache file is then taken into account during operation. This means that slowdowns can only occur due to **active** features. However, as some features deactivate WordPress functions, the plugin can actually make WordPress even faster than in the standard configuration.
    119124
     125= How can the plugin be lightweight despite so many features? =
     126
     127**Short answer:**
     128Typically, graphical user interfaces represent the largest chunk of data. Since we have largely standardized the user interface for all features, additional features are of little importance.
     129
     130**Detailed answer:**
     131The plugin is currently only 0.5 MB in size. Only about a third of that is used for the actual functionality of the features. The rest is used for the graphical user interface, backward compatibility, performance optimization, and the like. So if we doubled the number of features, the plugin would still be smaller than 0.7 MB. Theoretically, we could quadruple the number of features and still stay under 1 MB. So the number of features isn't crucial. The trick is to standardize everything around the features so that each additional feature has minimal impact.
     132
     133We are passionate about lightweight plugins that remain politely in the background while you work, so we can assure you that this will continue to be the case.
     134
    120135= How is the security of the plugin ensured? =
    121136
     
    152167== Changelog ==
    153168
     169= 3.2.0 - 2025-09-07 =
     170* Fixed a medium priority security issue which appeared on activated feature "Automatically generate the Post Thumbnail"
     171* Thoroughly revised feature "Automatically generate the Post Thumbnail"
     172* Fixed a low priority security issue which appeared on activated experimental feature "Post types settings" (now "Custom post types")
     173* Thoroughly revised feature "Post types settings", renamed it to "Custom post types" and removed the "experimental" flag
     174
    154175= 3.1.1 - 2025-07-06 =
    155176* Fixed minor cache problem
     
    161182= 3.0.2 - 2025-06-04 =
    162183* Fixed order of features
    163 * Fixed a minor security issue in old code (rated as low priority by [Patchstack](https://patchstack.com/))
     184* Fixed a low priority security issue which appeared on activated experimental feature "Filters and actions settings"
    164185
    165186= 3.0.1 - 2025-05-03 =
Note: See TracChangeset for help on using the changeset viewer.