Changeset 3286707
- Timestamp:
- 05/03/2025 11:00:37 AM (11 months ago)
- Location:
- th23-contact/tags/3.1.3
- Files:
-
- 14 copied
-
. (copied) (copied from th23-contact/trunk)
-
img (copied) (copied from th23-contact/trunk/img)
-
inc (copied) (copied from th23-contact/trunk/inc)
-
inc/th23-admin-class.css (copied) (copied from th23-contact/trunk/inc/th23-admin-class.css) (1 diff)
-
inc/th23-admin-class.js (copied) (copied from th23-contact/trunk/inc/th23-admin-class.js)
-
inc/th23-admin-class.php (copied) (copied from th23-contact/trunk/inc/th23-admin-class.php) (9 diffs)
-
lang (copied) (copied from th23-contact/trunk/lang)
-
readme.txt (copied) (copied from th23-contact/trunk/readme.txt) (3 diffs)
-
th23-contact-admin.php (copied) (copied from th23-contact/trunk/th23-contact-admin.php) (2 diffs)
-
th23-contact-block-editor.js (copied) (copied from th23-contact/trunk/th23-contact-block-editor.js)
-
th23-contact-upgrade.php (copied) (copied from th23-contact/trunk/th23-contact-upgrade.php)
-
th23-contact.css (copied) (copied from th23-contact/trunk/th23-contact.css)
-
th23-contact.js (copied) (copied from th23-contact/trunk/th23-contact.js)
-
th23-contact.php (copied) (copied from th23-contact/trunk/th23-contact.php) (2 diffs)
Legend:
- Unmodified
- Added
- Removed
-
th23-contact/tags/3.1.3/inc/th23-admin-class.css
r3253885 r3286707 100 100 margin-left: 64px; 101 101 } 102 @media screen and (min-width: 782px) { 103 div.th23-admin-about .floating-right { 104 float: right; 105 } 106 div.th23-admin-about .floating-right .separator-left { 107 display: none; 108 } 109 } 110 div.th23-admin-about .update-url { 111 cursor: pointer; 112 } 102 113 div.th23-admin-about a { 103 114 text-decoration: none; -
th23-contact/tags/3.1.3/inc/th23-admin-class.php
r3284653 r3286707 1 1 <?php 2 2 /* 3 th23 Admin (adjusted for WP.org)3 th23 Admin 4 4 Basic admin functionality 5 Version: 1. 6.2-wp5 Version: 1.7.0 6 6 7 7 Coded 2024-2025 by Thorsten Hartmann (th23) … … 16 16 } 17 17 18 if(!class_exists('th23_admin_v1 62wp')) {19 class th23_admin_v1 62wp{18 if(!class_exists('th23_admin_v170')) { 19 class th23_admin_v170 { 20 20 21 21 private $parent; … … 51 51 add_filter('plugin_row_meta', array(&$this, 'contact_link'), 10, 2); 52 52 53 // Handle plugin repository for plugin info and update for non-WP.org hosted plugin 54 // note: "update_url" and main filter for site_transient... have to be set by the plugin admin script 55 if(!empty($this->parent->plugin['update_url'])) { 56 // replace plugin row info / links to ensure detailed info from alternative source is available 57 // note: hook early to allow other modifications based on this eg adding support link and requirement notices 58 add_filter('plugin_row_meta', array(&$this, 'update_details'), 1, 4); 59 add_filter('plugins_api', array(&$this, 'update_info'), 20, 3); 60 add_action('upgrader_process_complete', array(&$this, 'update_cache'), 10, 2); 61 } 62 53 63 // Add settings page and JS/ CSS 54 64 if(!empty($this->parent->plugin['settings'])) { … … 105 115 } 106 116 return $links; 117 } 118 119 // Set plugin repository for plugin info and update 120 // Replace plugin row info / links to ensure detailed info from alternative source is available 121 function update_details($links, $file, $data, $status) { 122 if($this->parent->plugin['basename'] == $file) { 123 $links = array(); 124 // replicate default version and author 125 if(!empty($data['Version'])) { 126 /* translators: parses in plugin version number */ 127 $links[] = sprintf($this->__('Version %s'), $data['Version']); 128 } 129 if(!empty($data['Author'])) { 130 $author = $data['Author']; 131 if(!empty($data['AuthorURI'])) { 132 $author = '<a href="' . $data['AuthorURI'] . '">' . $data['Author'] . '</a>'; 133 } 134 /* translators: parses in plugin author name / link */ 135 $links[] = sprintf($this->__('By %s'), $author); 136 } 137 // add detailed info independed from source - original code only focuses on WP.org repository 138 if(current_user_can('install_plugins')) { 139 $links[] = '<a href="' . esc_url(network_admin_url('plugin-install.php?tab=plugin-information&plugin=' . $this->parent->plugin['slug'] . '&TB_iframe=true&width=600&height=550')) . '" class="thickbox open-plugin-details-modal" data-title="' . esc_attr($data['Name']) . '">' . $this->__('View details') . '</a>'; 140 } 141 elseif(!empty($data['PluginURI'])) { 142 $links[] = '<a href="' . esc_url($data['PluginURI']) . '">' . $this->__('Visit plugin site') . '</a>'; 143 } 144 } 145 return $links; 146 } 147 148 // Get plugin (update) information 149 function update_info($res, $action, $args) { 150 // no action, if no plugin information request for this plugin 151 if('plugin_information' !== $action || $this->parent->plugin['slug'] !== $args->slug) { 152 return $res; 153 } 154 // note: returning $res defaults to WP.org repo, if exists and alternative is unreachable - nevertheless return error message, as versions might differ 155 if(empty($remote = $this->update_request()) || !is_array($remote)) { 156 $res = new stdClass(); 157 $res->slug = $this->parent->plugin['slug']; 158 $res->name = $this->parent->plugin['data']['Name']; 159 /* translators: parses in plugin information source url */ 160 $res->sections = array('other_notes' => '<div class="notice notice-error"><p>' . sprintf($this->__('Failed to load plugin information from %s'), '<code>' . $this->parent->plugin['update_url'] . '</code>') . '</p></div>'); 161 return $res; 162 } 163 // convert top array from response to class structure 164 $res = new stdClass(); 165 foreach($remote as $id => $content) { 166 $res->$id = $content; 167 } 168 return $res; 169 } 170 171 // Retrieve plugin (update) information from cache or download from repository 172 function update_request() { 173 $json = get_transient($this->parent->plugin['slug'] . '_update_cache'); 174 if(false === $json) { 175 $remote = wp_remote_get($this->parent->plugin['update_url'], array('timeout' => 10, 'headers' => array('Accept' => 'application/json'))); 176 if(is_wp_error($remote) || 200 !== wp_remote_retrieve_response_code($remote) || empty($json = wp_remote_retrieve_body($remote))) { 177 return false; 178 } 179 $json = json_decode($json, true); 180 set_transient($this->parent->plugin['slug'] . '_update_cache', $json, DAY_IN_SECONDS); 181 } 182 return $json; 183 } 184 185 // Insert plugin (update) information into data passed to wp updater 186 function update_download($transient) { 187 // plugin has own update url and participates in update checks 188 if(empty($this->parent->plugin['update_url']) || empty($transient->checked[$this->parent->plugin['basename']])) { 189 return $transient; 190 } 191 // ignore responses from default Wordpress repository 192 unset($transient->response[$this->parent->plugin['basename']]); 193 unset($transient->no_update[$this->parent->plugin['basename']]); 194 // not yet checked during current page load - check if update available 195 if(empty($this->data['update_cache'])) { 196 $this->data['update_cache'] = array(); 197 // update server contacted and valid response received 198 if(!empty($plugin = $this->update_request()) && is_array($plugin)) { 199 $res = new stdClass(); 200 $res->id = $this->parent->plugin['slug']; 201 $res->slug = $this->parent->plugin['slug']; 202 $res->plugin = $this->parent->plugin['basename']; 203 $res->url = $plugin['homepage']; 204 $res->new_version = $plugin['version']; 205 $res->package = $plugin['download_link']; 206 $res->requires_php = $plugin['requires_php']; 207 $res->requires = $plugin['requires']; 208 $res->tested = $plugin['tested']; 209 $res->icons = (!empty($plugin['icons'])) ? $plugin['icons'] : array(); 210 $res->banners = (!empty($plugin['banners'])) ? $plugin['banners'] : array(); 211 $this->data['update_cache']['plugin_info'] = $res; 212 // newer version available and required wp and php versions are met 213 if(version_compare($this->parent->plugin['version'], $plugin['version'], '<') && version_compare($plugin['requires'], get_bloginfo('version'), '<=') && version_compare($plugin['requires_php'], PHP_VERSION, '<')) { 214 $this->data['update_cache']['status'] = 'update_available'; 215 } 216 else { 217 $this->data['update_cache']['status'] = 'no_update'; 218 } 219 } 220 } 221 // (re-)add plugin to transient based on update availability (see above or cached) 222 if(!empty($this->data['update_cache']['plugin_info'])) { 223 if('update_available' == $this->data['update_cache']['status']) { 224 $transient->response[$this->parent->plugin['basename']] = $this->data['update_cache']['plugin_info']; 225 } 226 else { 227 $transient->no_update[$this->parent->plugin['basename']] = $this->data['update_cache']['plugin_info']; 228 } 229 } 230 return $transient; 231 } 232 233 // Clear plugin (update) information from cache upon installation of new version 234 function update_cache($upgrader, $options){ 235 if('update' === $options['action'] && 'plugin' === $options['type'] && in_array($this->parent->plugin['basename'], $options['plugins'])) { 236 delete_transient($this->parent->plugin['slug'] . '_update_cache'); 237 } 107 238 } 108 239 … … 184 315 // update user preference for screen options via AJAX 185 316 function set_screen_options() { 186 if(!empty($_POST['nonce']) && wp_verify_nonce( $_POST['nonce'], 'th23-admin-screen-options-nonce')) {317 if(!empty($_POST['nonce']) && wp_verify_nonce(sanitize_text_field(wp_unslash($_POST['nonce'])), 'th23-admin-screen-options-nonce') && !empty($_POST['plugin'])) { 187 318 $screen_options = $this->get_screen_options(); 188 319 $new = array(); … … 270 401 // html input 271 402 elseif($html_input) { 272 if(isset($_POST['input_' . $option . '_' . $element . '_' . $sub_option])) { 273 // if only single value allowed, only take first element from value array for validation 274 if($sub_type == 'single' && is_array($_POST['input_' . $option . '_' . $element . '_' . $sub_option])) { 275 $value = sanitize_text_field(wp_unslash(reset($_POST['input_' . $option . '_' . $element . '_' . $sub_option]))); 403 $html_input_name = 'input_' . $option . '_' . $element . '_' . $sub_option; 404 if(isset($_POST[$html_input_name])) { 405 // for textarea preserve linebreaks 406 if(!empty($sub_option_details['element']) && 'textarea' == $sub_option_details['element']) { 407 $value = sanitize_textarea_field(wp_unslash($_POST[$html_input_name])); 276 408 } 277 409 else { 278 $value = sanitize_text_field(wp_unslash($_POST['input_' . $option . '_' . $element . '_' . $sub_option])); 410 $value = (is_array($_POST[$html_input_name])) ? array_map('sanitize_text_field', wp_unslash($_POST[$html_input_name])) : sanitize_text_field(wp_unslash($_POST[$html_input_name])); 411 // if only single value allowed, only take first element from value array for validation 412 if($type == 'single' && is_array($value)) { 413 $value = reset($value); 414 } 279 415 } 280 416 } … … 320 456 if($html_input) { 321 457 if(isset($_POST['input_' . $option])) { 322 // if only single value allowed, only take first element from value array for validation 323 if($type == 'single' && is_array($_POST['input_' . $option])) { 324 $value = sanitize_text_field(wp_unslash(reset($_POST['input_' . $option]))); 325 } 326 elseif($type == 'multiple' && is_array($_POST['input_' . $option])) { 327 $value = array(); 328 foreach($_POST['input_' . $option] as $key => $val) { 329 $value[$key] = sanitize_text_field(wp_unslash($val)); 458 // for textarea preserve linebreaks 459 if(!empty($option_details['element']) && 'textarea' == $option_details['element']) { 460 $value = sanitize_textarea_field(wp_unslash($_POST['input_' . $option])); 461 } 462 else { 463 $value = (is_array($_POST['input_' . $option])) ? array_map('sanitize_text_field', wp_unslash($_POST['input_' . $option])) : sanitize_text_field(wp_unslash($_POST['input_' . $option])); 464 // if only single value allowed, only take first element from value array for validation 465 if($type == 'single' && is_array($value)) { 466 $value = reset($value); 330 467 } 331 }332 // for textarea preserve linebreaks333 elseif(!empty($option_details['element']) && 'textarea' == $option_details['element']) {334 $value = sanitize_textarea_field(wp_unslash($_POST['input_' . $option]));335 }336 else {337 $value = sanitize_text_field(wp_unslash($_POST['input_' . $option]));338 468 } 339 469 } … … 432 562 } 433 563 elseif(!empty($value)) { 434 $form_classes[] = 'th23-admin-screen-option-' . $option . '-' . str_replace(' ', '_', $value);564 $form_classes[] = 'th23-admin-screen-option-' . $option . '-' . esc_attr(str_replace(' ', '_', $value)); 435 565 } 436 566 } … … 727 857 echo ' | <a href="' . esc_url($this->parent->plugin['data']['PluginURI']) . '">' . esc_html($this->__('Visit plugin site')) . '</a>'; 728 858 } 859 if(!empty($this->parent->plugin['update_url'])) { 860 $update_parsed = parse_url($this->parent->plugin['update_url']); 861 /* translators: parses in host / domain part of repository url for non-WP.org hosted plugin */ 862 echo '<span class="floating-right"><span class="separator-left"> | </span>' . sprintf(esc_html($this->__('Updated via %s')), '<span class="update-url" title="' . esc_attr($this->parent->plugin['update_url']) . '">' . esc_html($update_parsed['host']) . '</span>') . '</span>'; 863 } 729 864 echo '</p></div>'; 730 865 -
th23-contact/tags/3.1.3/readme.txt
r3284653 r3286707 4 4 Tags: contact, form, block, shortcode 5 5 Requires at least: 4.2 6 Tested up to: 6.8 7 Stable tag: 3.1. 26 Tested up to: 6.8.1 7 Stable tag: 3.1.3 8 8 Requires PHP: 8.0 9 9 License: GPL-3.0 … … 81 81 == Changelog == 82 82 83 = v3.1. 2=83 = v3.1.3 = 84 84 85 * fix: ensure WP specific th23 Admin class is loaded85 * fix: upgrade to th23 Admin class 1.7.0, removing some inconsistencies regarding own updater incl removal of option to chose update source and ensure proper escaping of all output 86 86 87 87 = v3.1.1 = … … 128 128 == Upgrade Notice == 129 129 130 = v3.1. 2=130 = v3.1.3 = 131 131 132 132 * n/a -
th23-contact/tags/3.1.3/th23-contact-admin.php
r3284653 r3286707 22 22 'permission' => 'manage_options', 23 23 ); 24 // icon : "square" 48 x 48px (footer) /"horizontal" 36px height (header, width irrelevant) / both (resized if larger)24 // icons "square" 48 x 48px (footer) and "horizontal" 36px height (header, width irrelevant) / both (resized if larger) 25 25 $this->plugin['icon'] = array('square' => 'img/icon-square.png', 'horizontal' => 'img/icon-horizontal.png'); 26 26 $this->plugin['support_url'] = 'https://github.com/th23x/th23-contact/issues'; 27 27 $this->plugin['requirement_notices'] = array(); 28 // update: alternative update source29 $this->plugin['update_url'] = 'https://github.com/th23x/th23-contact/releases/latest/download/update.json';30 28 31 29 // Load and setup required th23 Admin class 32 30 if(file_exists($this->plugin['dir_path'] . '/inc/th23-admin-class.php')) { 33 31 require($this->plugin['dir_path'] . '/inc/th23-admin-class.php'); 34 $this->admin = new th23_admin_v1 62wp($this);32 $this->admin = new th23_admin_v170($this); 35 33 } 36 34 if(!empty($this->admin)) { 37 35 add_action('init', array(&$this, 'setup_admin_class')); 36 // alternative update source for non-WP.org hosted plugin 37 // important: remove following two lines for WP.org-hosted plugin 38 // ... removed 38 39 } 39 40 else { … … 74 75 // note: need to populate $this->i18n earliest at init hook to get user locale 75 76 $this->i18n = array( 76 'Plugin' => __('Plugin', 'th23-specials'),77 'Settings' => __('Settings' , 'th23-contact'),78 /* translators: parses in pluginversion number */79 'Version %s' => __('Version %s' , 'th23-contact'),77 // reviewer: to keep consistency some admin language strings are used in sync with core 78 'Settings' => __('Settings'), 79 /* translators: parses in version number */ 80 'Version %s' => __('Version %s'), 80 81 /* translators: parses in plugin name */ 81 82 'Copy from %s' => __('Copy from %s', 'th23-contact'), 82 'Support' => __('Support' , 'th23-contact'),83 'Done' => __('Done' , 'th23-contact'),84 'Settings saved.' => __('Settings saved.' , 'th23-contact'),85 '+' => __('+' , 'th23-contact'),86 '-' => __('-' , 'th23-contact'),87 'Save Changes' => __('Save Changes' , 'th23-contact'),88 /* translators: parses in plugin author name / link*/89 'By %s' => __('By %s' , 'th23-contact'),90 'View details' => __('View details' , 'th23-specials'),91 'Visit plugin site' => __('Visit plugin site' , 'th23-contact'),92 'Error' => __('Error' , 'th23-contact'),83 'Support' => __('Support'), 84 'Done' => __('Done'), 85 'Settings saved.' => __('Settings saved.'), 86 '+' => __('+'), 87 '-' => __('-'), 88 'Save Changes' => __('Save Changes'), 89 /* translators: parses in author */ 90 'By %s' => __('By %s'), 91 'View details' => __('View details'), 92 'Visit plugin site' => __('Visit plugin site'), 93 'Error' => __('Error'), 93 94 /* translators: 1: option name, 2: opening a tag of link to support/ plugin page, 3: closing a tag of link */ 94 95 'Invalid combination of input field and default value for "%1$s" - please %2$scontact the plugin author%3$s' => __('Invalid combination of input field and default value for "%1$s" - please %2$scontact the plugin author%3$s', 'th23-contact'), 95 'Updates' => __('Updates', 'th23-contact'),96 ' If disabled or unreachable, updates will use default WordPress repository' => __('If disabled or unreachable, updates will use default WordPress repository', 'th23-contact'),97 /* translators: parses in repositoryurl */98 ' Update from %s' => __('Updatefrom %s', 'th23-contact'),96 /* translators: parses in repository url for non-WP.org hosted plugin */ 97 'Updated via %s' => __('Updated via %s', 'th23-contact'), 98 /* translators: parses in plugin information source url */ 99 'Failed to load plugin information from %s' => __('Failed to load plugin information from %s', 'th23-contact'), 99 100 ); 100 101 -
th23-contact/tags/3.1.3/th23-contact.php
r3284653 r3286707 13 13 License URI: https://github.com/th23x/th23-contact/blob/main/LICENSE 14 14 15 Version: 3.1. 215 Version: 3.1.3 16 16 17 17 Requires at least: 4.2 18 Tested up to: 6.8 18 Tested up to: 6.8.1 19 19 Requires PHP: 8.0 20 20 … … 42 42 $this->plugin['basename'] = plugin_basename($this->plugin['file']); 43 43 $this->plugin['dir_url'] = plugin_dir_url($this->plugin['file']); 44 $this->plugin['version'] = '3.1. 2';44 $this->plugin['version'] = '3.1.3'; 45 45 46 46 // Load plugin options
Note: See TracChangeset
for help on using the changeset viewer.