Changeset 3749369
- Timestamp:
- 10/15/2026 09:46:36 PM (13 days ago)
- Location:
- publisherkits-plugintidy/trunk
- Files:
-
- 1 deleted
- 10 edited
-
includes/class-pktidy-support-copilot-boot.php (modified) (2 diffs)
-
includes/support-copilot/class-copilot.php (modified) (7 diffs)
-
includes/support-copilot/support-copilot.php (modified) (2 diffs)
-
publisherkits-plugintidy.php (modified) (2 diffs)
-
readme.txt (modified) (4 diffs)
-
vendor/freemius/BWD-PATCH-NOTES.md (deleted)
-
vendor/freemius/includes/class-freemius.php (modified) (18 diffs)
-
vendor/freemius/includes/managers/class-fs-clone-manager.php (modified) (5 diffs)
-
vendor/freemius/start.php (modified) (1 diff)
-
vendor/freemius/templates/add-ons.php (modified) (4 diffs)
-
vendor/freemius/templates/pricing.php (modified) (4 diffs)
Legend:
- Unmodified
- Added
- Removed
-
publisherkits-plugintidy/trunk/includes/class-pktidy-support-copilot-boot.php
r3708042 r3749369 181 181 ), 182 182 'diagnostics' => array( __CLASS__, 'diagnostics' ), 183 // The customer's own AI answers wherever one is connected in 184 // WordPress, at their cost; the support service answers only where 185 // none is. The bridge below runs on the WordPress core AI Client 186 // and uses no AI code of this plugin's own. 187 'local_ai' => array( __CLASS__, 'local_ai' ), 188 'local_ai_available' => array( __CLASS__, 'local_ai_available' ), 189 'connectors_url' => array( __CLASS__, 'connectors_url' ), 183 190 'suite' => array( __CLASS__, 'suite_manifest' ), 184 191 'accent' => '#c98a70', … … 189 196 add_action( 'pktidy_settings_after_form', array( __CLASS__, 'render_panel' ) ); 190 197 add_action( 'admin_post_pktidy_save_copilot_settings', array( __CLASS__, 'handle_save' ) ); 198 } 199 200 /** 201 * Local answering on the site's own AI: one reply from the WordPress core AI 202 * Client (Settings > Connectors), at the customer's own cost. The module 203 * supplies only this plugin's documentation and the administrator's question; 204 * nothing here reads Freemius, a token, a licence or any of this plugin's 205 * data, and it uses no AI code of this plugin's own, so it is the same in 206 * every build. The house standard (1 Oct 2026): the customer's AI answers 207 * wherever one is connected; the support service answers only where none is. 208 * 209 * @since 0.18.0 210 * @param string $system System prompt. 211 * @param string $user User prompt. 212 * @param array $opts max_tokens. 213 * @return string|\WP_Error 214 */ 215 public static function local_ai( $system, $user, array $opts = array() ) { 216 if ( ! function_exists( 'wp_ai_client_prompt' ) ) { 217 return new \WP_Error( 'pktidy_copilot_unavailable', __( 'The WordPress AI Client is not available on this site.', 'publisherkits-plugintidy' ) ); 218 } 219 $max_tokens = isset( $opts['max_tokens'] ) ? (int) $opts['max_tokens'] : 1400; 220 $system = (string) $system; 221 try { 222 // A combined prompt is the baseline every provider honours; a separate 223 // system instruction is preferred where the client supports it. The 224 // builder resolves methods via __call, so it is probed by calling inside 225 // try/catch - method_exists() reports false for magic methods. 226 $builder = wp_ai_client_prompt( '' === $system ? $user : $system . "\n\n" . $user )->using_max_tokens( $max_tokens ); 227 if ( '' !== $system ) { 228 try { 229 $b2 = wp_ai_client_prompt( $user )->using_max_tokens( $max_tokens )->using_system_instruction( $system ); 230 if ( is_object( $b2 ) ) { 231 $builder = $b2; 232 } 233 } catch ( \Throwable $e ) { 234 unset( $e ); 235 } 236 } 237 $text = $builder->generate_text(); 238 return is_wp_error( $text ) ? $text : (string) $text; 239 } catch ( \Throwable $e ) { 240 return new \WP_Error( 'pktidy_copilot_failed', __( 'The site AI provider could not produce a reply.', 'publisherkits-plugintidy' ) ); 241 } 242 } 243 244 /** 245 * Whether the WordPress core AI Client has a provider that can generate text 246 * right now. The client ships with WordPress 7.0; on older sites this is false 247 * and the support service answers. 248 * 249 * @since 0.18.0 250 * @return bool 251 */ 252 public static function local_ai_available() { 253 if ( ! function_exists( 'wp_ai_client_prompt' ) ) { 254 return false; 255 } 256 try { 257 return (bool) wp_ai_client_prompt( 'test' )->is_supported_for_text_generation(); 258 } catch ( \Throwable $e ) { 259 return false; 260 } 261 } 262 263 /** 264 * Where an administrator connects an AI provider: Settings > Connectors from 265 * WordPress 7.0, the general Settings screen before it. 266 * 267 * @since 0.18.0 268 * @return string 269 */ 270 public static function connectors_url() { 271 return admin_url( function_exists( 'wp_get_connectors' ) ? 'options-connectors.php' : 'options-general.php' ); 191 272 } 192 273 -
publisherkits-plugintidy/trunk/includes/support-copilot/class-copilot.php
r3708042 r3749369 5 5 * Self-contained, host-agnostic popup help chat for the WordPress admin. 6 6 * Renders a floating help launcher on the host plugin's admin screens and 7 * proxies chat/escalation to the Black and White Digitalsupport hub8 * ( BW Support AI/copilot endpoints). The widget is strictly read-only:7 * proxies chat/escalation to the plugin author's support hub 8 * (/copilot endpoints). The widget is strictly read-only: 9 9 * beyond its own consent + settings options it never modifies site state. 10 10 * … … 19 19 * 20 20 * @package BlackWhiteDigital\SupportCopilot 21 * @version 2.1. 421 * @version 2.1.5 22 22 */ 23 23 … … 72 72 * Module version (independent of any host plugin version). 73 73 */ 74 const MODULE_VERSION = '2.1. 4';74 const MODULE_VERSION = '2.1.5'; 75 75 76 76 /** … … 203 203 'panel_subtitle' => 'Instant answers about this plugin', 204 204 'consent_heading' => 'Before you start', 205 'consent_body' => 'To answer your questions, this assistant sends your message, the chat history and a small read-only diagnostic snapshot — the plugin version, the versions of installed companion plugins, the admin screen you are on, and a short whitelist of non-sensitive settings — to the plugin author\'s support service at %host%. No API keys, passwords, contact records or site content are ever sent, and the assistant cannot change anything on your site. Chats are not stored on the support server; only if you later choose "Send to support" is the conversation retained, with the email address you give and your WordPress first and last name, so that it can become a support ticket.',205 'consent_body' => 'To answer your questions, this assistant sends your message, the chat history and a small read-only diagnostic snapshot — the plugin version, the versions of installed companion plugins, the admin screen you are on, and a short whitelist of non-sensitive settings — to the plugin author\'s support service at %host%. If this site has its own AI provider configured, the assistant tries that provider first: your question goes to your provider, and the support service is asked only for documentation matched to your version — no question or chat text is sent to it. If that local answer fails and this site is authorised to use the support service, the assistant automatically falls back to the support service, and your question and recent chat history are then sent to it as described above. No API keys, passwords, contact records or site content are ever sent, and the assistant cannot change anything on your site. Chats are not stored on the support server; only if you later choose "Send to support" is the conversation retained, with the email address you give and your WordPress first and last name, so that it can become a support ticket.', 206 206 'consent_accept' => 'Agree and start chatting', 207 207 'consent_accept_hint' => 'Records your agreement for this whole site and enables the assistant for everyone who can use it. Only a user with settings access can accept. You can withdraw consent on the settings page.', … … 244 244 'settings_mode_label' => 'Answer mode', 245 245 'settings_mode_local_title' => 'This site\'s own AI (detected)', 246 'settings_mode_local_desc' => 'Questions are answered locally on the AI provider configured on this site, at this site\'s expense. The support service is used only to fetch version-matched knowledge files, and as an automatic fallback if the local AI fails. Locally answered replies are labelled in the chat.',246 'settings_mode_local_desc' => 'Questions are answered locally on the AI provider configured on this site, at this site\'s expense. The support service is asked only for version-matched knowledge files (no question or chat text). If the local AI fails and this site is authorised for the support service, the assistant automatically falls back to it, and the question and recent chat history are then sent to the support service. Locally answered replies are labelled in the chat.', 247 247 'settings_mode_hub_title' => 'Support service', 248 248 'settings_mode_hub_desc' => 'No local AI provider is configured on this site, so questions are answered by the plugin author\'s support service. If this site later gains its own AI key, the assistant switches to local answering automatically — no setting needed here.', … … 444 444 445 445 /** 446 * Whether an endpoint URL may be used: https only. The hub token rides 447 * on every request, so a cleartext endpoint would expose it in transit. 446 * Whether an endpoint URL may be used: https only, except on a local 447 * development install. The hub token rides on every request, so a 448 * cleartext endpoint would expose it in transit. 448 449 * 449 450 * @param string $url Endpoint base or full request URL. … … 455 456 } 456 457 457 /** 458 * Filters whether a non-https support endpoint is allowed. 459 * 460 * Intended for local development only — never enable in production: 461 * the widget token would travel in cleartext. 462 * 463 * @param bool $allow Default false. 464 * @param string $url The endpoint URL being checked. 465 * @param string $id The copilot instance id. 466 */ 467 return (bool) apply_filters( 'black_white_digital_support_copilot_allow_insecure_endpoint', false, $url, $this->config['id'] ); 458 // A cleartext endpoint is allowed only on a site that declares itself 459 // a local development install (WP_ENVIRONMENT_TYPE 'local'), never in 460 // production: the widget token would travel in cleartext. 2.1.5 461 // replaced the former allow-insecure filter, whose fixed name could 462 // carry no host prefix in a verbatim-vendored module. 463 return function_exists( 'wp_get_environment_type' ) && 'local' === wp_get_environment_type(); 468 464 } 469 465 -
publisherkits-plugintidy/trunk/includes/support-copilot/support-copilot.php
r3708042 r3749369 23 23 * 24 24 * @package BlackWhiteDigital\SupportCopilot 25 * @version 2.1. 425 * @version 2.1.5 26 26 */ 27 27 … … 30 30 } 31 31 32 /* *33 * Version of the Support Copilot module vendored in THIS plugin.32 /* 33 * Register THIS copy's version with the shared loader. 34 34 * 35 * Bump together with Copilot::MODULE_VERSION on any module change. The loader 36 * compares this across all vendored copies with version_compare(); the highest 37 * wins. A variable, not a constant: constants collide across copies, variables 38 * do not. 35 * $bwdsc_this_version is the version of the module vendored in this plugin. 36 * Bump it together with Copilot::MODULE_VERSION on any module change. The 37 * loader compares it across all vendored copies with version_compare(); the 38 * highest wins. It is a closure-local variable: not a constant (constants 39 * collide across copies) and, since 2.1.5, not a global either (a global in a 40 * verbatim-vendored file can carry no host prefix, which Plugin Check flags). 41 * Each copy's entry runs its own closure, so every copy registers its own 42 * version and nothing leaks into the next copy's require. 39 43 */ 40 $bwdsc_this_version = '2.1.4'; 44 ( static function () { 45 $bwdsc_this_version = '2.1.5'; 41 46 42 // The loader is a tiny, FROZEN registry shared by all vendored copies. The43 // first copy to load defines it; later copies reuse it (class_exists guard44 // inside). Versioned behaviour lives in the class file it loads, never here.45 require_once __DIR__ . '/class-loader.php';47 // The loader is a tiny, FROZEN registry shared by all vendored copies. The 48 // first copy to load defines it; later copies reuse it (class_exists guard 49 // inside). Versioned behaviour lives in the class file it loads, never here. 50 require_once __DIR__ . '/class-loader.php'; 46 51 47 \BlackWhiteDigital\SupportCopilot\Loader::register( $bwdsc_this_version, __DIR__ ); 48 49 // Don't leak into the next copy's require of its own entry file. 50 unset( $bwdsc_this_version ); 52 \BlackWhiteDigital\SupportCopilot\Loader::register( $bwdsc_this_version, __DIR__ ); 53 } )(); 51 54 52 55 // Negotiate once every host has had a chance to register. Hosts commonly -
publisherkits-plugintidy/trunk/publisherkits-plugintidy.php
r3708042 r3749369 5 5 * Plugin URI: https://publisherkits.com/plugintidy 6 6 * Description: Audits your installed plugins by real-world usage and risk, records every plugin change, and makes removal safe. 7 * Version: 0.1 7.07 * Version: 0.18.0 8 8 * Author: Black and White Digital 9 9 * Author URI: https://publisherkits.com … … 20 20 */ 21 21 defined( 'ABSPATH' ) || exit; 22 define( 'PKTIDY_VERSION', '0.1 7.0' );22 define( 'PKTIDY_VERSION', '0.18.0' ); 23 23 define( 'PKTIDY_PLUGIN_FILE', __FILE__ ); 24 24 define( 'PKTIDY_PLUGIN_DIR', plugin_dir_path( __FILE__ ) ); -
publisherkits-plugintidy/trunk/readme.txt
r3708091 r3749369 5 5 Tested up to: 7.1 6 6 Requires PHP: 7.4 7 Stable tag: 0.1 7.07 Stable tag: 0.18.0 8 8 License: GPLv2 or later 9 9 License URI: https://www.gnu.org/licenses/gpl-2.0.html … … 90 90 = Does it send my data anywhere? = 91 91 92 The free version contacts WordPress.org to ask whether your plugins are still maintained, sending only the plugin's folder name. Nothing else leaves your site.92 The free version contacts WordPress.org to ask whether your plugins are still maintained, sending only the plugin's folder name. The audit sends nothing else. The Help assistant sends your question only when you use it and have agreed to its privacy notice - see External services below. 93 93 94 94 = It says three plugins do the same job. Which should I remove? = … … 123 123 What is sent when you ask a question: your question and the recent chat; your site address and, where available, your licence installation identifier, used to authorise the request and apply fair-use limits; the PluginTidy version; the admin screen you are on; and a few non-sensitive diagnostics - how many plugins are active and inactive, whether an audit has run, whether scheduled tasks work, and whether WordPress.org could be reached. **The names of the plugins on your site, your audit results, your notes and your change log are never sent.** Choosing "Send to support" keeps the conversation as a ticket, with the email you give and your WordPress first and last name. 124 124 125 If an AI provider is connected to your site under Settings > Connectors (WordPress 7.0 or later), the assistant answers on that provider instead, through the WordPress AI Client and under that provider's own terms and privacy policy: your question, the recent chat, the matching PluginTidy documentation and the diagnostic counts above go to the provider you chose, and the support service is asked only for the documentation, never your question. If that local answer fails and your site is authorised to use the support service, the question and recent chat are sent to the support service as described above. 126 125 127 Terms of service: https://publisherkits.com/terms/ 126 128 Privacy policy: https://publisherkits.com/privacy-policy/ 127 129 128 130 == Changelog == 131 132 = 0.18.0 = 133 * Updated Freemius SDK to the latest version. 134 * New: the Help assistant now answers on an AI provider you have already connected to your site (Settings > Connectors, WordPress 7.0 or later), at your own cost, instead of our support service. It runs on the WordPress core AI Client and reads no licence, token or audit data, so it works the same in the free and Pro builds. Where no provider is connected, the support service answers as before. 135 * Changed: the assistant's privacy notice now says that if a local answer fails, the question and recent chat are sent to the support service instead. 136 * PluginTidy still uses no AI to audit your plugins. Every reading in the audit remains a measurement. 129 137 130 138 = 0.17.0 = … … 141 149 == Upgrade Notice == 142 150 151 = 0.18.0 = 152 The Help assistant can now answer on the AI provider already connected to your site. The audit itself still uses no AI. 153 143 154 = 0.17.0 = 144 155 Adds the Support Copilot, a Help button that answers PluginTidy questions in place. Read-only, consent first, and it never sends your plugin list or audit results. -
publisherkits-plugintidy/trunk/vendor/freemius/includes/class-freemius.php
r3707999 r3749369 4541 4541 4542 4542 if ( $this->is_user_in_admin() ) { 4543 if ( $this->is_registered() && fs_request_has( 'purchase_completed' ) ) {4544 $this->_admin_notices->add_sticky(4545 sprintf(4546 /* translators: %s: License type (e.g. you have a professional license) */4547 $this->get_text_inline( 'You have purchased a %s license.', 'you-have-x-license' ),4548 fs_request_get( 'purchased_plan' )4549 ) .4550 sprintf(4551 $this->get_text_inline(" The %s's %sdownload link%s, license key, and installation instructions have been sent to %s. If you can't find the email after 5 min, please check your spam box.", 'post-purchase-email-sent-message' ),4552 $this->get_module_label( true ),4553 ( FS_Plugin::is_valid_id( $this->get_bundle_id() ) ? "products' " : '' ),4554 ( FS_Plugin::is_valid_id( $this->get_bundle_id() ) ? 's' : '' ),4555 sprintf(4556 '<strong>%s</strong>',4557 fs_request_get( 'purchase_email' )4558 )4559 ),4560 'plan_purchased',4561 $this->get_text_x_inline( 'Yee-haw', 'interjection expressing joy or exuberance', 'yee-haw' ) . '!'4562 );4563 }4564 4565 4543 if ( $this->is_addon() ) { 4566 4544 if ( ! $this->is_parent_plugin_installed() ) { … … 4864 4842 $code = empty( $result->error->code ) ? '' : " Code: {$result->error->code}"; 4865 4843 4866 $error_message .= "<b>{$result->error->message}{$code}</b>"; 4844 // We do not send HTML from exceptions from Freemius servers, but we escape it nonetheless. 4845 $error_message .= "<b>" . esc_html( $result->error->message . $code ) . "</b>"; 4867 4846 } else { 4868 $error_message .= var_export( $result, true);4847 $error_message .= esc_html( var_export( $result, true ) ); 4869 4848 } 4870 4849 … … 6798 6777 } 6799 6778 6779 $escaped_email_address = esc_html( $email_address ); 6780 6800 6781 $formatted_message_args = array( 6801 6782 "<b>{$this->get_plugin_name()}</b>", 6802 "<b>{$e mail_address}</b>",6783 "<b>{$escaped_email_address}</b>", 6803 6784 ); 6804 6785 … … 6845 6826 6846 6827 $formatted_message_args[] = ( ! empty( $support_email_address ) ) ? 6847 ( "<b> {$support_email_address}</b>" ) :6828 ( "<b>" . esc_html( $support_email_address ) . "</b>" ) : 6848 6829 $this->get_text_x_inline( 6849 6830 "the product's support email address", … … 17565 17546 } 17566 17547 17548 // Only site administrators who can activate plugins may opt-in. 17549 if ( ! $this->is_user_admin() ) { 17550 return; 17551 } 17552 17567 17553 $has_pending_activation_confirmation_param = fs_request_has( 'pending_activation' ); 17568 17554 … … 18144 18130 $this->_admin_notices->add( 18145 18131 sprintf( $this->get_text_inline( 'Couldn\'t activate %s.', 'could-not-activate-x' ), $this->get_plugin_name() ) . ' ' . 18146 $this->get_text_inline( 'Please contact us with the following message:', 'contact-us-with-error-message' ) . ' ' . '<b>' . $result->error->message. '</b>',18132 $this->get_text_inline( 'Please contact us with the following message:', 'contact-us-with-error-message' ) . ' ' . '<b>' . esc_html( $result->error->message ) . '</b>', 18147 18133 $this->get_text_x_inline( 'Oops', 'exclamation', 'oops' ) . '...', 18148 18134 'error' … … 18264 18250 $this->_admin_notices->add( 18265 18251 sprintf( $this->get_text_inline( 'Couldn\'t activate %s.', 'could-not-activate-x' ), $this->get_plugin_name() ) . ' ' . 18266 $this->get_text_inline( 'Please contact us with the following message:', 'contact-us-with-error-message' ) . ' ' . '<b>' . $error_message. '</b>',18252 $this->get_text_inline( 'Please contact us with the following message:', 'contact-us-with-error-message' ) . ' ' . '<b>' . esc_html( $error_message ) . '</b>', 18267 18253 $this->get_text_x_inline( 'Oops', 'exclamation', 'oops' ) . '...', 18268 18254 'error' … … 18441 18427 $this->_admin_notices->add( 18442 18428 sprintf( $this->get_text_inline( 'Couldn\'t activate %s.', 'could-not-activate-x' ), $this->get_plugin_name() ) . ' ' . 18443 $this->get_text_inline( 'Please contact us with the following message:', 'contact-us-with-error-message' ) . ' ' . '<b>' . $parent_install->error->message. '</b>',18429 $this->get_text_inline( 'Please contact us with the following message:', 'contact-us-with-error-message' ) . ' ' . '<b>' . esc_html( $parent_install->error->message ) . '</b>', 18444 18430 $this->get_text_x_inline( 'Oops', 'exclamation', 'oops' ) . '...', 18445 18431 'error' … … 21159 21145 // Authentication params are broken. 21160 21146 $this->_admin_notices->add( 21161 $this->get_text_inline( 'It seems like one of the authentication parameters is wrong. Update your Public Key, Secret Key & User ID, and try again.', 'wrong-authentication-param-message' ) . '<br> ' . $this->get_text_inline( 'Error received from the server:', 'server-error-message' ) . var_export( $result->error, true),21147 $this->get_text_inline( 'It seems like one of the authentication parameters is wrong. Update your Public Key, Secret Key & User ID, and try again.', 'wrong-authentication-param-message' ) . '<br> ' . $this->get_text_inline( 'Error received from the server:', 'server-error-message' ) . esc_html( var_export( $result->error, true ) ), 21162 21148 '', 21163 21149 'error' … … 21480 21466 sprintf( 21481 21467 '<a href="%s">%s</a>', 21482 $this->contact_url( 'bug'),21468 esc_url( $this->contact_url( 'bug' ) ), 21483 21469 $this->get_text_inline( 'Please contact us here', 'contact-us-here' ) 21484 21470 ), … … 21573 21559 '<div class="fs-api-request-error-details" style="display: none">' . 21574 21560 '<strong>' . $this->get_text_inline( 'Error received from the server:', 'server-error-message' ) . '</strong><br>' . 21575 $result->error->message.21561 esc_html( $result->error->message ) . 21576 21562 '</div>'; 21577 21563 … … 21678 21664 $this->get_text_inline( 'It looks like the license could not be activated.', 'license-activation-failed-message' ), 21679 21665 ( is_object( $license ) && isset( $license->error ) ? 21680 $license->error->message:21666 esc_html( $license->error->message ) : 21681 21667 sprintf( '%s<br><code>%s</code>', 21682 21668 $this->get_text_inline( 'Error received from the server:', 'server-error-message' ), 21683 var_export( $license, true)21669 esc_html( var_export( $license, true ) ) 21684 21670 ) 21685 21671 ) … … 21769 21755 $this->_admin_notices->add( 21770 21756 $this->get_text_inline( 'It looks like the license deactivation failed.', 'license-deactivation-failed-message' ) . '<br> ' . 21771 $this->get_text_inline( 'Error received from the server:', 'server-error-message' ) . ' ' . var_export( $license->error, true),21757 $this->get_text_inline( 'Error received from the server:', 'server-error-message' ) . ' ' . esc_html( var_export( $license->error, true ) ), 21772 21758 $hmm_text, 21773 21759 'error' … … 22418 22404 sprintf( 22419 22405 '<a href="%s" target="_blank" rel="noopener">%s</a>', 22420 $this->get_account_url( 'download_latest'),22406 esc_url( $this->get_account_url( 'download_latest' ) ), 22421 22407 sprintf( 22422 22408 /* translators: %s: plan name (e.g. latest "Professional" version) */ … … 22836 22822 $this->_admin_notices->add( sprintf( 22837 22823 $this->get_text_inline( 'Verification mail was just sent to %s. If you can\'t find it after 5 min, please check your spam box.', 'verification-email-sent-message' ), 22838 sprintf( '<a href="mailto:%1$s">%2$s</a>', esc_url( $this->_user->email ), $this->_user->email)22824 sprintf( '<a href="mailto:%1$s">%2$s</a>', esc_url( $this->_user->email ), esc_html( $this->_user->email ) ) 22839 22825 ) ); 22840 22826 } else { … … 23160 23146 if ( $this->init_change_owner( $candidate_email, $transfer_type ) ) { 23161 23147 if ( 'transfer' === $transfer_type ) { 23162 $this->_admin_notices->add( sprintf( $this->get_text_inline( 'A confirmation email was just sent to %s. The email owner must confirm the update within the next 4 hours.', 'change-owner-request-sent-x-transfer' ), '<b>' . $this->_user->email. '</b>' ) );23148 $this->_admin_notices->add( sprintf( $this->get_text_inline( 'A confirmation email was just sent to %s. The email owner must confirm the update within the next 4 hours.', 'change-owner-request-sent-x-transfer' ), '<b>' . esc_html( $this->_user->email ) . '</b>' ) ); 23163 23149 } else { 23164 $this->_admin_notices->add( sprintf( $this->get_text_inline( 'A confirmation email was just sent to %s. You must confirm the update within the next 4 hours. If you cannot find the email, please check your spam folder.', 'change-owner-request-sent-x' ), '<b>' . $this->_user->email. '</b>' ) );23150 $this->_admin_notices->add( sprintf( $this->get_text_inline( 'A confirmation email was just sent to %s. You must confirm the update within the next 4 hours. If you cannot find the email, please check your spam folder.', 'change-owner-request-sent-x' ), '<b>' . esc_html( $this->_user->email ) . '</b>' ) ); 23165 23151 } 23166 23152 } … … 23174 23160 } 23175 23161 23176 $this->_admin_notices->add( sprintf( $this->get_text_inline( 'Thanks for confirming the ownership change. An email was just sent to %s for final approval.', 'change-owner-request_owner-confirmed' ), '<b>' . $candidate_email. '</b>' ) );23162 $this->_admin_notices->add( sprintf( $this->get_text_inline( 'Thanks for confirming the ownership change. An email was just sent to %s for final approval.', 'change-owner-request_owner-confirmed' ), '<b>' . esc_html( $candidate_email ) . '</b>' ) ); 23177 23163 break; 23178 23164 case 'candidate_confirmed': … … 23180 23166 if ( $this->complete_change_owner() ) { 23181 23167 $this->_admin_notices->add_sticky( 23182 sprintf( $this->get_text_inline( '%s is the new owner of the account.', 'change-owner-request_candidate-confirmed' ), '<b>' . $this->_user->email. '</b>' ),23168 sprintf( $this->get_text_inline( '%s is the new owner of the account.', 'change-owner-request_candidate-confirmed' ), '<b>' . esc_html( $this->_user->email ) . '</b>' ), 23183 23169 'ownership_changed', 23184 23170 $this->get_text_x_inline( 'Congrats', 'as congratulations', 'congrats' ) . '!' -
publisherkits-plugintidy/trunk/vendor/freemius/includes/managers/class-fs-clone-manager.php
r3707999 r3749369 1224 1224 1225 1225 foreach ( $product_titles as $product_title ) { 1226 $products_list .= sprintf( '<li>%s</li>', $product_title);1226 $products_list .= sprintf( '<li>%s</li>', esc_html( $product_title ) ); 1227 1227 } 1228 1228 … … 1232 1232 $sites_list .= sprintf( 1233 1233 '<li><a href="%s" target="_blank">%s</a></li>', 1234 $site_url,1235 fs_strip_url_protocol( $site_url)1234 esc_url( $site_url ), 1235 esc_html( fs_strip_url_protocol( $site_url ) ) 1236 1236 ); 1237 1237 } … … 1243 1243 sprintf( 1244 1244 '<a href="%s" target="_blank">%s</a>', 1245 $site_urls[0],1246 fs_strip_url_protocol( $site_urls[0])1245 esc_url( $site_urls[0] ), 1246 esc_html( fs_strip_url_protocol( $site_urls[0] ) ) 1247 1247 ) : 1248 1248 fs_text_inline( 'the above-mentioned sites', 'above-mentioned-sites' )) . '</b>'; … … 1250 1250 $current_site_link = sprintf( 1251 1251 '<b><a href="%s" target="_blank">%s</a></b>', 1252 $current_url,1253 fs_strip_url_protocol( $current_url)1252 esc_url( $current_url ), 1253 esc_html( fs_strip_url_protocol( $current_url ) ) 1254 1254 ); 1255 1255 … … 1324 1324 // %1$s 1325 1325 ( 1 === $total_products ? 1326 sprintf( '<b>%s</b>', $product_titles[0]) :1326 sprintf( '<b>%s</b>', esc_html( $product_titles[0] ) ) : 1327 1327 ( 1 === $total_sites ? 1328 1328 sprintf( '<div>%s</div>', $products_list ) : -
publisherkits-plugintidy/trunk/vendor/freemius/start.php
r3707999 r3749369 16 16 * @var string 17 17 */ 18 // BWD LOCAL PATCH (2026-08-12): guarded the unguarded count( $this->_plans )19 // calls in includes/class-freemius.php against a non-array (bool) value, which20 // fataled on PHP 8 during the multisite network-admin opt-in (_store_plans).21 // Freemius 2.13.4 / master carry the bug unfixed. Version nudged to 2.13.5 so22 // this fixed copy wins the newest-wins loader over unpatched 2.13.4 copies on23 // the same site. See vendor/freemius/BWD-PATCH-NOTES.md. Revert to the stock24 // version string when Freemius ships an official fix.25 18 $this_sdk_version = '2.13.5'; 26 19 -
publisherkits-plugintidy/trunk/vendor/freemius/templates/add-ons.php
r3707999 r3749369 123 123 $open_addon = ( $open_addon || ( $open_addon_slug === $addon->slug ) ); 124 124 125 $price = 0; 126 $has_trial = false; 125 $price = 0; 126 $min_price = 999999; 127 $has_trial = false; 127 128 $has_free_plan = false; 128 129 $has_paid_plan = false; … … 142 143 } 143 144 144 145 145 $has_paid_plan = true; 146 146 $has_trial = $has_trial || ( is_numeric( $plan->trial_period ) && ( $plan->trial_period > 0 ) ); 147 147 148 $min_price = 999999;149 148 foreach ( $plan->pricing as $pricing ) { 150 149 $pricing = new FS_Pricing( $pricing ); … … 164 163 } else if ( $pricing->has_monthly() ) { 165 164 $min_price = min( $min_price, 12 * $pricing->monthly_price ); 166 } 165 } else if ( $pricing->has_lifetime() ) { 166 $min_price = min( $min_price, $pricing->lifetime_price ); 167 } 167 168 } 168 169 … … 170 171 $price = $min_price; 171 172 } 172 173 173 } 174 174 } -
publisherkits-plugintidy/trunk/vendor/freemius/templates/pricing.php
r3707999 r3749369 25 25 26 26 $context_params = array( 27 // Plugin information 27 28 'plugin_id' => $fs->get_id(), 28 29 'plugin_public_key' => $fs->get_public_key(), 29 30 'plugin_version' => $fs->get_plugin_version(), 31 32 // React pricing app context 33 'contact_url' => $fs->contact_url(), 34 'is_production' => ( defined( 'WP_FS__IS_PRODUCTION_MODE' ) ? WP_FS__IS_PRODUCTION_MODE : null ), 35 'menu_slug' => $fs->get_menu_slug(), 36 'mode' => 'dashboard', 37 'fs_wp_endpoint_url' => WP_FS__ADDRESS, 38 'request_handler_url' => admin_url( 39 'admin-ajax.php?' . http_build_query( array( 40 'module_id' => $fs->get_id(), 41 'action' => $fs->get_ajax_action( 'pricing_ajax_action' ), 42 'security' => $fs->get_ajax_security( 'pricing_ajax_action' ) 43 ) ) 44 ), 45 'selector' => '#fs_pricing_wrapper', 46 'unique_affix' => $fs->get_unique_affix(), 47 'show_annual_in_monthly' => $fs->apply_filters( 'pricing/show_annual_in_monthly', true ), 48 'license' => $fs->has_active_valid_license() ? $fs->_get_license() : null, 49 'plugin_icon' => $fs->get_local_icon_url(), 50 'disable_single_package' => $fs->apply_filters( 'pricing/disable_single_package', false ), 30 51 ); 31 52 … … 55 76 } 56 77 57 $query_params = array_merge( $ context_params, $_GET, array(78 $query_params = array_merge( $_GET, array( 58 79 'next' => $fs->_get_sync_license_url( false, false ), 59 'plugin_version' => $fs->get_plugin_version(),60 80 // Billing cycle. 61 81 'billing_cycle' => fs_request_get( 'billing_cycle', WP_FS__PERIOD_ANNUALLY ), … … 64 84 'discounts_model' => $fs->apply_filters( 'pricing/discounts_model', 'absolute' ), 65 85 ) ); 86 87 $has_tabs = $fs->_add_tabs_before_content(); 88 89 if ( $has_tabs ) { 90 $query_params['tabs'] = 'true'; 91 } 92 93 $pricing_config = array_merge( $query_params, $context_params ); 66 94 67 95 $pricing_js_url = fs_asset_url( $fs->get_pricing_js_path() ); … … 73 101 wp_enqueue_style( 'freemius-pricing', fs_asset_url( $pricing_css_path ) ); 74 102 } 75 76 $has_tabs = $fs->_add_tabs_before_content();77 78 if ( $has_tabs ) {79 $query_params['tabs'] = 'true';80 }81 103 ?> 82 104 <div id="fs_pricing" class="wrap fs-section fs-full-size-wrapper"> 83 105 <div id="fs_pricing_wrapper" data-public-url="<?php echo trailingslashit( dirname( $pricing_js_url ) ) ?>"></div> 84 <?php 85 $pricing_config = array_merge( array( 86 'contact_url' => $fs->contact_url(), 87 'is_production' => ( defined( 'WP_FS__IS_PRODUCTION_MODE' ) ? WP_FS__IS_PRODUCTION_MODE : null ), 88 'menu_slug' => $fs->get_menu_slug(), 89 'mode' => 'dashboard', 90 'fs_wp_endpoint_url' => WP_FS__ADDRESS, 91 'request_handler_url' => admin_url( 92 'admin-ajax.php?' . http_build_query( array( 93 'module_id' => $fs->get_id(), 94 'action' => $fs->get_ajax_action( 'pricing_ajax_action' ), 95 'security' => $fs->get_ajax_security( 'pricing_ajax_action' ) 96 ) ) 97 ), 98 'selector' => '#fs_pricing_wrapper', 99 'unique_affix' => $fs->get_unique_affix(), 100 'show_annual_in_monthly' => $fs->apply_filters( 'pricing/show_annual_in_monthly', true ), 101 'license' => $fs->has_active_valid_license() ? $fs->_get_license() : null, 102 'plugin_icon' => $fs->get_local_icon_url(), 103 'disable_single_package' => $fs->apply_filters( 'pricing/disable_single_package', false ), 104 ), $query_params ); 105 106 wp_add_inline_script( 'freemius-pricing', 'Freemius.pricing.new( ' . json_encode( $pricing_config ) . ' )' ); 107 ?> 106 <?php wp_add_inline_script( 'freemius-pricing', 'Freemius.pricing.new( ' . json_encode( $pricing_config ) . ' )' ); ?> 108 107 </div> 109 108 <?php
Note:
See TracChangeset
for help on using the changeset viewer.
![(please configure the [header_logo] section in trac.ini)](/chrome/site/your_project_logo.png)