Changeset 3468187
- Timestamp:
- 02/24/2026 03:38:59 AM (5 weeks ago)
- Location:
- pap-afiliados-pro/trunk
- Files:
-
- 14 edited
-
assets/css/papafpro-template-builder.css (modified) (1 diff)
-
assets/js/papafpro-linker-tracker.js (modified) (1 diff)
-
assets/js/papafpro-template-builder.js (modified) (6 diffs)
-
includes/api/class-papafpro-linker-api.php (modified) (2 diffs)
-
includes/blocks/class-papafpro-linker-block.php (modified) (5 diffs)
-
includes/class-papafpro-help-page.php (modified) (4 diffs)
-
includes/class-papafpro-template-builder.php (modified) (9 diffs)
-
includes/class-papafpro-template-render.php (modified) (3 diffs)
-
languages/pap-afiliados-pro-pt_BR.mo (modified) (previous)
-
languages/pap-afiliados-pro-pt_BR.po (modified) (2 diffs)
-
languages/pap-afiliados-pro.pot (modified) (2 diffs)
-
pap-afiliados-pro.php (modified) (3 diffs)
-
readme.txt (modified) (5 diffs)
-
views/papafpro-template-builder.php (modified) (1 diff)
Legend:
- Unmodified
- Added
- Removed
-
pap-afiliados-pro/trunk/assets/css/papafpro-template-builder.css
r3466192 r3468187 361 361 ========================================================================== */ 362 362 363 /* ========================================================================== 364 PRESET EDITING CONTEXT BAR 365 ========================================================================== */ 366 367 .papafpro-preset-editing-bar { 368 background: #fff3cd; 369 border: 1px solid #ffc107; 370 border-radius: 4px; 371 padding: 10px 16px; 372 margin-bottom: 16px; 373 display: flex; 374 align-items: center; 375 justify-content: space-between; 376 gap: 12px; 377 } 378 379 .papafpro-preset-editing-label { 380 font-size: 14px; 381 color: #664d03; 382 } 383 384 .papafpro-back-to-global { 385 white-space: nowrap; 386 } 387 363 388 /* Tablet: empilhar verticalmente */ 364 389 @media (max-width: 1024px) { -
pap-afiliados-pro/trunk/assets/js/papafpro-linker-tracker.js
r3466192 r3468187 28 28 }; 29 29 30 var url = papafpro_linker.rest_url + 'track-click ';30 var url = papafpro_linker.rest_url + 'track-click?_wpnonce=' + encodeURIComponent( papafpro_linker.rest_nonce ); 31 31 32 32 // Primary: sendBeacon with JSON blob (non-blocking). -
pap-afiliados-pro/trunk/assets/js/papafpro-template-builder.js
r3466192 r3468187 16 16 } 17 17 18 var ajaxUrl = papafproTemplateBuilder.ajax_url; 19 var nonce = papafproTemplateBuilder.nonce; 18 var ajaxUrl = papafproTemplateBuilder.ajax_url; 19 var nonce = papafproTemplateBuilder.nonce; 20 var currentPresetId = null; 21 var currentPresetName = null; 20 22 21 23 /** … … 59 61 60 62 /** 63 * Atualizar UI da barra de contexto (preset vs global). 64 * 65 * Mostra/esconde a barra e ajusta o texto do botão de salvar. 66 */ 67 function updatePresetEditingUI() { 68 var $bar = $( '#papafpro-preset-editing-bar' ); 69 var $nameEl = $( '#papafpro-preset-editing-name' ); 70 var $saveBtn = $( '#papafpro-save-settings' ); 71 var i18n = papafproTemplateBuilder.i18n || {}; 72 73 if ( currentPresetId ) { 74 $bar.show(); 75 if ( $nameEl.length ) { 76 $nameEl[0].textContent = currentPresetName || ''; 77 } 78 $saveBtn.text( 79 i18n.save_preset_label 80 ? i18n.save_preset_label.replace( '%s', currentPresetName ) 81 : 'Save Preset' 82 ); 83 } else { 84 $bar.hide(); 85 $nameEl.empty(); 86 $saveBtn.text( i18n.save_settings_label || 'Save Settings' ); 87 } 88 } 89 90 /** 61 91 * Coletar todos os campos do formulário. 62 92 * … … 70 100 var name = $( this ).attr( 'name' ); 71 101 var value = $( this ).val(); 72 if ( name && value) {102 if ( name ) { 73 103 data[ name ] = value; 74 104 } … … 187 217 e.preventDefault(); 188 218 189 var $btn = $( this ); 190 $btn.prop( 'disabled', true ); 191 192 var data = { 193 action: 'papafpro_save_settings', 194 nonce: nonce, 195 settings: collectFormData() 196 }; 197 198 $.post( ajaxUrl, data, function( response ) { 199 if ( response.success ) { 200 showNotice( response.data.message || 'Configurações salvas!', 'success' ); 201 } else { 202 showNotice( response.data.message || 'Erro ao salvar.', 'error' ); 203 } 204 }).fail( function() { 205 showNotice( 'Erro de conexão.', 'error' ); 206 }).always( function() { 207 $btn.prop( 'disabled', false ); 208 }); 219 var $btn = $( this ); 220 $btn.prop( 'disabled', true ); 221 222 var formData = collectFormData(); 223 224 if ( currentPresetId ) { 225 // Save to preset. 226 $.post( ajaxUrl, { 227 action: 'papafpro_update_preset', 228 nonce: nonce, 229 preset_id: currentPresetId, 230 settings: formData 231 }, function( response ) { 232 if ( response.success ) { 233 showNotice( response.data.message, 'success' ); 234 } else { 235 showNotice( 236 ( response.data && response.data.message ) || 'Error saving preset.', 237 'error' 238 ); 239 } 240 }).fail( function() { 241 showNotice( 'Erro de conexão.', 'error' ); 242 }).always( function() { 243 $btn.prop( 'disabled', false ); 244 }); 245 } else { 246 // Save global settings. 247 $.post( ajaxUrl, { 248 action: 'papafpro_save_settings', 249 nonce: nonce, 250 settings: formData 251 }, function( response ) { 252 if ( response.success ) { 253 showNotice( response.data.message, 'success' ); 254 } else { 255 showNotice( 256 ( response.data && response.data.message ) || 'Error saving settings.', 257 'error' 258 ); 259 } 260 }).fail( function() { 261 showNotice( 'Erro de conexão.', 'error' ); 262 }).always( function() { 263 $btn.prop( 'disabled', false ); 264 }); 265 } 209 266 }); 210 267 … … 285 342 if ( response.success && response.data.settings ) { 286 343 applySettings( response.data.settings ); 344 currentPresetId = id; 345 currentPresetName = response.data.preset_name || ''; 346 updatePresetEditingUI(); 287 347 showNotice( response.data.message || 'Preset carregado!', 'success' ); 288 348 } else { … … 291 351 }).fail( function() { 292 352 showNotice( 'Erro de conexão.', 'error' ); 353 }).always( function() { 354 $btn.prop( 'disabled', false ); 355 }); 356 }); 357 358 // ========================================================================= 359 // 6b. BACK TO GLOBAL 360 // ========================================================================= 361 362 $( document ).on( 'click', '#papafpro-back-to-global', function( e ) { 363 e.preventDefault(); 364 365 var $btn = $( this ); 366 $btn.prop( 'disabled', true ); 367 368 $.post( ajaxUrl, { 369 action: 'papafpro_load_preset', 370 nonce: nonce, 371 preset_id: 0 372 }, function( response ) { 373 if ( response.success && response.data.settings ) { 374 applySettings( response.data.settings ); 375 currentPresetId = null; 376 currentPresetName = null; 377 updatePresetEditingUI(); 378 var i18n = papafproTemplateBuilder.i18n || {}; 379 showNotice( 380 i18n.global_restored || 'Global settings restored.', 381 'success' 382 ); 383 } 293 384 }).always( function() { 294 385 $btn.prop( 'disabled', false ); -
pap-afiliados-pro/trunk/includes/api/class-papafpro-linker-api.php
r3466192 r3468187 55 55 56 56 add_action( 'rest_api_init', array( $this, 'register_routes' ) ); 57 add_filter( 'rest_authentication_errors', array( $this, 'bypass_cookie_auth_for_tracking' ), 999 ); 57 58 } 58 59 … … 93 94 public function check_edit_permission() { 94 95 return current_user_can( 'edit_posts' ); 96 } 97 98 /** 99 * Bypass cookie authentication errors for the track-click endpoint. 100 * 101 * WordPress REST cookie authentication (rest_cookie_check_errors) 102 * rejects requests that carry auth cookies without a valid nonce, 103 * returning WP_Error 'rest_cookie_invalid_nonce' (HTTP 403). 104 * This blocks navigator.sendBeacon() for logged-in users because 105 * sendBeacon cannot send custom headers (X-WP-Nonce) and cookies 106 * are sent automatically by the browser. 107 * 108 * This filter nullifies that specific error ONLY for the public 109 * track-click route, which uses permission_callback __return_true 110 * and relies on transient rate-limiting for anti-spam protection. 111 * 112 * @since 2.0.27 113 * @param WP_Error|null|true $errors Authentication result. 114 * @return WP_Error|null|true Unchanged or null for track-click route. 115 */ 116 public function bypass_cookie_auth_for_tracking( $errors ) { 117 // Only act when there is a cookie-nonce error. 118 if ( ! is_wp_error( $errors ) || 'rest_cookie_invalid_nonce' !== $errors->get_error_code() ) { 119 return $errors; 120 } 121 122 // Only bypass for our specific tracking route. 123 $rest_route = $GLOBALS['wp']->query_vars['rest_route'] ?? ''; 124 if ( '/papafpro/v1/track-click' !== $rest_route ) { 125 return $errors; 126 } 127 128 // Nullify the error — let permission_callback decide access. 129 return null; 95 130 } 96 131 -
pap-afiliados-pro/trunk/includes/blocks/class-papafpro-linker-block.php
r3466192 r3468187 51 51 'papafpro/linker', 52 52 array( 53 'api_version' => 2,53 'api_version' => 3, 54 54 'editor_script' => 'papafpro-linker-block-editor', 55 55 'render_callback' => array( $this, 'render_linker_block' ), … … 164 164 $this->maybe_enqueue_tracker(); 165 165 166 // Block wrapper attributes for theme layout alignment (api_version 2).166 // Block wrapper attributes for theme layout alignment (api_version 3). 167 167 $wrapper_attributes = get_block_wrapper_attributes( 168 168 array( … … 183 183 184 184 /** 185 * Ensure the linker tracker script is registered and localized. 186 * 187 * Normally PAPAFPRO_Shortcodes::register_assets() handles registration 188 * on the wp_enqueue_scripts hook. This method is a defensive fallback 189 * so that Block and Format Type enqueue paths are self-sufficient and 190 * do not silently fail if the primary registration did not run. 191 * 192 * @since 2.0.29 193 * @access private 194 */ 195 private function ensure_tracker_registered() { 196 if ( wp_script_is( 'papafpro-linker-tracker', 'registered' ) ) { 197 return; 198 } 199 200 wp_register_script( 201 'papafpro-linker-tracker', 202 PAPAFPRO_PLUGIN_URL . 'assets/js/papafpro-linker-tracker.js', 203 array(), 204 PAPAFPRO_VERSION, 205 true 206 ); 207 208 wp_localize_script( 209 'papafpro-linker-tracker', 210 'papafpro_linker', 211 array( 212 'rest_url' => esc_url_raw( rest_url( 'papafpro/v1/' ) ), 213 'rest_nonce' => wp_create_nonce( 'wp_rest' ), 214 ) 215 ); 216 } 217 218 /** 185 219 * Enqueue the linker tracker script on the frontend. 186 220 * 187 * Localization is handled by PAPAFPRO_Shortcodes::register_assets()188 * at registration time — this method only enqueues.221 * Ensures registration before enqueuing so this path works 222 * independently of PAPAFPRO_Shortcodes::register_assets(). 189 223 * 190 224 * @since 2.0.0 … … 192 226 */ 193 227 private function maybe_enqueue_tracker() { 228 $this->ensure_tracker_registered(); 194 229 wp_enqueue_script( 'papafpro-linker-tracker' ); 195 230 } … … 208 243 */ 209 244 public function maybe_enqueue_linker_from_content( $content ) { 210 if ( wp_script_is( 'papafpro-linker-tracker', 'registered' ) 211 && false !== strpos( $content, 'papafpro-linker' ) 212 ) { 245 if ( false !== strpos( $content, 'papafpro-linker' ) ) { 246 $this->ensure_tracker_registered(); 213 247 wp_enqueue_script( 'papafpro-linker-tracker' ); 214 248 } -
pap-afiliados-pro/trunk/includes/class-papafpro-help-page.php
r3466192 r3468187 185 185 ), 186 186 array( 187 'name' => 'preset ',187 'name' => 'preset_id', 188 188 'type' => 'preset', 189 189 'required' => false, … … 211 211 ), 212 212 array( 213 'name' => 'preset ',213 'name' => 'preset_id', 214 214 'type' => 'preset', 215 215 'required' => false, … … 243 243 ), 244 244 array( 245 'name' => 'preset ',245 'name' => 'preset_id', 246 246 'type' => 'preset', 247 247 'required' => false, … … 269 269 ), 270 270 array( 271 'name' => 'preset ',271 'name' => 'preset_id', 272 272 'type' => 'preset', 273 273 'required' => false, -
pap-afiliados-pro/trunk/includes/class-papafpro-template-builder.php
r3466192 r3468187 333 333 if ( $existing && $overwrite ) { 334 334 // UPDATE existente. 335 wp_cache_get( 'papafpro_preset_' . $existing->id, 'papafpro' );336 335 // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery -- Custom table, no WP API available. 337 336 $wpdb->update( … … 355 354 356 355 // INSERT novo. 357 wp_cache_get( 'papafpro_presets_list', 'papafpro' );358 356 // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery -- Custom table, no WP API available. 359 357 $wpdb->insert( … … 386 384 387 385 /** 386 * AJAX: Update existing preset settings by ID. 387 * 388 * Updates the settings JSON of a preset without changing its name. 389 * Used when the admin edits a loaded preset and clicks "Save". 390 * 391 * @since 2.0.1 392 */ 393 public function ajax_update_preset() { 394 check_ajax_referer( 'papafpro_template_builder', 'nonce' ); 395 396 if ( ! current_user_can( 'manage_options' ) ) { 397 wp_send_json_error( 398 array( 399 'message' => __( 'Insufficient permissions.', 'pap-afiliados-pro' ), 400 ) 401 ); 402 } 403 404 $preset_id = isset( $_POST['preset_id'] ) ? absint( $_POST['preset_id'] ) : 0; 405 406 if ( $preset_id < 1 ) { 407 wp_send_json_error( 408 array( 409 'message' => __( 'Invalid preset ID.', 'pap-afiliados-pro' ), 410 ) 411 ); 412 } 413 414 global $wpdb; 415 $table_name = $wpdb->prefix . 'papafpro_presets'; 416 417 // Verify preset exists. 418 $cache_key = 'papafpro_preset_' . $preset_id; 419 $existing = wp_cache_get( $cache_key, 'papafpro' ); 420 if ( false === $existing ) { 421 // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery -- Custom table, no WP API available. 422 $existing = $wpdb->get_row( 423 $wpdb->prepare( 424 'SELECT id, name, settings FROM %i WHERE id = %d', 425 $table_name, 426 $preset_id 427 ) 428 ); 429 if ( null !== $existing ) { 430 wp_cache_set( $cache_key, $existing, 'papafpro' ); 431 } 432 } 433 434 if ( ! $existing ) { 435 wp_send_json_error( 436 array( 437 'message' => __( 'Preset not found.', 'pap-afiliados-pro' ), 438 ) 439 ); 440 } 441 442 // Sanitize settings. 443 $raw_settings = isset( $_POST['settings'] ) && is_array( $_POST['settings'] ) 444 ? map_deep( wp_unslash( $_POST['settings'] ), 'sanitize_text_field' ) 445 : array(); 446 447 $sanitized = $this->sanitize_partial_settings( $raw_settings ); 448 449 // Update preset in database. 450 // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery -- Custom table, no WP API available. 451 $result = $wpdb->update( 452 $table_name, 453 array( 'settings' => wp_json_encode( $sanitized ) ), 454 array( 'id' => $preset_id ), 455 array( '%s' ), 456 array( '%d' ) 457 ); 458 459 // Invalidate cache. 460 wp_cache_delete( 'papafpro_preset_' . $preset_id, 'papafpro' ); 461 462 if ( false === $result ) { 463 wp_send_json_error( 464 array( 465 'message' => __( 'Failed to update preset.', 'pap-afiliados-pro' ), 466 ) 467 ); 468 } 469 470 wp_send_json_success( 471 array( 472 'message' => sprintf( 473 /* translators: %s: preset name */ 474 __( 'Preset "%s" updated successfully.', 'pap-afiliados-pro' ), 475 esc_html( $existing->name ) 476 ), 477 'preset_id' => $preset_id, 478 ) 479 ); 480 } 481 482 /** 388 483 * AJAX: Carregar preset. 389 484 * … … 405 500 $preset_id = isset( $_POST['preset_id'] ) ? absint( $_POST['preset_id'] ) : 0; 406 501 407 if ( $preset_id < 1 ) { 408 wp_send_json_error( 409 array( 410 'message' => __( 'Invalid preset ID.', 'pap-afiliados-pro' ), 502 // preset_id=0 returns global settings. 503 if ( 0 === $preset_id ) { 504 $settings = get_option( 'papafpro_settings', array() ); 505 wp_send_json_success( 506 array( 507 'preset_id' => 0, 508 'name' => '', 509 'preset_name' => '', 510 'settings' => $settings, 511 'message' => __( 'Global settings loaded.', 'pap-afiliados-pro' ), 411 512 ) 412 513 ); … … 446 547 wp_send_json_success( 447 548 array( 448 'preset_id' => absint( $preset->id ), 449 'name' => $preset->name, 450 'settings' => $settings, 549 'preset_id' => absint( $preset->id ), 550 'name' => $preset->name, 551 'preset_name' => esc_html( $preset->name ), 552 'settings' => $settings, 451 553 ) 452 554 ); … … 482 584 $table = $wpdb->prefix . 'papafpro_presets'; 483 585 484 wp_cache_get( 'papafpro_preset_' . $preset_id, 'papafpro' );485 586 // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery -- Custom table, no WP API available. 486 587 $deleted = $wpdb->delete( … … 726 827 727 828 foreach ( $ids as $id ) { 728 wp_cache_get( 'papafpro_preset_' . $id, 'papafpro' );729 829 // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery -- Custom table, no WP API available. 730 830 $result = $wpdb->delete( … … 815 915 $new_name = $this->generate_unique_preset_name( $original->name, $table ); 816 916 817 wp_cache_get( 'papafpro_presets_list', 'papafpro' );818 917 // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery -- Custom table, no WP API available. 819 918 $wpdb->insert( … … 960 1059 'ajax_url' => admin_url( 'admin-ajax.php' ), 961 1060 'nonce' => wp_create_nonce( 'papafpro_template_builder' ), 1061 'i18n' => array( 1062 /* translators: %s: preset name */ 1063 'save_preset_label' => __( 'Save Preset: %s', 'pap-afiliados-pro' ), 1064 'save_settings_label' => __( 'Save Settings', 'pap-afiliados-pro' ), 1065 'global_restored' => __( 'Global settings restored.', 'pap-afiliados-pro' ), 1066 ), 962 1067 ) 963 1068 ); -
pap-afiliados-pro/trunk/includes/class-papafpro-template-render.php
r3466192 r3468187 36 36 */ 37 37 public function render_card( $product_id, $settings = array() ) { 38 $merged_settings = $this->get_merged_settings( $settings ); 39 return $this->build_single_card( $product_id, $merged_settings ); 40 } 41 42 /** 43 * Build HTML for a single product card. 44 * 45 * Internal method that receives already-merged settings. 46 * MUST NOT call get_merged_settings() or get_option() directly. 47 * 48 * @since 2.0.1 49 * @access private 50 * @param int $product_id Product post ID. 51 * @param array $merged_settings Settings array already merged with defaults. 52 * @return string Card HTML or empty string. 53 */ 54 private function build_single_card( $product_id, $merged_settings ) { 38 55 $product_id = absint( $product_id ); 39 56 … … 56 73 } 57 74 58 $product_data = $this->get_product_data( $product_id ); 59 $merged_settings = $this->get_merged_settings( $settings ); 75 $product_data = $this->get_product_data( $product_id ); 60 76 61 77 return $this->build_card_html( $product_data, $merged_settings ); … … 83 99 84 100 foreach ( $product_ids as $product_id ) { 85 $cards_html .= $this-> render_card( absint( $product_id ), $merged_settings );101 $cards_html .= $this->build_single_card( absint( $product_id ), $merged_settings ); 86 102 } 87 103 -
pap-afiliados-pro/trunk/languages/pap-afiliados-pro-pt_BR.po
r3466192 r3468187 7 7 "Report-Msgid-Bugs-To: https://pap-afiliados-pro.com.br\n" 8 8 "POT-Creation-Date: 2026-02-18T00:00:00+00:00\n" 9 "PO-Revision-Date: 2026-02- 1800:00+0000\n"9 "PO-Revision-Date: 2026-02-23 00:00+0000\n" 10 10 "Last-Translator: Fernando Pimenta\n" 11 11 "Language-Team: Brazilian Portuguese\n" … … 540 540 msgstr "%d preset(s) duplicado(s) com sucesso." 541 541 542 #. translators: %s: preset name 543 #: includes/class-papafpro-template-builderphp:469 544 msgid "Preset \"%s\" updated successfully." 545 msgstr "Preset \"%s\" atualizado com sucesso." 546 547 #: includes/class-papafpro-template-builderphp:460 548 msgid "Failed to update preset." 549 msgstr "Falha ao atualizar preset." 550 551 #: includes/class-papafpro-template-builderphp:416 552 msgid "Global settings loaded." 553 msgstr "Configurações globais carregadas." 554 555 #. translators: %s: preset name 556 #: includes/class-papafpro-template-builderphp:964 557 msgid "Save Preset: %s" 558 msgstr "Salvar Preset: %s" 559 560 #: includes/class-papafpro-template-builderphp:966 561 msgid "Global settings restored." 562 msgstr "Configurações globais restauradas." 563 564 #. translators: %s: preset name placeholder, replaced dynamically by JavaScript 565 #: views/papafpro-template-builderphp:389 566 msgid "Editing preset: %s" 567 msgstr "Editando preset: %s" 568 569 #: views/papafpro-template-builderphp:395 570 msgid "Back to Global Settings" 571 msgstr "Voltar para Configurações Globais" 572 542 573 #: includes/class-papafpro-template-builderphp:871 543 574 #: views/papafpro-help-pagephp:57 -
pap-afiliados-pro/trunk/languages/pap-afiliados-pro.pot
r3466192 r3468187 5 5 "Project-Id-Version: PAP Afiliados Pro 2.0.0\n" 6 6 "Report-Msgid-Bugs-To: https://pap-afiliados-pro.com.br\n" 7 "POT-Creation-Date: 2026-02- 18T00:00:00+00:00\n"7 "POT-Creation-Date: 2026-02-23T00:00:00+00:00\n" 8 8 "MIME-Version: 1.0\n" 9 9 "Content-Type: text/plain; charset=UTF-8\n" … … 536 536 msgstr "" 537 537 538 #. translators: %s: preset name 539 #: includes/class-papafpro-template-builderphp:469 540 msgid "Preset \"%s\" updated successfully." 541 msgstr "" 542 543 #: includes/class-papafpro-template-builderphp:460 544 msgid "Failed to update preset." 545 msgstr "" 546 547 #: includes/class-papafpro-template-builderphp:416 548 msgid "Global settings loaded." 549 msgstr "" 550 551 #. translators: %s: preset name 552 #: includes/class-papafpro-template-builderphp:964 553 msgid "Save Preset: %s" 554 msgstr "" 555 556 #: includes/class-papafpro-template-builderphp:966 557 msgid "Global settings restored." 558 msgstr "" 559 560 #. translators: %s: preset name placeholder, replaced dynamically by JavaScript 561 #: views/papafpro-template-builderphp:389 562 msgid "Editing preset: %s" 563 msgstr "" 564 565 #: views/papafpro-template-builderphp:395 566 msgid "Back to Global Settings" 567 msgstr "" 568 538 569 #: includes/class-papafpro-template-builderphp:871 539 570 #: views/papafpro-help-pagephp:57 -
pap-afiliados-pro/trunk/pap-afiliados-pro.php
r3466192 r3468187 4 4 * Plugin URI: https://pap-afiliados-pro.com.br 5 5 * Description: Professional affiliate link management for Brazilian marketplaces (Amazon, Mercado Livre, Shopee, AliExpress, and others). 6 * Version: 2.0. 06 * Version: 2.0.1 7 7 * Requires at least: 6.2 8 8 * Requires PHP: 7.4 … … 24 24 * Constantes do plugin. 25 25 */ 26 define( 'PAPAFPRO_VERSION', '2.0. 0' );26 define( 'PAPAFPRO_VERSION', '2.0.1' ); 27 27 define( 'PAPAFPRO_PLUGIN_DIR', plugin_dir_path( __FILE__ ) ); 28 28 define( 'PAPAFPRO_PLUGIN_URL', plugin_dir_url( __FILE__ ) ); … … 108 108 $loader->add_action( 'wp_ajax_papafpro_save_preset', $template_builder, 'ajax_save_preset' ); 109 109 $loader->add_action( 'wp_ajax_papafpro_load_preset', $template_builder, 'ajax_load_preset' ); 110 $loader->add_action( 'wp_ajax_papafpro_update_preset', $template_builder, 'ajax_update_preset' ); 110 111 $loader->add_action( 'wp_ajax_papafpro_delete_preset', $template_builder, 'ajax_delete_preset' ); 111 112 $loader->add_action( 'wp_ajax_papafpro_update_preview', $template_builder, 'ajax_update_preview' ); -
pap-afiliados-pro/trunk/readme.txt
r3466192 r3468187 5 5 Tested up to: 6.9 6 6 Requires PHP: 7.4 7 Stable tag: 2.0. 07 Stable tag: 2.0.1 8 8 License: GPLv2 or later 9 9 License URI: https://www.gnu.org/licenses/gpl-2.0.html … … 48 48 * Custom badges per marketplace 49 49 * Adjustable colors, borders and spacing 50 * Custom CSS with sanitization51 50 * Saved presets for reuse 52 51 … … 113 112 = Can I customize the card appearance? = 114 113 115 Absolutely! The Template Builder lets you adjust 19 CSS variables, colors, borders, spacing , button styles and custom CSS. You can save presets to reuse across different pages.114 Absolutely! The Template Builder lets you adjust 19 CSS variables, colors, borders, spacing and button styles. You can save presets to reuse across different pages. 116 115 117 116 = What happens when I uninstall the plugin? = … … 131 130 132 131 == Changelog == 132 133 = 2.0.1 = 134 * Fixed: Preset system now correctly applies individual visual configurations per shortcode 135 * Fixed: Shortcode generator produces correct preset_id parameter 136 * Fixed: Click tracking for PAP Link and PAP Linker block works reliably for all users including logged-in administrators 137 * Improved: Block API updated to version 3 for WordPress 7.0 compatibility 138 * Improved: Template Builder displays visual indicator when editing a preset 133 139 134 140 = 2.0.0 = … … 158 164 == Upgrade Notice == 159 165 166 = 2.0.1 = 167 Bug fixes for preset system, shortcode generator, and click tracking. Block API updated for WordPress 7.0 compatibility. 168 160 169 = 2.0.0 = 161 170 Complete rewrite! Version 2.0 is not compatible with 1.0.2. Please back up before upgrading. -
pap-afiliados-pro/trunk/views/papafpro-template-builder.php
r3466192 r3468187 381 381 </div> 382 382 383 <!-- Preset Editing Context Bar --> 384 <div id="papafpro-preset-editing-bar" class="papafpro-preset-editing-bar" style="display:none;"> 385 <span class="papafpro-preset-editing-label"> 386 <?php 387 printf( 388 /* translators: %s: preset name placeholder, replaced dynamically by JavaScript */ 389 esc_html__( 'Editing preset: %s', 'pap-afiliados-pro' ), 390 '<strong id="papafpro-preset-editing-name"></strong>' 391 ); 392 ?> 393 </span> 394 <button type="button" id="papafpro-back-to-global" class="button papafpro-back-to-global"> 395 <?php esc_html_e( 'Back to Global Settings', 'pap-afiliados-pro' ); ?> 396 </button> 397 </div> 398 383 399 <!-- Botões de Ação --> 384 400 <div class="papafpro-builder-actions">
Note: See TracChangeset
for help on using the changeset viewer.