| @@ -3052,8 +3052,34 @@ | ||
| 3052 | 3052 | } |
| 3053 | 3053 | } |
| 3054 | 3054 | } |
| 3055 | 3055 | |
| 3056 | + /** | |
| 3057 | + * Fires after a template has been imported from the cloud. | |
| 3058 | + * | |
| 3059 | + * WDesignKit's templates live in the cloud, so nothing local records that an import | |
| 3060 | + * happened — there is no post type, no option, nothing to count after the fact. This is the | |
| 3061 | + * only moment the information exists. | |
| 3062 | + * | |
| 3063 | + * @since 2.6.4 | |
| 3064 | + * | |
| 3065 | + * @param string $kind 'single' or 'kit'. | |
| 3066 | + * @param string $builder Builder the template was imported for, e.g. 'elementor'. | |
| 3067 | + * @param int $count How many templates this import brought in. | |
| 3068 | + */ | |
| 3069 | + // Only a completed import counts. The cloud's failure shape for this endpoint family sets | |
| 3070 | + // content => 'error' (see the sibling check in wdkit_import_kit_template() above) — that is | |
| 3071 | + // non-empty, so the previous `||` fired the counter on failed imports too. Require success | |
| 3072 | + // AND an absent/non-'error' content instead. | |
| 3073 | + if ( ! empty( $response['success'] ) && ( ! isset( $response['content'] ) || 'error' !== $response['content'] ) ) { | |
| 3074 | + do_action( | |
| 3075 | + 'wdkit_template_imported', | |
| 3076 | + 'import_kit_template' === $api_type ? 'kit' : 'single', | |
| 3077 | + isset( $_POST['builder'] ) ? sanitize_key( wp_unslash( $_POST['builder'] ) ) : '', | |
| 3078 | + 1 | |
| 3079 | + ); | |
| 3080 | + } | |
| 3081 | + | |
| 3056 | 3082 | wp_send_json( $response ); |
| 3057 | 3083 | wp_die(); |
| 3058 | 3084 | } |
| 3059 | 3085 | |
| @@ -3880,8 +3906,28 @@ | ||
| 3880 | 3906 | $output['description'] = $response['description']; |
| 3881 | 3907 | $output['data'] = $result; |
| 3882 | 3908 | $output['success'] = $response['success']; |
| 3883 | 3909 | |
| 3910 | + // Counts the IMPORT ACTION, not what it brought in. A kit import always counts as 1 kit, | |
| 3911 | + // no matter how many blocks/pages that kit contains — confirmed live: a single gutenberg | |
| 3912 | + // kit import recorded total=680, kinds.kit=680, because $template_ids for that call was a | |
| 3913 | + // 680-element array of the kit's own blocks and count( $template_ids ) counted every one of | |
| 3914 | + // them. A page-kit's *size* is not tracking's concern; "was a kit imported" is. | |
| 3915 | + // | |
| 3916 | + // The 'single' branch keeps a defensive fallback for the one shape this endpoint's own | |
| 3917 | + // $template_ids reliably takes when it is not a kit — a single {id, name, slug, thumb...} | |
| 3918 | + // object — where count() would likewise count JSON keys instead of "1 template imported". | |
| 3919 | + if ( ! empty( $output['success'] ) ) { | |
| 3920 | + $is_kit = ( '' !== $website_kit ); | |
| 3921 | + $import_count = $is_kit ? 1 : ( isset( $template_ids['id'] ) ? 1 : ( is_array( $template_ids ) ? count( $template_ids ) : 1 ) ); | |
| 3922 | + do_action( | |
| 3923 | + 'wdkit_template_imported', | |
| 3924 | + $is_kit ? 'kit' : 'single', | |
| 3925 | + sanitize_key( $builder ), | |
| 3926 | + $import_count | |
| 3927 | + ); | |
| 3928 | + } | |
| 3929 | + | |
| 3884 | 3930 | wp_send_json( $output ); |
| 3885 | 3931 | wp_die(); |
| 3886 | 3932 | } |
| 3887 | 3933 | |
| @@ -4366,8 +4412,15 @@ | ||
| 4366 | 4412 | Tpgb_Library()->remove_backend_dir_files(); |
| 4367 | 4413 | } |
| 4368 | 4414 | |
| 4369 | 4415 | clean_post_cache( $inserted_post ); |
| 4416 | + | |
| 4417 | + // This whole method imports exactly one section per call — unlike | |
| 4418 | + // wdkit_import_template()/wdkit_import_kit_template(), it never fired this hook | |
| 4419 | + // at all, so single-section imports (Header/Footer/CTA/etc., a primary import | |
| 4420 | + // path per the Template Type sidebar) were invisible to tracking entirely. | |
| 4421 | + do_action( 'wdkit_template_imported', 'single', sanitize_key( $editor ), 1 ); | |
| 4422 | + | |
| 4370 | 4423 | wp_send_json( |
| 4371 | 4424 | array( |
| 4372 | 4425 | $temp_id => $temp_detail, |
| 4373 | 4426 | 'description' => 'Yay! Your Section has been Successfully Imported.', |
| @@ -4505,8 +4558,12 @@ | ||
| 4505 | 4558 | } |
| 4506 | 4559 | |
| 4507 | 4560 | \Elementor\Plugin::$instance->files_manager->clear_cache(); |
| 4508 | 4561 | |
| 4562 | + // See the matching note in the Gutenberg branch above — this method never | |
| 4563 | + // fired the tracking hook for either editor. | |
| 4564 | + do_action( 'wdkit_template_imported', 'single', 'elementor', 1 ); | |
| 4565 | + | |
| 4509 | 4566 | wp_send_json( |
| 4510 | 4567 | array( |
| 4511 | 4568 | $temp_id => $temp_detail, |
| 4512 | 4569 | 'content' => $temp_con, |
| @@ -5040,12 +5097,73 @@ | ||
| 5040 | 5097 | } |
| 5041 | 5098 | |
| 5042 | 5099 | $response = json_decode( wp_json_encode( $response['data'] ), true ); |
| 5043 | 5100 | |
| 5101 | + $this->wdkit_cache_cloud_usage( $response ); | |
| 5102 | + | |
| 5044 | 5103 | wp_send_json( $response ); |
| 5045 | 5104 | wp_die(); |
| 5046 | 5105 | } |
| 5047 | 5106 | |
| 5107 | + /** | |
| 5108 | + * Caches the storage / credit figures this response carried. | |
| 5109 | + * | |
| 5110 | + * This handler is the ONLY place those numbers ever exist on the site: the cloud endpoint | |
| 5111 | + * authenticates with a user token that only a logged-in dashboard request carries, so the | |
| 5112 | + * analytics heartbeat — which runs on cron with no user at all — can never fetch them itself. | |
| 5113 | + * Caching them here is what lets Posimyth_Tracker_WDK report them, and it reports the cache's | |
| 5114 | + * age alongside so a stale reading is recognisable as one. | |
| 5115 | + * | |
| 5116 | + * Field names are probed rather than assumed: the cloud has renamed these before, and the | |
| 5117 | + * licence ability already carries six spellings of its own key field for the same reason. An | |
| 5118 | + * unrecognised shape simply caches nothing rather than storing a wrong number. | |
| 5119 | + * | |
| 5120 | + * Only the figures are kept. No token, no account id, no email — the analytics consent copy | |
| 5121 | + * promises non-sensitive data only, and this is read by the payload builder. | |
| 5122 | + * | |
| 5123 | + * @since 2.6.4 | |
| 5124 | + * | |
| 5125 | + * @param mixed $data Decoded `data` object from the credits endpoint. | |
| 5126 | + * @return void | |
| 5127 | + */ | |
| 5128 | + private function wdkit_cache_cloud_usage( $data ) { | |
| 5129 | + if ( ! is_array( $data ) ) { | |
| 5130 | + return; | |
| 5131 | + } | |
| 5132 | + | |
| 5133 | + $pick = static function ( $source, array $fields ) { | |
| 5134 | + foreach ( $fields as $field ) { | |
| 5135 | + if ( isset( $source[ $field ] ) && is_numeric( $source[ $field ] ) ) { | |
| 5136 | + return (float) $source[ $field ]; | |
| 5137 | + } | |
| 5138 | + } | |
| 5139 | + return null; | |
| 5140 | + }; | |
| 5141 | + | |
| 5142 | + $usage = array( | |
| 5143 | + 'storage_used' => $pick( $data, array( 'used_storage', 'storage_used', 'used_space' ) ), | |
| 5144 | + 'storage_total' => $pick( $data, array( 'total_storage', 'storage_total', 'storage', 'total_space' ) ), | |
| 5145 | + 'credit_used' => $pick( $data, array( 'used_credit', 'credit_used', 'used_credits' ) ), | |
| 5146 | + 'credit_total' => $pick( $data, array( 'total_credit', 'credit_total', 'credits', 'real_credit' ) ), | |
| 5147 | + ); | |
| 5148 | + | |
| 5149 | + $usage = array_filter( | |
| 5150 | + $usage, | |
| 5151 | + static function ( $value ) { | |
| 5152 | + return null !== $value; | |
| 5153 | + } | |
| 5154 | + ); | |
| 5155 | + | |
| 5156 | + if ( empty( $usage ) ) { | |
| 5157 | + return; | |
| 5158 | + } | |
| 5159 | + | |
| 5160 | + $usage['cached_at'] = gmdate( 'Y-m-d H:i:s' ); | |
| 5161 | + | |
| 5162 | + // Not autoloaded: read once a week by the heartbeat, never on a front-end request. | |
| 5163 | + update_option( 'wdkit_cloud_usage', $usage, false ); | |
| 5164 | + } | |
| 5165 | + | |
| 5048 | 5166 | public function wdkit_nxt_thembuilder_reset() { |
| 5049 | 5167 | $post_id = isset( $_POST['post_id'] ) ? sanitize_text_field( $_POST['post_id'] ) : ''; |
| 5050 | 5168 | $sections_layout = get_post_meta( $post_id, 'nxt-hooks-layout-sections', true ); |
| 5051 | 5169 | |
| @@ -5776,9 +5894,9 @@ | ||
| 5776 | 5894 | 'template' => isset( $get_setting['template'] ) ? $get_setting['template'] : true, |
| 5777 | 5895 | 'gutenberg_builder' => isset( $get_setting['gutenberg_builder'] ) ? $get_setting['gutenberg_builder'] : true, |
| 5778 | 5896 | 'gutenberg_core_builder' => isset( $get_setting['gutenberg_core_builder'] ) ? $get_setting['gutenberg_core_builder'] : false, |
| 5779 | 5897 | 'elementor_builder' => isset( $get_setting['elementor_builder'] ) ? $get_setting['elementor_builder'] : true, |
| 5780 | - 'bricks_builder' => isset( $get_setting['bricks_builder'] ) ? $get_setting['bricks_builder'] : false, | |
| 5898 | + 'bricks_builder' => isset( $get_setting['bricks_builder'] ) ? $get_setting['bricks_builder'] : true, | |
| 5781 | 5899 | 'gutenberg_template' => isset( $get_setting['gutenberg_template'] ) ? $get_setting['gutenberg_template'] : true, |
| 5782 | 5900 | 'elementor_template' => isset( $get_setting['elementor_template'] ) ? $get_setting['elementor_template'] : true, |
| 5783 | 5901 | 'code_snippet' => isset( $get_setting['code_snippet'] ) ? $get_setting['code_snippet'] : true, |
| 5784 | 5902 | 'cross_copy_paste' => isset( $get_setting['cross_copy_paste'] ) ? $get_setting['cross_copy_paste'] : false, |