Changeset 3447574
- Timestamp:
- 01/27/2026 07:49:19 AM (2 months ago)
- Location:
- readmo-ai
- Files:
-
- 20 edited
- 10 copied
-
tags/1.2.2 (copied) (copied from readmo-ai/trunk)
-
tags/1.2.2/Controller/admin/class-readmo-ai-admin-settings.php (modified) (11 diffs)
-
tags/1.2.2/Controller/frontend/class-readmo-ai-ajax-handler.php (modified) (28 diffs)
-
tags/1.2.2/Controller/frontend/class-readmo-ai-tracking-handler.php (modified) (4 diffs)
-
tags/1.2.2/Infrastructure/dao/class-readmo-ai-settings-dao.php (modified) (4 diffs)
-
tags/1.2.2/View/admin/class-readmo-ai-admin-settings-view.php (modified) (3 diffs)
-
tags/1.2.2/assets/css/admin.css (modified) (8 diffs)
-
tags/1.2.2/assets/css/frontend.css (modified) (6 diffs)
-
tags/1.2.2/assets/js/admin.js (modified) (6 diffs)
-
tags/1.2.2/assets/svg/question.svg (modified) (1 diff)
-
tags/1.2.2/languages/readmo-ai-zh_CN.mo (copied) (copied from readmo-ai/trunk/languages/readmo-ai-zh_CN.mo)
-
tags/1.2.2/languages/readmo-ai-zh_CN.po (copied) (copied from readmo-ai/trunk/languages/readmo-ai-zh_CN.po)
-
tags/1.2.2/languages/readmo-ai-zh_HK.mo (copied) (copied from readmo-ai/trunk/languages/readmo-ai-zh_HK.mo)
-
tags/1.2.2/languages/readmo-ai-zh_HK.po (copied) (copied from readmo-ai/trunk/languages/readmo-ai-zh_HK.po)
-
tags/1.2.2/languages/readmo-ai-zh_TW.mo (copied) (copied from readmo-ai/trunk/languages/readmo-ai-zh_TW.mo)
-
tags/1.2.2/languages/readmo-ai-zh_TW.po (copied) (copied from readmo-ai/trunk/languages/readmo-ai-zh_TW.po)
-
tags/1.2.2/languages/readmo-ai.pot (copied) (copied from readmo-ai/trunk/languages/readmo-ai.pot)
-
tags/1.2.2/readme.txt (copied) (copied from readmo-ai/trunk/readme.txt) (1 diff)
-
tags/1.2.2/readmo-ai.php (copied) (copied from readmo-ai/trunk/readmo-ai.php) (2 diffs)
-
trunk/Controller/admin/class-readmo-ai-admin-settings.php (modified) (11 diffs)
-
trunk/Controller/frontend/class-readmo-ai-ajax-handler.php (modified) (28 diffs)
-
trunk/Controller/frontend/class-readmo-ai-tracking-handler.php (modified) (4 diffs)
-
trunk/Infrastructure/dao/class-readmo-ai-settings-dao.php (modified) (4 diffs)
-
trunk/View/admin/class-readmo-ai-admin-settings-view.php (modified) (3 diffs)
-
trunk/assets/css/admin.css (modified) (8 diffs)
-
trunk/assets/css/frontend.css (modified) (6 diffs)
-
trunk/assets/js/admin.js (modified) (6 diffs)
-
trunk/assets/svg/question.svg (modified) (1 diff)
-
trunk/readme.txt (modified) (1 diff)
-
trunk/readmo-ai.php (modified) (2 diffs)
Legend:
- Unmodified
- Added
- Removed
-
readmo-ai/tags/1.2.2/Controller/admin/class-readmo-ai-admin-settings.php
r3447021 r3447574 112 112 add_action( 'wp_ajax_readmo_ai_save_settings', array( $this, 'ajax_save_settings' ) ); 113 113 add_action( 'wp_ajax_readmo_ai_save_auto_insert_settings', array( $this, 'ajax_save_auto_insert_settings' ) ); 114 add_action( 'wp_ajax_readmo_ai_delete_auto_insert_settings', array( $this, 'ajax_delete_auto_insert_settings' ) );115 114 } 116 115 … … 343 342 array( 344 343 'message' => __( 'Security check failed', 'readmo-ai' ), 345 ) 344 ), 345 null, 346 JSON_UNESCAPED_UNICODE 346 347 ); 347 348 } … … 352 353 array( 353 354 'message' => __( 'Insufficient permissions', 'readmo-ai' ), 354 ) 355 ), 356 null, 357 JSON_UNESCAPED_UNICODE 355 358 ); 356 359 } … … 361 364 array( 362 365 'message' => __( 'API Key is required', 'readmo-ai' ), 363 ) 366 ), 367 null, 368 JSON_UNESCAPED_UNICODE 364 369 ); 365 370 } … … 372 377 array( 373 378 'message' => __( 'API Key cannot be empty', 'readmo-ai' ), 374 ) 379 ), 380 null, 381 JSON_UNESCAPED_UNICODE 375 382 ); 376 383 } … … 387 394 array( 388 395 'message' => __( 'Settings saved successfully', 'readmo-ai' ), 389 ) 396 ), 397 null, 398 JSON_UNESCAPED_UNICODE 390 399 ); 391 400 } else { … … 393 402 array( 394 403 'message' => __( 'Failed to save settings', 'readmo-ai' ), 395 ) 404 ), 405 null, 406 JSON_UNESCAPED_UNICODE 396 407 ); 397 408 } … … 412 423 array( 413 424 'message' => __( 'Security check failed', 'readmo-ai' ), 414 ) 425 ), 426 null, 427 JSON_UNESCAPED_UNICODE 415 428 ); 416 429 } … … 421 434 array( 422 435 'message' => __( 'Insufficient permissions', 'readmo-ai' ), 423 ) 436 ), 437 null, 438 JSON_UNESCAPED_UNICODE 424 439 ); 425 440 } … … 437 452 wp_send_json_success( 438 453 array( 439 'message' => __( 'Auto-insert settings saved successfully', 'readmo-ai' ),440 'saved_settings' => $auto_insert_settings,454 'message' => __( 'Auto-insert settings saved successfully', 'readmo-ai' ), 455 'saved_settings' => $auto_insert_settings, 441 456 'verified_settings' => $verified_settings, 442 ) 457 ), 458 null, 459 JSON_UNESCAPED_UNICODE 443 460 ); 444 461 } else { 445 462 wp_send_json_error( 446 463 array( 447 'message' => __( 'Failed to save auto-insert settings', 'readmo-ai' ),464 'message' => __( 'Failed to save auto-insert settings', 'readmo-ai' ), 448 465 'attempted_settings' => $auto_insert_settings, 449 ) 450 ); 451 } 452 } 453 454 /** 455 * AJAX handler to delete auto-insert settings 456 * 457 * Handles AJAX requests to remove auto-insert configuration. 458 * 459 * @since 1.2.0 460 * @return void 461 */ 462 public function ajax_delete_auto_insert_settings() { 463 // Verify nonce. 464 if ( ! isset( $_POST['nonce'] ) || ! wp_verify_nonce( sanitize_text_field( wp_unslash( $_POST['nonce'] ) ), 'readmo_ai_admin_nonce' ) ) { 465 wp_send_json_error( 466 array( 467 'message' => __( 'Security check failed', 'readmo-ai' ), 468 ) 469 ); 470 } 471 472 // Check user capabilities. 473 if ( ! current_user_can( 'manage_options' ) ) { 474 wp_send_json_error( 475 array( 476 'message' => __( 'Insufficient permissions', 'readmo-ai' ), 477 ) 478 ); 479 } 480 481 // Delete settings. 482 $deleted = $this->settings_dao->delete_auto_insert_settings(); 483 484 if ( $deleted ) { 485 wp_send_json_success( 486 array( 487 'message' => __( 'Auto-insert settings removed successfully', 'readmo-ai' ), 488 ) 489 ); 490 } else { 491 wp_send_json_error( 492 array( 493 'message' => __( 'Failed to remove auto-insert settings', 'readmo-ai' ), 494 ) 466 ), 467 null, 468 JSON_UNESCAPED_UNICODE 495 469 ); 496 470 } … … 513 487 $settings['enabled'] = ! empty( $post_data['enabled'] ); 514 488 515 // Position. 516 $allowed_positions = array( 'before_content', 'after_content', 'footer' ); 489 // Position is fixed to 'after_content'. 517 490 $settings['position'] = 'after_content'; 518 if ( isset( $post_data['position'] ) && in_array( $post_data['position'], $allowed_positions, true ) ) {519 $settings['position'] = sanitize_text_field( $post_data['position'] );520 }521 491 522 492 // Excluded post types (array of post type names). -
readmo-ai/tags/1.2.2/Controller/frontend/class-readmo-ai-ajax-handler.php
r3445269 r3447574 108 108 'error_code' => 'nonce_expired', 109 109 ), 110 400 110 400, 111 JSON_UNESCAPED_UNICODE 111 112 ); 112 113 } … … 118 119 'message' => esc_html__( 'API is not properly configured.', 'readmo-ai' ), 119 120 ), 120 400 121 400, 122 JSON_UNESCAPED_UNICODE 121 123 ); 122 124 } … … 128 130 'message' => esc_html__( 'Source URL is missing.', 'readmo-ai' ), 129 131 ), 130 400 132 400, 133 JSON_UNESCAPED_UNICODE 131 134 ); 132 135 } … … 143 146 'message' => esc_html__( 'Invalid source URL provided.', 'readmo-ai' ), 144 147 ), 145 400 148 400, 149 JSON_UNESCAPED_UNICODE 146 150 ); 147 151 } … … 161 165 ), 162 166 ), 163 $response['status_code'] 167 $response['status_code'], 168 JSON_UNESCAPED_UNICODE 164 169 ); 165 170 } … … 171 176 'message' => esc_html__( 'Invalid API response format.', 'readmo-ai' ), 172 177 ), 173 500 178 500, 179 JSON_UNESCAPED_UNICODE 174 180 ); 175 181 } … … 183 189 'message' => esc_html__( 'Job ID not returned from API.', 'readmo-ai' ), 184 190 ), 185 500 191 500, 192 JSON_UNESCAPED_UNICODE 186 193 ); 187 194 } … … 192 199 array( 193 200 'jobId' => $job_id, 194 ) 201 ), 202 null, 203 JSON_UNESCAPED_UNICODE 195 204 ); 196 205 } … … 212 221 'error_code' => 'nonce_expired', 213 222 ), 214 400 223 400, 224 JSON_UNESCAPED_UNICODE 215 225 ); 216 226 } … … 222 232 'message' => esc_html__( 'API is not properly configured.', 'readmo-ai' ), 223 233 ), 224 400 234 400, 235 JSON_UNESCAPED_UNICODE 225 236 ); 226 237 } … … 232 243 'message' => esc_html__( 'Job ID is missing.', 'readmo-ai' ), 233 244 ), 234 400 245 400, 246 JSON_UNESCAPED_UNICODE 235 247 ); 236 248 } … … 242 254 'message' => esc_html__( 'Job ID is invalid.', 'readmo-ai' ), 243 255 ), 244 400 256 400, 257 JSON_UNESCAPED_UNICODE 245 258 ); 246 259 } … … 255 268 'message' => $response['error_message'], 256 269 ), 257 $response['status_code'] 270 $response['status_code'], 271 JSON_UNESCAPED_UNICODE 258 272 ); 259 273 } … … 265 279 'message' => esc_html__( 'Invalid API response format.', 'readmo-ai' ), 266 280 ), 267 500 281 500, 282 JSON_UNESCAPED_UNICODE 268 283 ); 269 284 } … … 281 296 'status' => $status, 282 297 'articleId' => $article_id, 283 ) 298 ), 299 null, 300 JSON_UNESCAPED_UNICODE 284 301 ); 285 302 } … … 301 318 'error_code' => 'nonce_expired', 302 319 ), 303 400 320 400, 321 JSON_UNESCAPED_UNICODE 304 322 ); 305 323 } … … 311 329 'message' => esc_html__( 'API is not properly configured.', 'readmo-ai' ), 312 330 ), 313 400 331 400, 332 JSON_UNESCAPED_UNICODE 314 333 ); 315 334 } … … 321 340 'message' => esc_html__( 'Article ID is missing.', 'readmo-ai' ), 322 341 ), 323 400 342 400, 343 JSON_UNESCAPED_UNICODE 324 344 ); 325 345 } … … 331 351 'message' => esc_html__( 'Article ID is invalid.', 'readmo-ai' ), 332 352 ), 333 400 353 400, 354 JSON_UNESCAPED_UNICODE 334 355 ); 335 356 } … … 344 365 'message' => $response['error_message'], 345 366 ), 346 $response['status_code'] 367 $response['status_code'], 368 JSON_UNESCAPED_UNICODE 347 369 ); 348 370 } … … 354 376 'message' => esc_html__( 'Invalid API response format.', 'readmo-ai' ), 355 377 ), 356 500 378 500, 379 JSON_UNESCAPED_UNICODE 357 380 ); 358 381 } … … 361 384 $article_data = $response['body']; 362 385 363 wp_send_json_success( $article_data );386 wp_send_json_success( $article_data, null, JSON_UNESCAPED_UNICODE ); 364 387 } 365 388 … … 381 404 'error_code' => 'nonce_expired', 382 405 ), 383 400 406 400, 407 JSON_UNESCAPED_UNICODE 384 408 ); 385 409 } … … 390 414 array( 391 415 'message' => esc_html__( 'API key is not configured. Please configure it in Settings > Readmo AI.', 'readmo-ai' ), 392 ) 416 ), 417 null, 418 JSON_UNESCAPED_UNICODE 393 419 ); 394 420 } … … 399 425 if ( false !== $cached_path_info ) { 400 426 // Return cached data. 401 wp_send_json_success( $cached_path_info );427 wp_send_json_success( $cached_path_info, null, JSON_UNESCAPED_UNICODE ); 402 428 } 403 429 … … 410 436 array( 411 437 'message' => $result['error_message'], 412 ) 438 ), 439 null, 440 JSON_UNESCAPED_UNICODE 413 441 ); 414 442 } … … 443 471 444 472 // Return success with path info. 445 wp_send_json_success( $path_info );473 wp_send_json_success( $path_info, null, JSON_UNESCAPED_UNICODE ); 446 474 } 447 475 … … 473 501 array( 474 502 'nonce' => $new_nonce, 475 ) 503 ), 504 null, 505 JSON_UNESCAPED_UNICODE 476 506 ); 477 507 } -
readmo-ai/tags/1.2.2/Controller/frontend/class-readmo-ai-tracking-handler.php
r3445269 r3447574 86 86 'error_code' => 'nonce_expired', 87 87 ), 88 400 88 400, 89 JSON_UNESCAPED_UNICODE 89 90 ); 90 91 return; … … 98 99 array( 99 100 'message' => __( 'API Key not configured.', 'readmo-ai' ), 100 ) 101 ), 102 null, 103 JSON_UNESCAPED_UNICODE 101 104 ); 102 105 return; … … 114 117 array( 115 118 'message' => __( 'Failed to retrieve client ID.', 'readmo-ai' ), 116 ) 119 ), 120 null, 121 JSON_UNESCAPED_UNICODE 117 122 ); 118 123 return; … … 148 153 array( 149 154 'message' => __( 'Event tracked successfully.', 'readmo-ai' ), 150 ) 155 ), 156 null, 157 JSON_UNESCAPED_UNICODE 151 158 ); 152 159 } -
readmo-ai/tags/1.2.2/Infrastructure/dao/class-readmo-ai-settings-dao.php
r3447021 r3447574 69 69 $settings = get_option( self::OPTION_NAME, array() ); 70 70 71 // If no settings exist, initialize with empty defaults in database. 72 if ( empty( $settings ) ) { 73 $defaults = array( 'api_key' => '' ); 74 update_option( self::OPTION_NAME, $defaults, true ); 75 return ''; 76 } 77 71 78 if ( empty( $settings['api_key'] ) ) { 72 79 return ''; … … 118 125 */ 119 126 public function get_auto_insert_settings() { 120 // Default: all empty arrays = all checked = apply to all content.127 // Default: enabled = true, only 'post' type is checked (page is excluded). 121 128 $defaults = array( 122 'enabled' => false,129 'enabled' => true, 123 130 'position' => 'after_content', 124 'excluded_post_types' => array( ),131 'excluded_post_types' => array( 'page' ), 125 132 'excluded_categories' => array(), 126 133 'excluded_posts' => array(), … … 129 136 $settings = get_option( self::AUTO_INSERT_OPTION_NAME, array() ); 130 137 138 // If no settings exist, initialize with defaults in database. 131 139 if ( empty( $settings ) ) { 140 update_option( self::AUTO_INSERT_OPTION_NAME, $defaults, true ); 132 141 return $defaults; 133 142 } … … 161 170 } 162 171 163 /**164 * Delete auto-insert settings165 *166 * Removes the auto-insert settings option entirely.167 *168 * @since 1.2.0169 * @return bool True on success, false on failure.170 */171 public function delete_auto_insert_settings() {172 return delete_option( self::AUTO_INSERT_OPTION_NAME );173 }174 172 } -
readmo-ai/tags/1.2.2/View/admin/class-readmo-ai-admin-settings-view.php
r3447021 r3447574 41 41 // Using "excluded" storage: empty arrays = all checked (apply to all). 42 42 $ai_enabled = ! empty( $auto_insert_settings['enabled'] ); 43 $ai_position = isset( $auto_insert_settings['position'] ) ? $auto_insert_settings['position'] : 'after_content';44 43 $ai_excluded_post_types = isset( $auto_insert_settings['excluded_post_types'] ) ? $auto_insert_settings['excluded_post_types'] : array(); 45 44 $ai_excluded_categories = isset( $auto_insert_settings['excluded_categories'] ) ? $auto_insert_settings['excluded_categories'] : array(); … … 110 109 /> 111 110 <label for="readmo-ai-auto-insert-enabled" class="readmo-ai-toggle-slider"></label> 112 </div>113 </div>114 115 <!-- Insert Position -->116 <div class="readmo-ai-setting-column">117 <label class="readmo-ai-label">118 <span class="readmo-ai-text"><?php echo esc_html( __( 'Insert Position', 'readmo-ai' ) ); ?></span>119 </label>120 <div class="readmo-ai-radio-group">121 <label class="readmo-ai-radio-label">122 <input123 type="radio"124 name="auto_insert_position"125 value="after_content"126 <?php checked( $ai_position, 'after_content' ); ?>127 />128 <span class="readmo-ai-radio-dot"></span>129 <span class="readmo-ai-radio-text"><?php echo esc_html( __( 'After content', 'readmo-ai' ) ); ?></span>130 </label>131 <label class="readmo-ai-radio-label">132 <input133 type="radio"134 name="auto_insert_position"135 value="footer"136 <?php checked( $ai_position, 'footer' ); ?>137 />138 <span class="readmo-ai-radio-dot"></span>139 <span class="readmo-ai-radio-text"><?php echo esc_html( __( 'Page footer', 'readmo-ai' ) ); ?></span>140 </label>141 111 </div> 142 112 </div> … … 255 225 </div> 256 226 257 <div class="readmo-ai-action readmo-ai-auto-insert-actions">227 <div class="readmo-ai-action"> 258 228 <button type="button" id="readmo-ai-save-auto-insert" class="btn btn-ban"> 259 229 <?php echo esc_html( __( 'Save Settings', 'readmo-ai' ) ); ?> 260 230 </button> 261 <button type="button" id="readmo-ai-remove-auto-insert" class="btn btn-danger">262 <?php echo esc_html( __( 'Disable and Remove', 'readmo-ai' ) ); ?>263 </button>264 </div>265 </div>266 267 <!-- Remove Confirmation Modal -->268 <div id="readmo-ai-confirm-modal" class="readmo-ai-modal" style="display: none;">269 <div class="readmo-ai-modal-overlay"></div>270 <div class="readmo-ai-modal-content">271 <span class="readmo-ai-title"><?php echo esc_html( __( 'Confirm Remove', 'readmo-ai' ) ); ?></span>272 <span class="readmo-ai-text"><?php echo esc_html( __( 'Are you sure you want to disable and remove all auto-insert settings? This action cannot be undone.', 'readmo-ai' ) ); ?></span>273 <div class="readmo-ai-modal-actions">274 <button type="button" id="readmo-ai-modal-cancel" class="btn btn-tertiary">275 <?php echo esc_html( __( 'Cancel', 'readmo-ai' ) ); ?>276 </button>277 <button type="button" id="readmo-ai-modal-confirm" class="btn btn-danger">278 <?php echo esc_html( __( 'Confirm Remove', 'readmo-ai' ) ); ?>279 </button>280 </div>281 231 </div> 282 232 </div> -
readmo-ai/tags/1.2.2/assets/css/admin.css
r3447021 r3447574 15 15 align-items: center; 16 16 flex-direction: column; 17 gap: 12px; 17 gap: 24px; 18 padding: 32px 0; 18 19 font-family: Noto Sans TC, PingFang TC, Arial, Helvetica, LiHei Pro, Microsoft JhengHei, MingLiU, sans-serif; 19 20 } … … 53 54 display: flex; 54 55 flex-direction: column; 55 min-width: 720px; 56 width: 50vw; 57 max-width: 100%; 56 58 background-color: #ffffff; 57 59 border-radius: 12px; … … 186 188 } 187 189 188 /* Danger Button */189 .btn-danger {190 background: rgb(235, 67, 67);191 color: #ffffff;192 border: none;193 padding: 16px 20px;194 }195 196 .btn-danger:hover {197 background: rgb(220, 38, 38);198 }199 200 190 /* Ban Button (Disabled State) */ 201 191 .btn-ban, … … 434 424 .readmo-ai-content-tree { 435 425 max-height: 400px; 426 max-width: 100%; 436 427 overflow: auto; 437 428 padding: 12px; … … 491 482 cursor: pointer; 492 483 flex: 1; 484 min-width: 0; 485 overflow: hidden; 493 486 } 494 487 … … 555 548 color: var(--Text-Neutral-400, rgba(74, 75, 88, 1)); 556 549 transition: color 0.15s ease; 550 overflow: hidden; 551 text-overflow: ellipsis; 552 white-space: nowrap; 557 553 } 558 554 … … 588 584 } 589 585 590 /* Auto-Insert Actions */591 .readmo-ai-auto-insert-actions {592 justify-content: space-between;593 }594 595 /* Modal Styles */596 .readmo-ai-modal {597 position: fixed;598 top: 0;599 left: 0;600 width: 100%;601 height: 100%;602 z-index: 100000;603 display: flex;604 align-items: center;605 justify-content: center;606 }607 608 .readmo-ai-modal-overlay {609 position: absolute;610 top: 0;611 left: 0;612 width: 100%;613 height: 100%;614 background: rgba(0, 0, 0, 0.5);615 }616 617 .readmo-ai-modal-content {618 display: flex;619 flex-direction: column;620 gap: 24px;621 position: relative;622 background: #ffffff;623 padding: 24px;624 border-radius: 12px;625 max-width: 400px;626 width: 90%;627 box-shadow: 0 4px 20px rgba(0, 0, 0, 0.15);628 }629 630 .readmo-ai-modal-content h3 {631 font-size: 18px;632 font-weight: 600;633 color: var(--Text-Neutral-400, rgba(74, 75, 88, 1));634 }635 636 .readmo-ai-modal-content p {637 font-size: 14px;638 color: var(--Text-Neutral-300, rgba(113, 113, 113, 1));639 line-height: 1.5;640 }641 642 .readmo-ai-modal-actions {643 display: flex;644 justify-content: flex-end;645 gap: 12px;646 }647 648 .readmo-ai-modal-actions .btn {649 width: auto;650 padding: 10px 20px;651 }652 653 586 @media screen and (max-width: 767px) { 654 587 .readmo-ai-settings-container { … … 679 612 } 680 613 681 .btn-danger {682 width: auto;683 padding: 12px 16px;684 }685 686 .readmo-ai-auto-insert-actions {687 flex-direction: column;688 gap: 12px;689 }690 691 .readmo-ai-auto-insert-actions .btn {692 width: 100%;693 }694 695 614 .readmo-ai-checkbox-group, 696 615 .readmo-ai-radio-group { -
readmo-ai/tags/1.2.2/assets/css/frontend.css
r3411004 r3447574 35 35 36 36 .readmo-ai-question-icon { 37 width: 2 8px;38 height: 2 8px;37 width: 24px; 38 height: 24px; 39 39 flex-shrink: 0; 40 40 } … … 42 42 .readmo-ai-question-title { 43 43 font-weight: 600; 44 font-size: 2 4px;44 font-size: 20px; 45 45 color: #215376; 46 line-height: 2 8px;46 line-height: 24px; 47 47 } 48 48 … … 50 50 .readmo-ai-questions { 51 51 display: flex; 52 flex-wrap: wrap; 52 flex-direction: column; 53 align-items: flex-start; 53 54 gap: 12px; 54 55 margin: 0; … … 59 60 /* Question Button Styles */ 60 61 .readmo-ai-question-btn { 62 display: inline-flex; 61 63 position: relative; 62 64 border-radius: 12px; 63 padding: 1 rem;65 padding: 12px 16px; 64 66 background-color: #ffffff; 65 67 box-shadow: 0px 2px 4px 0px rgba(62, 179, 218, 0.2); … … 107 109 font-weight: 400; 108 110 color: #26272C; 109 font-size: 20px;110 line-height: 2 4px;111 font-size: 16px; 112 line-height: 20px; 111 113 transition: 0.3s ease-out; 114 text-decoration: none; 112 115 } 113 116 … … 123 126 @media screen and (max-width: 767px) { 124 127 .readmo-ai-question-title { 125 font-size: 20px;126 line-height: 2 4px;128 font-size: 16px; 129 line-height: 20px; 127 130 } 128 131 129 132 .readmo-ai-question-btn { 130 font-size: 1 6px;131 line-height: 20px;132 padding: 10px 14px;133 font-size: 12px; 134 line-height: 16px; 135 padding: 8px 12px; 133 136 } 134 137 } -
readmo-ai/tags/1.2.2/assets/js/admin.js
r3447021 r3447574 256 256 cacheElements: function () { 257 257 this.$enabledCheckbox = $( '#readmo-ai-auto-insert-enabled' ); 258 this.$positionRadios = $( 'input[name="auto_insert_position"]' );259 258 this.$fromUrlInput = $( '#readmo-ai-from-url' ); 260 259 this.$contentTree = $( '#readmo-ai-content-tree' ); 261 260 this.$saveButton = $( '#readmo-ai-save-auto-insert' ); 262 this.$removeButton = $( '#readmo-ai-remove-auto-insert' );263 this.$modal = $( '#readmo-ai-confirm-modal' );264 this.$modalCancel = $( '#readmo-ai-modal-cancel' );265 this.$modalConfirm = $( '#readmo-ai-modal-confirm' );266 this.$modalOverlay = $( '.readmo-ai-modal-overlay' );267 261 }, 268 262 … … 321 315 return { 322 316 enabled: this.$enabledCheckbox.is( ':checked' ), 323 position: $( 'input[name="auto_insert_position"]:checked' ).val(),324 317 fromUrl: this.$fromUrlInput.val(), 325 318 excludedPostTypes: excludedPostTypes, … … 367 360 }); 368 361 369 this.$positionRadios.on( 'change', function () {370 self.updateSaveButtonState();371 });372 373 362 this.$fromUrlInput.on( 'input', function () { 374 363 self.updateSaveButtonState(); … … 402 391 e.preventDefault(); 403 392 self.saveSettings(); 404 });405 406 // Remove button click.407 this.$removeButton.on( 'click', function ( e ) {408 e.preventDefault();409 self.showModal();410 });411 412 // Modal cancel.413 this.$modalCancel.on( 'click', function ( e ) {414 e.preventDefault();415 self.hideModal();416 });417 418 // Modal overlay click.419 this.$modalOverlay.on( 'click', function () {420 self.hideModal();421 });422 423 // Modal confirm.424 this.$modalConfirm.on( 'click', function ( e ) {425 e.preventDefault();426 self.removeSettings();427 });428 429 // ESC key to close modal.430 $( document ).on( 'keydown', function ( e ) {431 if ( e.key === 'Escape' && self.$modal.is( ':visible' ) ) {432 self.hideModal();433 }434 393 }); 435 394 }, … … 582 541 nonce: readmoAiAdminData.nonce, 583 542 enabled: values.enabled ? 1 : 0, 584 position: values.position,585 543 from_url: values.fromUrl, 586 544 excluded_post_types: values.excludedPostTypes, … … 624 582 } 625 583 }); 626 },627 628 /**629 * Show confirmation modal.630 */631 showModal: function () {632 this.$modal.fadeIn( 200 );633 },634 635 /**636 * Hide confirmation modal.637 */638 hideModal: function () {639 this.$modal.fadeOut( 200 );640 },641 642 /**643 * Remove auto-insert settings via AJAX.644 */645 removeSettings: function () {646 var self = this;647 648 // Disable confirm button.649 this.$modalConfirm.prop( 'disabled', true ).text( readmoAiAdminData.i18n.saving );650 651 $.ajax({652 type: 'POST',653 url: readmoAiAdminData.ajaxUrl,654 data: {655 action: 'readmo_ai_delete_auto_insert_settings',656 nonce: readmoAiAdminData.nonce657 },658 success: function ( response ) {659 if ( response.success ) {660 // Reset form to defaults (all checked).661 self.$enabledCheckbox.prop( 'checked', false );662 $( 'input[name="auto_insert_position"][value="after_content"]' ).prop( 'checked', true );663 self.$fromUrlInput.val( '' );664 665 // Check all tree items.666 self.$contentTree.find( '.readmo-ai-tree-input' ).prop( 'checked', true ).prop( 'indeterminate', false );667 self.$contentTree.find( '.readmo-ai-tree-node' ).removeClass( 'unchecked' );668 669 // Update original values.670 self.originalValues = self.getFormValues();671 672 // Reset button state.673 self.$saveButton674 .removeClass( 'btn-secondary' )675 .addClass( 'btn-ban' )676 .prop( 'disabled', true );677 678 // Hide modal.679 self.hideModal();680 681 // Re-enable confirm button.682 self.$modalConfirm.prop( 'disabled', false ).text( readmoAiAdminData.i18n.confirmRemove || 'Confirm Remove' );683 684 // Show success message.685 ReadmoAiAdmin.showNotice( 'success', response.data.message );686 } else {687 // Show error message.688 ReadmoAiAdmin.showNotice( 'error', response.data.message );689 690 // Hide modal.691 self.hideModal();692 693 // Re-enable confirm button.694 self.$modalConfirm.prop( 'disabled', false ).text( readmoAiAdminData.i18n.confirmRemove || 'Confirm Remove' );695 }696 },697 error: function () {698 // Show error message.699 ReadmoAiAdmin.showNotice( 'error', 'An error occurred while removing settings.' );700 701 // Hide modal.702 self.hideModal();703 704 // Re-enable confirm button.705 self.$modalConfirm.prop( 'disabled', false ).text( readmoAiAdminData.i18n.confirmRemove || 'Confirm Remove' );706 }707 });708 584 } 709 585 }; -
readmo-ai/tags/1.2.2/assets/svg/question.svg
r3411004 r3447574 1 <svg width="2 8" height="28" viewBox="0 0 64 64" fill="none" xmlns="http://www.w3.org/2000/svg">1 <svg width="24" height="24" viewBox="0 0 64 64" fill="none" xmlns="http://www.w3.org/2000/svg"> 2 2 <g clip-path="url(#clip0_2581_15276)"> 3 3 <path d="M43.4926 22.9982C43.5179 19.3262 42.5599 15.7144 40.7181 12.5377C38.8762 9.36101 36.2177 6.73523 33.0184 4.93294C29.819 3.13066 26.1956 2.21758 22.5243 2.28851C18.853 2.35943 15.2675 3.41177 12.1402 5.33628C9.01287 7.26076 6.45767 9.9873 4.73991 13.2327C3.02212 16.4782 2.20436 20.1243 2.37152 23.7926C2.53867 27.4608 3.68466 31.0174 5.69049 34.0932C7.6963 37.169 10.4889 39.6518 13.7783 41.2839V48.1411C13.7783 48.7473 14.0191 49.3288 14.4478 49.7571C14.8764 50.1859 15.4578 50.4268 16.064 50.4268H29.7783C30.3845 50.4268 30.9659 50.1859 31.3945 49.7571C31.8232 49.3288 32.064 48.7473 32.064 48.1411V41.2839C35.481 39.6003 38.3608 36.9972 40.3797 33.767C42.3987 30.5367 43.4766 26.8074 43.4926 22.9982Z" fill="url(#paint0_radial_2581_15276)"/> -
readmo-ai/tags/1.2.2/readme.txt
r3447213 r3447574 5 5 Tested up to: 6.9 6 6 Requires PHP: 7.4 7 Stable tag: 1.2. 17 Stable tag: 1.2.2 8 8 License: GPLv2 or later 9 9 License URI: https://www.gnu.org/licenses/gpl-2.0.html -
readmo-ai/tags/1.2.2/readmo-ai.php
r3447213 r3447574 13 13 * Plugin Name: Readmo AI 14 14 * Description: AI-powered content analysis and optimization for WordPress with analytics tracking 15 * Version: 1.2. 115 * Version: 1.2.2 16 16 * Requires at least: 5.9 17 17 * Requires PHP: 7.4 … … 33 33 */ 34 34 if ( ! defined( 'READMO_AI_VERSION' ) ) { 35 define( 'READMO_AI_VERSION', '1.2. 1' );35 define( 'READMO_AI_VERSION', '1.2.2' ); 36 36 } 37 37 -
readmo-ai/trunk/Controller/admin/class-readmo-ai-admin-settings.php
r3447021 r3447574 112 112 add_action( 'wp_ajax_readmo_ai_save_settings', array( $this, 'ajax_save_settings' ) ); 113 113 add_action( 'wp_ajax_readmo_ai_save_auto_insert_settings', array( $this, 'ajax_save_auto_insert_settings' ) ); 114 add_action( 'wp_ajax_readmo_ai_delete_auto_insert_settings', array( $this, 'ajax_delete_auto_insert_settings' ) );115 114 } 116 115 … … 343 342 array( 344 343 'message' => __( 'Security check failed', 'readmo-ai' ), 345 ) 344 ), 345 null, 346 JSON_UNESCAPED_UNICODE 346 347 ); 347 348 } … … 352 353 array( 353 354 'message' => __( 'Insufficient permissions', 'readmo-ai' ), 354 ) 355 ), 356 null, 357 JSON_UNESCAPED_UNICODE 355 358 ); 356 359 } … … 361 364 array( 362 365 'message' => __( 'API Key is required', 'readmo-ai' ), 363 ) 366 ), 367 null, 368 JSON_UNESCAPED_UNICODE 364 369 ); 365 370 } … … 372 377 array( 373 378 'message' => __( 'API Key cannot be empty', 'readmo-ai' ), 374 ) 379 ), 380 null, 381 JSON_UNESCAPED_UNICODE 375 382 ); 376 383 } … … 387 394 array( 388 395 'message' => __( 'Settings saved successfully', 'readmo-ai' ), 389 ) 396 ), 397 null, 398 JSON_UNESCAPED_UNICODE 390 399 ); 391 400 } else { … … 393 402 array( 394 403 'message' => __( 'Failed to save settings', 'readmo-ai' ), 395 ) 404 ), 405 null, 406 JSON_UNESCAPED_UNICODE 396 407 ); 397 408 } … … 412 423 array( 413 424 'message' => __( 'Security check failed', 'readmo-ai' ), 414 ) 425 ), 426 null, 427 JSON_UNESCAPED_UNICODE 415 428 ); 416 429 } … … 421 434 array( 422 435 'message' => __( 'Insufficient permissions', 'readmo-ai' ), 423 ) 436 ), 437 null, 438 JSON_UNESCAPED_UNICODE 424 439 ); 425 440 } … … 437 452 wp_send_json_success( 438 453 array( 439 'message' => __( 'Auto-insert settings saved successfully', 'readmo-ai' ),440 'saved_settings' => $auto_insert_settings,454 'message' => __( 'Auto-insert settings saved successfully', 'readmo-ai' ), 455 'saved_settings' => $auto_insert_settings, 441 456 'verified_settings' => $verified_settings, 442 ) 457 ), 458 null, 459 JSON_UNESCAPED_UNICODE 443 460 ); 444 461 } else { 445 462 wp_send_json_error( 446 463 array( 447 'message' => __( 'Failed to save auto-insert settings', 'readmo-ai' ),464 'message' => __( 'Failed to save auto-insert settings', 'readmo-ai' ), 448 465 'attempted_settings' => $auto_insert_settings, 449 ) 450 ); 451 } 452 } 453 454 /** 455 * AJAX handler to delete auto-insert settings 456 * 457 * Handles AJAX requests to remove auto-insert configuration. 458 * 459 * @since 1.2.0 460 * @return void 461 */ 462 public function ajax_delete_auto_insert_settings() { 463 // Verify nonce. 464 if ( ! isset( $_POST['nonce'] ) || ! wp_verify_nonce( sanitize_text_field( wp_unslash( $_POST['nonce'] ) ), 'readmo_ai_admin_nonce' ) ) { 465 wp_send_json_error( 466 array( 467 'message' => __( 'Security check failed', 'readmo-ai' ), 468 ) 469 ); 470 } 471 472 // Check user capabilities. 473 if ( ! current_user_can( 'manage_options' ) ) { 474 wp_send_json_error( 475 array( 476 'message' => __( 'Insufficient permissions', 'readmo-ai' ), 477 ) 478 ); 479 } 480 481 // Delete settings. 482 $deleted = $this->settings_dao->delete_auto_insert_settings(); 483 484 if ( $deleted ) { 485 wp_send_json_success( 486 array( 487 'message' => __( 'Auto-insert settings removed successfully', 'readmo-ai' ), 488 ) 489 ); 490 } else { 491 wp_send_json_error( 492 array( 493 'message' => __( 'Failed to remove auto-insert settings', 'readmo-ai' ), 494 ) 466 ), 467 null, 468 JSON_UNESCAPED_UNICODE 495 469 ); 496 470 } … … 513 487 $settings['enabled'] = ! empty( $post_data['enabled'] ); 514 488 515 // Position. 516 $allowed_positions = array( 'before_content', 'after_content', 'footer' ); 489 // Position is fixed to 'after_content'. 517 490 $settings['position'] = 'after_content'; 518 if ( isset( $post_data['position'] ) && in_array( $post_data['position'], $allowed_positions, true ) ) {519 $settings['position'] = sanitize_text_field( $post_data['position'] );520 }521 491 522 492 // Excluded post types (array of post type names). -
readmo-ai/trunk/Controller/frontend/class-readmo-ai-ajax-handler.php
r3445269 r3447574 108 108 'error_code' => 'nonce_expired', 109 109 ), 110 400 110 400, 111 JSON_UNESCAPED_UNICODE 111 112 ); 112 113 } … … 118 119 'message' => esc_html__( 'API is not properly configured.', 'readmo-ai' ), 119 120 ), 120 400 121 400, 122 JSON_UNESCAPED_UNICODE 121 123 ); 122 124 } … … 128 130 'message' => esc_html__( 'Source URL is missing.', 'readmo-ai' ), 129 131 ), 130 400 132 400, 133 JSON_UNESCAPED_UNICODE 131 134 ); 132 135 } … … 143 146 'message' => esc_html__( 'Invalid source URL provided.', 'readmo-ai' ), 144 147 ), 145 400 148 400, 149 JSON_UNESCAPED_UNICODE 146 150 ); 147 151 } … … 161 165 ), 162 166 ), 163 $response['status_code'] 167 $response['status_code'], 168 JSON_UNESCAPED_UNICODE 164 169 ); 165 170 } … … 171 176 'message' => esc_html__( 'Invalid API response format.', 'readmo-ai' ), 172 177 ), 173 500 178 500, 179 JSON_UNESCAPED_UNICODE 174 180 ); 175 181 } … … 183 189 'message' => esc_html__( 'Job ID not returned from API.', 'readmo-ai' ), 184 190 ), 185 500 191 500, 192 JSON_UNESCAPED_UNICODE 186 193 ); 187 194 } … … 192 199 array( 193 200 'jobId' => $job_id, 194 ) 201 ), 202 null, 203 JSON_UNESCAPED_UNICODE 195 204 ); 196 205 } … … 212 221 'error_code' => 'nonce_expired', 213 222 ), 214 400 223 400, 224 JSON_UNESCAPED_UNICODE 215 225 ); 216 226 } … … 222 232 'message' => esc_html__( 'API is not properly configured.', 'readmo-ai' ), 223 233 ), 224 400 234 400, 235 JSON_UNESCAPED_UNICODE 225 236 ); 226 237 } … … 232 243 'message' => esc_html__( 'Job ID is missing.', 'readmo-ai' ), 233 244 ), 234 400 245 400, 246 JSON_UNESCAPED_UNICODE 235 247 ); 236 248 } … … 242 254 'message' => esc_html__( 'Job ID is invalid.', 'readmo-ai' ), 243 255 ), 244 400 256 400, 257 JSON_UNESCAPED_UNICODE 245 258 ); 246 259 } … … 255 268 'message' => $response['error_message'], 256 269 ), 257 $response['status_code'] 270 $response['status_code'], 271 JSON_UNESCAPED_UNICODE 258 272 ); 259 273 } … … 265 279 'message' => esc_html__( 'Invalid API response format.', 'readmo-ai' ), 266 280 ), 267 500 281 500, 282 JSON_UNESCAPED_UNICODE 268 283 ); 269 284 } … … 281 296 'status' => $status, 282 297 'articleId' => $article_id, 283 ) 298 ), 299 null, 300 JSON_UNESCAPED_UNICODE 284 301 ); 285 302 } … … 301 318 'error_code' => 'nonce_expired', 302 319 ), 303 400 320 400, 321 JSON_UNESCAPED_UNICODE 304 322 ); 305 323 } … … 311 329 'message' => esc_html__( 'API is not properly configured.', 'readmo-ai' ), 312 330 ), 313 400 331 400, 332 JSON_UNESCAPED_UNICODE 314 333 ); 315 334 } … … 321 340 'message' => esc_html__( 'Article ID is missing.', 'readmo-ai' ), 322 341 ), 323 400 342 400, 343 JSON_UNESCAPED_UNICODE 324 344 ); 325 345 } … … 331 351 'message' => esc_html__( 'Article ID is invalid.', 'readmo-ai' ), 332 352 ), 333 400 353 400, 354 JSON_UNESCAPED_UNICODE 334 355 ); 335 356 } … … 344 365 'message' => $response['error_message'], 345 366 ), 346 $response['status_code'] 367 $response['status_code'], 368 JSON_UNESCAPED_UNICODE 347 369 ); 348 370 } … … 354 376 'message' => esc_html__( 'Invalid API response format.', 'readmo-ai' ), 355 377 ), 356 500 378 500, 379 JSON_UNESCAPED_UNICODE 357 380 ); 358 381 } … … 361 384 $article_data = $response['body']; 362 385 363 wp_send_json_success( $article_data );386 wp_send_json_success( $article_data, null, JSON_UNESCAPED_UNICODE ); 364 387 } 365 388 … … 381 404 'error_code' => 'nonce_expired', 382 405 ), 383 400 406 400, 407 JSON_UNESCAPED_UNICODE 384 408 ); 385 409 } … … 390 414 array( 391 415 'message' => esc_html__( 'API key is not configured. Please configure it in Settings > Readmo AI.', 'readmo-ai' ), 392 ) 416 ), 417 null, 418 JSON_UNESCAPED_UNICODE 393 419 ); 394 420 } … … 399 425 if ( false !== $cached_path_info ) { 400 426 // Return cached data. 401 wp_send_json_success( $cached_path_info );427 wp_send_json_success( $cached_path_info, null, JSON_UNESCAPED_UNICODE ); 402 428 } 403 429 … … 410 436 array( 411 437 'message' => $result['error_message'], 412 ) 438 ), 439 null, 440 JSON_UNESCAPED_UNICODE 413 441 ); 414 442 } … … 443 471 444 472 // Return success with path info. 445 wp_send_json_success( $path_info );473 wp_send_json_success( $path_info, null, JSON_UNESCAPED_UNICODE ); 446 474 } 447 475 … … 473 501 array( 474 502 'nonce' => $new_nonce, 475 ) 503 ), 504 null, 505 JSON_UNESCAPED_UNICODE 476 506 ); 477 507 } -
readmo-ai/trunk/Controller/frontend/class-readmo-ai-tracking-handler.php
r3445269 r3447574 86 86 'error_code' => 'nonce_expired', 87 87 ), 88 400 88 400, 89 JSON_UNESCAPED_UNICODE 89 90 ); 90 91 return; … … 98 99 array( 99 100 'message' => __( 'API Key not configured.', 'readmo-ai' ), 100 ) 101 ), 102 null, 103 JSON_UNESCAPED_UNICODE 101 104 ); 102 105 return; … … 114 117 array( 115 118 'message' => __( 'Failed to retrieve client ID.', 'readmo-ai' ), 116 ) 119 ), 120 null, 121 JSON_UNESCAPED_UNICODE 117 122 ); 118 123 return; … … 148 153 array( 149 154 'message' => __( 'Event tracked successfully.', 'readmo-ai' ), 150 ) 155 ), 156 null, 157 JSON_UNESCAPED_UNICODE 151 158 ); 152 159 } -
readmo-ai/trunk/Infrastructure/dao/class-readmo-ai-settings-dao.php
r3447021 r3447574 69 69 $settings = get_option( self::OPTION_NAME, array() ); 70 70 71 // If no settings exist, initialize with empty defaults in database. 72 if ( empty( $settings ) ) { 73 $defaults = array( 'api_key' => '' ); 74 update_option( self::OPTION_NAME, $defaults, true ); 75 return ''; 76 } 77 71 78 if ( empty( $settings['api_key'] ) ) { 72 79 return ''; … … 118 125 */ 119 126 public function get_auto_insert_settings() { 120 // Default: all empty arrays = all checked = apply to all content.127 // Default: enabled = true, only 'post' type is checked (page is excluded). 121 128 $defaults = array( 122 'enabled' => false,129 'enabled' => true, 123 130 'position' => 'after_content', 124 'excluded_post_types' => array( ),131 'excluded_post_types' => array( 'page' ), 125 132 'excluded_categories' => array(), 126 133 'excluded_posts' => array(), … … 129 136 $settings = get_option( self::AUTO_INSERT_OPTION_NAME, array() ); 130 137 138 // If no settings exist, initialize with defaults in database. 131 139 if ( empty( $settings ) ) { 140 update_option( self::AUTO_INSERT_OPTION_NAME, $defaults, true ); 132 141 return $defaults; 133 142 } … … 161 170 } 162 171 163 /**164 * Delete auto-insert settings165 *166 * Removes the auto-insert settings option entirely.167 *168 * @since 1.2.0169 * @return bool True on success, false on failure.170 */171 public function delete_auto_insert_settings() {172 return delete_option( self::AUTO_INSERT_OPTION_NAME );173 }174 172 } -
readmo-ai/trunk/View/admin/class-readmo-ai-admin-settings-view.php
r3447021 r3447574 41 41 // Using "excluded" storage: empty arrays = all checked (apply to all). 42 42 $ai_enabled = ! empty( $auto_insert_settings['enabled'] ); 43 $ai_position = isset( $auto_insert_settings['position'] ) ? $auto_insert_settings['position'] : 'after_content';44 43 $ai_excluded_post_types = isset( $auto_insert_settings['excluded_post_types'] ) ? $auto_insert_settings['excluded_post_types'] : array(); 45 44 $ai_excluded_categories = isset( $auto_insert_settings['excluded_categories'] ) ? $auto_insert_settings['excluded_categories'] : array(); … … 110 109 /> 111 110 <label for="readmo-ai-auto-insert-enabled" class="readmo-ai-toggle-slider"></label> 112 </div>113 </div>114 115 <!-- Insert Position -->116 <div class="readmo-ai-setting-column">117 <label class="readmo-ai-label">118 <span class="readmo-ai-text"><?php echo esc_html( __( 'Insert Position', 'readmo-ai' ) ); ?></span>119 </label>120 <div class="readmo-ai-radio-group">121 <label class="readmo-ai-radio-label">122 <input123 type="radio"124 name="auto_insert_position"125 value="after_content"126 <?php checked( $ai_position, 'after_content' ); ?>127 />128 <span class="readmo-ai-radio-dot"></span>129 <span class="readmo-ai-radio-text"><?php echo esc_html( __( 'After content', 'readmo-ai' ) ); ?></span>130 </label>131 <label class="readmo-ai-radio-label">132 <input133 type="radio"134 name="auto_insert_position"135 value="footer"136 <?php checked( $ai_position, 'footer' ); ?>137 />138 <span class="readmo-ai-radio-dot"></span>139 <span class="readmo-ai-radio-text"><?php echo esc_html( __( 'Page footer', 'readmo-ai' ) ); ?></span>140 </label>141 111 </div> 142 112 </div> … … 255 225 </div> 256 226 257 <div class="readmo-ai-action readmo-ai-auto-insert-actions">227 <div class="readmo-ai-action"> 258 228 <button type="button" id="readmo-ai-save-auto-insert" class="btn btn-ban"> 259 229 <?php echo esc_html( __( 'Save Settings', 'readmo-ai' ) ); ?> 260 230 </button> 261 <button type="button" id="readmo-ai-remove-auto-insert" class="btn btn-danger">262 <?php echo esc_html( __( 'Disable and Remove', 'readmo-ai' ) ); ?>263 </button>264 </div>265 </div>266 267 <!-- Remove Confirmation Modal -->268 <div id="readmo-ai-confirm-modal" class="readmo-ai-modal" style="display: none;">269 <div class="readmo-ai-modal-overlay"></div>270 <div class="readmo-ai-modal-content">271 <span class="readmo-ai-title"><?php echo esc_html( __( 'Confirm Remove', 'readmo-ai' ) ); ?></span>272 <span class="readmo-ai-text"><?php echo esc_html( __( 'Are you sure you want to disable and remove all auto-insert settings? This action cannot be undone.', 'readmo-ai' ) ); ?></span>273 <div class="readmo-ai-modal-actions">274 <button type="button" id="readmo-ai-modal-cancel" class="btn btn-tertiary">275 <?php echo esc_html( __( 'Cancel', 'readmo-ai' ) ); ?>276 </button>277 <button type="button" id="readmo-ai-modal-confirm" class="btn btn-danger">278 <?php echo esc_html( __( 'Confirm Remove', 'readmo-ai' ) ); ?>279 </button>280 </div>281 231 </div> 282 232 </div> -
readmo-ai/trunk/assets/css/admin.css
r3447021 r3447574 15 15 align-items: center; 16 16 flex-direction: column; 17 gap: 12px; 17 gap: 24px; 18 padding: 32px 0; 18 19 font-family: Noto Sans TC, PingFang TC, Arial, Helvetica, LiHei Pro, Microsoft JhengHei, MingLiU, sans-serif; 19 20 } … … 53 54 display: flex; 54 55 flex-direction: column; 55 min-width: 720px; 56 width: 50vw; 57 max-width: 100%; 56 58 background-color: #ffffff; 57 59 border-radius: 12px; … … 186 188 } 187 189 188 /* Danger Button */189 .btn-danger {190 background: rgb(235, 67, 67);191 color: #ffffff;192 border: none;193 padding: 16px 20px;194 }195 196 .btn-danger:hover {197 background: rgb(220, 38, 38);198 }199 200 190 /* Ban Button (Disabled State) */ 201 191 .btn-ban, … … 434 424 .readmo-ai-content-tree { 435 425 max-height: 400px; 426 max-width: 100%; 436 427 overflow: auto; 437 428 padding: 12px; … … 491 482 cursor: pointer; 492 483 flex: 1; 484 min-width: 0; 485 overflow: hidden; 493 486 } 494 487 … … 555 548 color: var(--Text-Neutral-400, rgba(74, 75, 88, 1)); 556 549 transition: color 0.15s ease; 550 overflow: hidden; 551 text-overflow: ellipsis; 552 white-space: nowrap; 557 553 } 558 554 … … 588 584 } 589 585 590 /* Auto-Insert Actions */591 .readmo-ai-auto-insert-actions {592 justify-content: space-between;593 }594 595 /* Modal Styles */596 .readmo-ai-modal {597 position: fixed;598 top: 0;599 left: 0;600 width: 100%;601 height: 100%;602 z-index: 100000;603 display: flex;604 align-items: center;605 justify-content: center;606 }607 608 .readmo-ai-modal-overlay {609 position: absolute;610 top: 0;611 left: 0;612 width: 100%;613 height: 100%;614 background: rgba(0, 0, 0, 0.5);615 }616 617 .readmo-ai-modal-content {618 display: flex;619 flex-direction: column;620 gap: 24px;621 position: relative;622 background: #ffffff;623 padding: 24px;624 border-radius: 12px;625 max-width: 400px;626 width: 90%;627 box-shadow: 0 4px 20px rgba(0, 0, 0, 0.15);628 }629 630 .readmo-ai-modal-content h3 {631 font-size: 18px;632 font-weight: 600;633 color: var(--Text-Neutral-400, rgba(74, 75, 88, 1));634 }635 636 .readmo-ai-modal-content p {637 font-size: 14px;638 color: var(--Text-Neutral-300, rgba(113, 113, 113, 1));639 line-height: 1.5;640 }641 642 .readmo-ai-modal-actions {643 display: flex;644 justify-content: flex-end;645 gap: 12px;646 }647 648 .readmo-ai-modal-actions .btn {649 width: auto;650 padding: 10px 20px;651 }652 653 586 @media screen and (max-width: 767px) { 654 587 .readmo-ai-settings-container { … … 679 612 } 680 613 681 .btn-danger {682 width: auto;683 padding: 12px 16px;684 }685 686 .readmo-ai-auto-insert-actions {687 flex-direction: column;688 gap: 12px;689 }690 691 .readmo-ai-auto-insert-actions .btn {692 width: 100%;693 }694 695 614 .readmo-ai-checkbox-group, 696 615 .readmo-ai-radio-group { -
readmo-ai/trunk/assets/css/frontend.css
r3411004 r3447574 35 35 36 36 .readmo-ai-question-icon { 37 width: 2 8px;38 height: 2 8px;37 width: 24px; 38 height: 24px; 39 39 flex-shrink: 0; 40 40 } … … 42 42 .readmo-ai-question-title { 43 43 font-weight: 600; 44 font-size: 2 4px;44 font-size: 20px; 45 45 color: #215376; 46 line-height: 2 8px;46 line-height: 24px; 47 47 } 48 48 … … 50 50 .readmo-ai-questions { 51 51 display: flex; 52 flex-wrap: wrap; 52 flex-direction: column; 53 align-items: flex-start; 53 54 gap: 12px; 54 55 margin: 0; … … 59 60 /* Question Button Styles */ 60 61 .readmo-ai-question-btn { 62 display: inline-flex; 61 63 position: relative; 62 64 border-radius: 12px; 63 padding: 1 rem;65 padding: 12px 16px; 64 66 background-color: #ffffff; 65 67 box-shadow: 0px 2px 4px 0px rgba(62, 179, 218, 0.2); … … 107 109 font-weight: 400; 108 110 color: #26272C; 109 font-size: 20px;110 line-height: 2 4px;111 font-size: 16px; 112 line-height: 20px; 111 113 transition: 0.3s ease-out; 114 text-decoration: none; 112 115 } 113 116 … … 123 126 @media screen and (max-width: 767px) { 124 127 .readmo-ai-question-title { 125 font-size: 20px;126 line-height: 2 4px;128 font-size: 16px; 129 line-height: 20px; 127 130 } 128 131 129 132 .readmo-ai-question-btn { 130 font-size: 1 6px;131 line-height: 20px;132 padding: 10px 14px;133 font-size: 12px; 134 line-height: 16px; 135 padding: 8px 12px; 133 136 } 134 137 } -
readmo-ai/trunk/assets/js/admin.js
r3447021 r3447574 256 256 cacheElements: function () { 257 257 this.$enabledCheckbox = $( '#readmo-ai-auto-insert-enabled' ); 258 this.$positionRadios = $( 'input[name="auto_insert_position"]' );259 258 this.$fromUrlInput = $( '#readmo-ai-from-url' ); 260 259 this.$contentTree = $( '#readmo-ai-content-tree' ); 261 260 this.$saveButton = $( '#readmo-ai-save-auto-insert' ); 262 this.$removeButton = $( '#readmo-ai-remove-auto-insert' );263 this.$modal = $( '#readmo-ai-confirm-modal' );264 this.$modalCancel = $( '#readmo-ai-modal-cancel' );265 this.$modalConfirm = $( '#readmo-ai-modal-confirm' );266 this.$modalOverlay = $( '.readmo-ai-modal-overlay' );267 261 }, 268 262 … … 321 315 return { 322 316 enabled: this.$enabledCheckbox.is( ':checked' ), 323 position: $( 'input[name="auto_insert_position"]:checked' ).val(),324 317 fromUrl: this.$fromUrlInput.val(), 325 318 excludedPostTypes: excludedPostTypes, … … 367 360 }); 368 361 369 this.$positionRadios.on( 'change', function () {370 self.updateSaveButtonState();371 });372 373 362 this.$fromUrlInput.on( 'input', function () { 374 363 self.updateSaveButtonState(); … … 402 391 e.preventDefault(); 403 392 self.saveSettings(); 404 });405 406 // Remove button click.407 this.$removeButton.on( 'click', function ( e ) {408 e.preventDefault();409 self.showModal();410 });411 412 // Modal cancel.413 this.$modalCancel.on( 'click', function ( e ) {414 e.preventDefault();415 self.hideModal();416 });417 418 // Modal overlay click.419 this.$modalOverlay.on( 'click', function () {420 self.hideModal();421 });422 423 // Modal confirm.424 this.$modalConfirm.on( 'click', function ( e ) {425 e.preventDefault();426 self.removeSettings();427 });428 429 // ESC key to close modal.430 $( document ).on( 'keydown', function ( e ) {431 if ( e.key === 'Escape' && self.$modal.is( ':visible' ) ) {432 self.hideModal();433 }434 393 }); 435 394 }, … … 582 541 nonce: readmoAiAdminData.nonce, 583 542 enabled: values.enabled ? 1 : 0, 584 position: values.position,585 543 from_url: values.fromUrl, 586 544 excluded_post_types: values.excludedPostTypes, … … 624 582 } 625 583 }); 626 },627 628 /**629 * Show confirmation modal.630 */631 showModal: function () {632 this.$modal.fadeIn( 200 );633 },634 635 /**636 * Hide confirmation modal.637 */638 hideModal: function () {639 this.$modal.fadeOut( 200 );640 },641 642 /**643 * Remove auto-insert settings via AJAX.644 */645 removeSettings: function () {646 var self = this;647 648 // Disable confirm button.649 this.$modalConfirm.prop( 'disabled', true ).text( readmoAiAdminData.i18n.saving );650 651 $.ajax({652 type: 'POST',653 url: readmoAiAdminData.ajaxUrl,654 data: {655 action: 'readmo_ai_delete_auto_insert_settings',656 nonce: readmoAiAdminData.nonce657 },658 success: function ( response ) {659 if ( response.success ) {660 // Reset form to defaults (all checked).661 self.$enabledCheckbox.prop( 'checked', false );662 $( 'input[name="auto_insert_position"][value="after_content"]' ).prop( 'checked', true );663 self.$fromUrlInput.val( '' );664 665 // Check all tree items.666 self.$contentTree.find( '.readmo-ai-tree-input' ).prop( 'checked', true ).prop( 'indeterminate', false );667 self.$contentTree.find( '.readmo-ai-tree-node' ).removeClass( 'unchecked' );668 669 // Update original values.670 self.originalValues = self.getFormValues();671 672 // Reset button state.673 self.$saveButton674 .removeClass( 'btn-secondary' )675 .addClass( 'btn-ban' )676 .prop( 'disabled', true );677 678 // Hide modal.679 self.hideModal();680 681 // Re-enable confirm button.682 self.$modalConfirm.prop( 'disabled', false ).text( readmoAiAdminData.i18n.confirmRemove || 'Confirm Remove' );683 684 // Show success message.685 ReadmoAiAdmin.showNotice( 'success', response.data.message );686 } else {687 // Show error message.688 ReadmoAiAdmin.showNotice( 'error', response.data.message );689 690 // Hide modal.691 self.hideModal();692 693 // Re-enable confirm button.694 self.$modalConfirm.prop( 'disabled', false ).text( readmoAiAdminData.i18n.confirmRemove || 'Confirm Remove' );695 }696 },697 error: function () {698 // Show error message.699 ReadmoAiAdmin.showNotice( 'error', 'An error occurred while removing settings.' );700 701 // Hide modal.702 self.hideModal();703 704 // Re-enable confirm button.705 self.$modalConfirm.prop( 'disabled', false ).text( readmoAiAdminData.i18n.confirmRemove || 'Confirm Remove' );706 }707 });708 584 } 709 585 }; -
readmo-ai/trunk/assets/svg/question.svg
r3411004 r3447574 1 <svg width="2 8" height="28" viewBox="0 0 64 64" fill="none" xmlns="http://www.w3.org/2000/svg">1 <svg width="24" height="24" viewBox="0 0 64 64" fill="none" xmlns="http://www.w3.org/2000/svg"> 2 2 <g clip-path="url(#clip0_2581_15276)"> 3 3 <path d="M43.4926 22.9982C43.5179 19.3262 42.5599 15.7144 40.7181 12.5377C38.8762 9.36101 36.2177 6.73523 33.0184 4.93294C29.819 3.13066 26.1956 2.21758 22.5243 2.28851C18.853 2.35943 15.2675 3.41177 12.1402 5.33628C9.01287 7.26076 6.45767 9.9873 4.73991 13.2327C3.02212 16.4782 2.20436 20.1243 2.37152 23.7926C2.53867 27.4608 3.68466 31.0174 5.69049 34.0932C7.6963 37.169 10.4889 39.6518 13.7783 41.2839V48.1411C13.7783 48.7473 14.0191 49.3288 14.4478 49.7571C14.8764 50.1859 15.4578 50.4268 16.064 50.4268H29.7783C30.3845 50.4268 30.9659 50.1859 31.3945 49.7571C31.8232 49.3288 32.064 48.7473 32.064 48.1411V41.2839C35.481 39.6003 38.3608 36.9972 40.3797 33.767C42.3987 30.5367 43.4766 26.8074 43.4926 22.9982Z" fill="url(#paint0_radial_2581_15276)"/> -
readmo-ai/trunk/readme.txt
r3447213 r3447574 5 5 Tested up to: 6.9 6 6 Requires PHP: 7.4 7 Stable tag: 1.2. 17 Stable tag: 1.2.2 8 8 License: GPLv2 or later 9 9 License URI: https://www.gnu.org/licenses/gpl-2.0.html -
readmo-ai/trunk/readmo-ai.php
r3447213 r3447574 13 13 * Plugin Name: Readmo AI 14 14 * Description: AI-powered content analysis and optimization for WordPress with analytics tracking 15 * Version: 1.2. 115 * Version: 1.2.2 16 16 * Requires at least: 5.9 17 17 * Requires PHP: 7.4 … … 33 33 */ 34 34 if ( ! defined( 'READMO_AI_VERSION' ) ) { 35 define( 'READMO_AI_VERSION', '1.2. 1' );35 define( 'READMO_AI_VERSION', '1.2.2' ); 36 36 } 37 37
Note: See TracChangeset
for help on using the changeset viewer.