sun
Forum Replies Created
-
Sounds promising! An AJAX callback to only search for specific posts given a search term would resolve the problem.
Any ETA on the new release? We’re trying to determine how to move forward with the project.
Forum: Plugins
In reply to: [WooCommerce] Woocommerce plugin making site slow / too much queriesAnother issue touching on the same topic:
https://github.com/woocommerce/woocommerce/issues/33988Forum: Plugins
In reply to: [WooCommerce] Woocommerce plugin making site slow / too much queriesThe cause for this are the WooCommerce Onboarding Tasks. Possibly only in combination with translation plugins like WPML.
The Onboarding Tasks are performing slow queries to determine e.g. whether the shop has any physical products.
Automattic\WooCommerce\Admin\Features\OnboardingTasks\Tasks\Shipping::has_physical_products()looks innocent like this:count( wc_get_products( array( 'virtual' => false, 'limit' => 1, ) ) )But the query turns into this monster:
SELECT wp_posts.ID FROM wp_posts LEFT JOIN wp_term_relationships ON (wp_posts.ID = wp_term_relationships.object_id) JOIN wp_icl_translations wpml_translations ON wp_posts.ID = wpml_translations.element_id AND wpml_translations.element_type = CONCAT('post_', wp_posts.post_type) WHERE 1=1 AND ( wp_term_relationships.term_taxonomy_id IN (102,103,104,105) ) AND wp_posts.post_type = 'product' AND ((wp_posts.post_status = 'publish')) AND ( ( ( wpml_translations.language_code = 'de' OR 0 ) AND wp_posts.post_type IN ('post','page','attachment','wp_block','wp_template','wp_template_part','wp_navigation','event','reference-highlight','reference','acf-field-group','product','product_variation','ep-pointer' ) ) OR wp_posts.post_type NOT IN ('post','page','attachment','wp_block','wp_template','wp_template_part','wp_navigation','event','reference-highlight','reference','acf-field-group','product','product_variation','ep-pointer' ) ) GROUP BY wp_posts.ID ORDER BY wp_posts.post_date DESC LIMIT 0, 1Each of these queries takes roughly 0.5 seconds on an (highly optimized) larger database.
They are executed multiple times with different conditions for different tasks, so WooCommerce slows down the backend page rendering of all backend pages by several seconds in total.
A recent version fixed the case of the Shipping Onboarding Task, but two other cases are still remaining:
Products::has_products()– which is invoked twice!TaskLists::setup_tasks_remaining()invokes the Products onboarding task two times, so the same expensive query is run twice.The problem is also documented in this blog post: https://www.wpintense.com/2022/03/11/how-to-fix-slow-woocommerce-6-01-for-wp-admin-pages/
As all of those queries appear to be count queries, maybe WooCommerce should look into using
wp_count_posts()instead. At least WPML seems to implement special handling for count queries (as opposed to regular queries).What’s worst about this is that the WooCommerce Onboarding isn’t even visible on all backend pages – to my knowledge it only appears on the WordPress Dashboard.
So in addition to fixing the problematic queries themselves,
Automattic\WooCommerce\Admin\Features\OnboardingTasks\TaskLists::init()should really limit all of this to the Dashboard page only.In fact, it’s not clear to me why the starting point for TaskLists are the
initandadmin_inithooks – instead of the WooCommerce Onboarding admin meta box.The whole Onboarding code should not run at all if the admin meta box is not enabled.
Forum: Plugins
In reply to: [WooCommerce Stripe Payment Gateway] Subscriptions with SEPA paymentsThe problem still exists after updating all plugins to the latest versions.
A subscription paid with SEPA / direct debit is immediately moved to cancelled instead of pending-cancellation.
Forum: Plugins
In reply to: [WooCommerce Stripe Payment Gateway] Subscriptions with SEPA paymentsThis is happening with the WooCommerce Subscriptions plugin.
It seems like the Stripe plugin supports the subscription cancellation, but only for the credit card method thus far. The SEPA / direct debit method does not seem to use the trait for subscriptions.
https://github.com/woocommerce/woocommerce-gateway-stripe/search?q=subscription_cancellation
Wondering whether you would just need to add the trait to the direct debit class in the Stripe plugin?
- This reply was modified 4 years, 3 months ago by sun. Reason: Added technical details
Forum: Plugins
In reply to: [WooCommerce] Order sorting backendIn our case, it was the order-delivery-date plugin. It changes the sort order on the administrative orders page to be based on the custom delivery date for each order.
However, the plugin provides a configuration option on its own settings page to enable or disable the custom sorting.
We were able to identify the causing code by looking at the actual query being executed using the query-monitor plugin.
👍
Forum: Plugins
In reply to: [ACF: Search] Console Warninghttps://ww.wp.xz.cn/support/topic/php-7-1-warning-4/ is a duplicate of this issue.
You can apply the following simple change to fix the problem:
diff --git a/acf-search.php b/acf-search.php index e42da27f..68c4ad76 100644 --- a/acf-search.php +++ b/acf-search.php @@ -88,7 +88,7 @@ function acf_search_list_searcheable_acf(){ * see https://vzurczak.wordpress.com/2013/06/15/extend-the-default-wordpress-search/ * credits to Vincent Zurczak for the base query structure/spliting tags section */ - function acf_search_advanced_custom_search( $where, &$wp_query ) { + function acf_search_advanced_custom_search( $where, $wp_query ) { global $wpdb; if ( empty( $where ))Forum: Plugins
In reply to: [ACF: Search] PHP 7.1 WarningDuplicate of https://ww.wp.xz.cn/support/topic/console-warning-2/
Forum: Plugins
In reply to: [WooCommerce] Woocommerce Product importTo prevent the error “Invalid or duplicated SKU.” in the WooCommerce product import disable the mapping for the field “ID” by setting it to “Do not import” in the field mapping form of the 2nd step in the WooCommerce product import.
By attempting to import and map the (post) ID of your products by default, WooCommerce assumes that the database you’re exporting from and the database you’re importing into were the same databases at some point — which is most probably not the case in your case (and in 99% of all cases).
In order for the import to work without IDs, all of your products need to have a SKU. The WooCommerce product import falls back to using the SKUs of the products to map e.g. variations to their parent variable products.
Also, all of your SKUs need to be unique. SKUs of variations need to differ from their parents. To double-check that your product data is actually correct, execute the following database queries and ensure that they do not produce any results:
-- Most basic check within postmeta (must pass) SELECT COUNT(p.ID) AS count, p.post_title FROM wp_posts p INNER JOIN wp_postmeta pm ON pm.post_id = p.ID AND pm.meta_key = '_sku' GROUP BY pm.meta_value HAVING count > 1; -- More granular results with actual product IDs to check SELECT p.ID, sku.meta_value AS sku, p.post_title, p.post_type FROM wp_posts p INNER JOIN wp_postmeta sku ON sku.post_id = p.ID AND sku.meta_key = '_sku' INNER JOIN wp_postmeta dupe ON dupe.post_id <> sku.post_id AND dupe.meta_key = '_sku' WHERE dupe.meta_value = sku.meta_value GROUP BY p.ID;Also make sure to use the action “Remove orphan product variations from database” in the Tools screen of the WooCommerce Settings page. The action has been introduced in a recent version and removes product variations whose parent variable products no longer exist in the database, but which still exist for unknown reasons (invisibly) in the database and cannot be seen or reached through the backend user interface.
Forum: Plugins
In reply to: [WooCommerce] Invalid or duplicated SKU.To prevent the error “Invalid or duplicated SKU.” in the WooCommerce product import disable the mapping for the field “ID” by setting it to “Do not import” in the field mapping form of the 2nd step in the WooCommerce product import.
By attempting to import and map the (post) ID of your products by default, WooCommerce assumes that the database you’re exporting from and the database you’re importing into were the same databases at some point — which is most probably not the case in your case (and in 99% of all cases).
In order for the import to work without IDs, all of your products need to have a SKU. The WooCommerce product import falls back to using the SKUs of the products to map e.g. variations to their parent variable products.
Also, all of your SKUs need to be unique. SKUs of variations need to differ from their parents. To double-check that your product data is actually correct, execute the following database queries and ensure that they do not produce any results:
-- Most basic check within postmeta (must pass) SELECT COUNT(p.ID) AS count, p.post_title FROM wp_posts p INNER JOIN wp_postmeta pm ON pm.post_id = p.ID AND pm.meta_key = '_sku' GROUP BY pm.meta_value HAVING count > 1; -- More granular results with actual product IDs to check SELECT p.ID, sku.meta_value AS sku, p.post_title, p.post_type FROM wp_posts p INNER JOIN wp_postmeta sku ON sku.post_id = p.ID AND sku.meta_key = '_sku' INNER JOIN wp_postmeta dupe ON dupe.post_id <> sku.post_id AND dupe.meta_key = '_sku' WHERE dupe.meta_value = sku.meta_value GROUP BY p.ID;Also make sure to use the action “Remove orphan product variations from database” in the Tools screen of the WooCommerce Settings page. The action has been introduced in a recent version and removes product variations whose parent variable products no longer exist in the database, but which still exist for unknown reasons (invisibly) in the database and cannot be seen or reached through the backend user interface.
The following patch resolves the issue by giving other code access to the class instance. It would be great if you could include this change in the next release.
diff --git a/language-fallback.php b/language-fallback.php index bf363a1..7b7d27d 100644 --- a/language-fallback.php +++ b/language-fallback.php @@ -13,6 +13,8 @@ class Language_Fallback { + private static = $instance; + /** * used to store the current locale e.g. "de_DE" * @@ -28,6 +30,7 @@ class Language_Fallback { private $fallback_locale; function __construct() { + self::$instance = $this; // get current locale $this->locale = get_locale(); @@ -48,6 +51,13 @@ class Language_Fallback { load_plugin_textdomain( 'language-fallback', false, dirname( plugin_basename( __FILE__ ) ) . '/languages' ); } + public static function getInstance() { + if (!isset(self::$instance)) { + new self(); + } + return self::$instance; + } + /** * A function to check if the requested mofile exists and if not, it checks if a mofile for the fallback locale exists *- This reply was modified 9 years, 3 months ago by sun. Reason: Removed windows line ending markers from diff
Forum: Plugins
In reply to: [Etsy Shop] Use uploads folder for writing cache filesAny feedback?
Forum: Plugins
In reply to: [Etsy Shop] Use uploads folder for writing cache filesdiff --git a/wp-content/plugins/etsy-shop/etsy-shop.php b/wp-content/plugins/etsy-shop/etsy-shop.php index 4f289e0..b44dd39 100644 --- a/wp-content/plugins/etsy-shop/etsy-shop.php +++ b/wp-content/plugins/etsy-shop/etsy-shop.php @@ -28,7 +28,6 @@ Version: 0.18 */ /* Roadmap to version 1.x - * TODO: touch() file in tmp folder * TODO: reset cache function * TODO: edit cache life * TODO: allow more than 100 items @@ -39,7 +38,7 @@ Version: 0.18 */ define( 'ETSY_SHOP_VERSION', '0.18'); -define( 'ETSY_SHOP_CACHE_LIFE', 21600 ); // 6 hours in seconds +define( 'ETSY_SHOP_CACHE_LIFE', 6 * HOUR_IN_SECONDS ); // load translation add_action( 'init', 'etsy_shop_load_translation_file' ); @@ -264,38 +263,28 @@ function etsy_shop_shortcode( $atts ) { add_shortcode( 'etsy-shop', 'etsy_shop_shortcode' ); function etsy_shop_getShopSectionListings( $etsy_shop_id, $etsy_section_id, $language ) { - $etsy_cache_file = etsy_shop_get_data_dir().'/'.$etsy_shop_id.'-'.$etsy_section_id.'_cache.json'; - - // if no cache file exist - if (!file_exists( $etsy_cache_file ) or ( time() - filemtime( $etsy_cache_file ) >= ETSY_SHOP_CACHE_LIFE ) or get_option( 'etsy_shop_debug_mode' ) ) { - // if language set - if ($language != null) { - $reponse = etsy_shop_api_request( "shops/$etsy_shop_id/sections/$etsy_section_id/listings/active", '&limit=100&includes=Images&language='.$language ); - } else { - $reponse = etsy_shop_api_request( "shops/$etsy_shop_id/sections/$etsy_section_id/listings/active", '&limit=100&includes=Images' ); + $transient_id = 'etsy-shop_' . $etsy_shop_id . '-' . $etsy_section_id; + if ($language) { + $transient_id .= '-' . $language; + } + if (get_option('etsy_shop_debug_mode') || !$response = get_transient($transient_id)) { + $api_parameters = '&limit=100&includes=Images'; + if ($language) { + $api_parameters .= '&language=' . $language; } - - if ( !is_wp_error( $reponse ) ) { - // if request OK - $tmp_file = $etsy_cache_file.rand().'.tmp'; - file_put_contents( $tmp_file, $reponse ); - rename( $tmp_file, $etsy_cache_file ); - } else { - // return WP_Error - return $reponse; + $response = etsy_shop_api_request("shops/$etsy_shop_id/sections/$etsy_section_id/listings/active", $api_parameters); + if (is_wp_error($response)) { + return $response; } - } else { - // read cache file - $reponse = file_get_contents( $etsy_cache_file ); + set_transient($transient_id, $response, ETSY_SHOP_CACHE_LIFE); } if ( get_option( 'etsy_shop_debug_mode' ) ) { - $file_content = file_get_contents( $etsy_cache_file ); - print_r( '<h3>--- Etsy Cache File:' . $etsy_cache_file . ' ---</h3>' ); - print_r( $file_content ); + print_r( '<h3>--- Etsy Transient: ' . $transient_id . ' ---</h3>' ); + print_r( $response ); } - $data = json_decode( $reponse ); + $data = json_decode( $response ); return $data; } @@ -426,11 +415,14 @@ function etsy_shop_menu() { } function etsy_shop_options_page() { + global $wpdb; + // did the user is allowed? if ( !current_user_can( 'manage_options' ) ) { wp_die( __( 'You do not have sufficient permissions to access this page.', 'etsyshop' ) ); } + $updated = FALSE; if ( isset( $_POST['submit'] ) ) { // did the user enter an API Key? if ( isset( $_POST['etsy_shop_api_key'] ) ) { @@ -485,30 +477,11 @@ function etsy_shop_options_page() { } } - // delete cache file - if ( isset( $_GET['delete'] ) ) { - - // did a file was choosed? - if ( isset( $_GET['file'] ) ) { - $tmp_directory = etsy_shop_get_data_dir() . '/'; - - // REGEX for security! - $filename = str_replace( '.json', '', $_GET['file'] ); - $filename = preg_replace( '/[^a-zA-Z0-9-_]/', '', $filename ); - - $fullpath_filename = $tmp_directory . $filename . '.json'; - if ( file_exists( $fullpath_filename ) ) { - $deletion = unlink( $fullpath_filename ); - } else { - $deletion = false; - } - - if ( $deletion ) { - // and remember to note deletion to user - $deleted = true; - $deleted_file = $fullpath_filename; - } - } + // delete cache + $deleted = FALSE; + if (isset($_GET['delete']) && isset($_GET['transient_id'])) { + delete_transient($_GET['transient_id']); + $deleted = TRUE; } // grab the Etsy API key @@ -544,7 +517,7 @@ function etsy_shop_options_page() { } if ( $deleted ) { - echo '<div class="updated fade"><p><strong>'. __( 'Cache file deleted:', 'etsyshop' ) . ' ' . $deleted_file . '</strong></p></div>'; + echo '<div class="updated fade"><p><strong>'. __( 'Cache deleted:', 'etsyshop' ) . ' ' . $_GET['transient_id'] . '</strong></p></div>'; } // print the Options Page @@ -616,29 +589,35 @@ function etsy_shop_options_page() { <thead id="EtsyShopCacheTableHead"> <tr> <th><?php _e('Shop Section', 'etsyshop'); ?></th> - <th><?php _e('Filename', 'etsyshop'); ?></th> - <th><?php _e('Last update', 'etsyshop'); ?></th> + <th><?php _e('Transient ID', 'etsyshop'); ?></th> + <th><?php _e('Expires', 'etsyshop'); ?></th> <th></th> </tr> </thead> <?php - $files = glob( etsy_shop_get_data_dir().'/*.json' ); - $time_zone = get_option('timezone_string'); - date_default_timezone_set( $time_zone ); - foreach ($files as $file) { - // downgrade to support PHP 5.2.4 - //$etsy_shop_section = explode( "-", strstr(basename( $file ), '_cache.json', true ) ); - $etsy_shop_section = explode( "-", substr( basename( $file ), 0, strpos( basename( $file ), '_cache.json' ) ) ); - $etsy_shop_section_info = etsy_shop_getShopSection($etsy_shop_section[0], $etsy_shop_section[1]); + $transients = $wpdb->get_results("SELECT * FROM {$wpdb->options} WHERE option_name LIKE '_transient_etsy-shop_%'"); + foreach ($transients as $transient) { + $transient_id = substr($transient->option_name, strlen('_transient_')); + @list($shop_name, $shop_section_id, $language) = explode('-', substr($transient_id, strlen('etsy-shop_')), 3); + $etsy_shop_section_info = etsy_shop_getShopSection($shop_name, $shop_section_id, $language); + $expiration = $wpdb->get_var("SELECT option_value FROM {$wpdb->options} WHERE option_name = '_transient_timeout_{$transient_id}'"); if ( !is_wp_error( $etsy_shop_section_info ) ) { - echo '<tr><td>' . $etsy_shop_section[0] . ' / ' . $etsy_shop_section_info->results[0]->title . '</td><td>' . basename( $file ) . '</td><td>' . date( "Y-m-d H:i:s", filemtime( $file ) ) . '</td><td><a href="options-general.php?page=etsy-shop.php&delete&file=' . basename( $file ) . '" title="'. __('Delete cache file', 'etsyshop') .'"><span class="dashicons dashicons-trash"></span></a></td></tr>'; - // echo '<tr><td>' . $etsy_shop_section[0] . ' / ' . $etsy_shop_section_info->results[0]->title . '</td><td>' . basename( $file ) . '</td><td>' . date( "Y-m-d H:i:s", filemtime( $file ) ) . '</td><td><a href="" title="' . _e('Delete cache file', 'etsyshop'); . '"><span class="dashicons dashicons-trash"></span></a></td></tr>'; + echo '<tr> +<td>' . $shop_name . ' / ' . $etsy_shop_section_info->results[0]->title . '</td> +<td>' . $transient_id . '</td> +<td>' . date_i18n( "Y-m-d H:i:s", $expiration ) . '</td> +<td><a href="options-general.php?page=etsy-shop.php&delete&transient_id=' . urlencode($transient_id) . '" title="'. __('Delete cache', 'etsyshop') .'"><span class="dashicons dashicons-trash"></span></a></td> +</tr>'; } else { - echo '<tr><td>' . $etsy_shop_section[0] . ' / <span style="color:red;">Error on API Request</span>' . '</td><td>' . basename( $file ) . '</td><td>' . date( "Y-m-d H:i:s", filemtime( $file ) ) . '</td><td></td></tr>'; + echo '<tr> +<td>' . $shop_name . ' / <span style="color:red;">Error on API Request</span>' . '</td> +<td>' . $transient_id . '</td> +<td>' . ($expiration ? date_i18n( "Y-m-d H:i:s", $expiration ) : $expiration ) . '</td> +<td></td> +</tr>'; } } ?></table><?php } else { _e('You must enter your Etsy API Key to view cache status!', 'etsyshop'); } ?> - <p class="description"><?php _e( 'You may reset cache a any time by deleting files in tmp folder of the plugin.', 'etsyshop' ); ?></p> </td> </tr> </table> @@ -681,12 +660,3 @@ function etsy_shop_plugin_action_links( $links, $file ) { return $links; } -function etsy_shop_get_data_dir() { - $uploads_config = wp_upload_dir(NULL, FALSE); - $dir = $uploads_config['basedir'] . '/etsy-shop/tmp'; - if ( !file_exists($dir) ) { - wp_mkdir_p($dir); - } - return $dir; -} -Forum: Plugins
In reply to: [Etsy Shop] Use uploads folder for writing cache filesOr even better, use WordPress’s Transient API, which has been built exactly for jobs like this, as also explained here:
https://css-tricks.com/the-deal-with-wordpress-transients/My next comment will contain a patch that replaces the file-based cache with a transient-based cache.