Changeset 3470221
- Timestamp:
- 02/26/2026 11:32:23 AM (4 weeks ago)
- Location:
- angie/trunk
- Files:
-
- 13 edited
-
angie.php (modified) (2 diffs)
-
modules/angie-app/assets/askAngieImage.png (modified) (previous)
-
modules/angie-app/components/angie-app.php (modified) (2 diffs)
-
modules/code-snippets/classes/deployment-meta-box.php (modified) (1 diff)
-
modules/code-snippets/classes/dev-mode-admin-ui.php (modified) (1 diff)
-
modules/code-snippets/classes/dev-mode-manager.php (modified) (1 diff)
-
modules/code-snippets/classes/list-table-manager.php (modified) (1 diff)
-
modules/code-snippets/classes/rest-api-controller.php (modified) (14 diffs)
-
modules/code-snippets/classes/snippet-repository.php (modified) (4 diffs)
-
modules/code-snippets/classes/snippet-validator.php (modified) (1 diff)
-
modules/consent-manager/assets/askAngieImage.png (modified) (previous)
-
modules/sidebar/components/sidebar-admin-bar.php (modified) (2 diffs)
-
readme.txt (modified) (2 diffs)
Legend:
- Unmodified
- Added
- Removed
-
angie/trunk/angie.php
r3464872 r3470221 4 4 * Description: Agentic AI for WordPress 5 5 * Plugin URI: https://elementor.com/pages/angie-early-access 6 * Version: 1.1. 06 * Version: 1.1.1 7 7 * Author: Elementor.com 8 8 * Author URI: https://elementor.com/ … … 21 21 } 22 22 23 define( 'ANGIE_VERSION', '1.1. 0' );23 define( 'ANGIE_VERSION', '1.1.1' ); 24 24 define( 'ANGIE_PATH', plugin_dir_path( __FILE__ ) ); 25 25 define( 'ANGIE_URL', plugins_url( '/', __FILE__ ) ); -
angie/trunk/modules/angie-app/components/angie-app.php
r3464872 r3470221 37 37 // Custom SVG icon for Angie menu 38 38 $svg_icon = 'data:image/svg+xml;base64,' . base64_encode( 39 '<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" fill="#a7aaad" viewBox="0 0 20 20"><path d="M0 3.36h3.36v14.428H0zM17.788 0v3.36H3.36V0zM12.225 4.413c.818 3.938 3.701 7.21 7.719 7.79-4.146.6-7.035 3.622-7.72 7.74-.643-3.872-3.96-6.885-7.755-7.74 3.798-.814 7.063-3.946 7.756-7.79ZM17.748 4.47c.233 1.113 1.109 2.036 2.252 2.2-1.18.17-2.057 1.047-2.252 2.213-.183-1.096-1.081-1.971-2.161-2.213 1.08-.23 1.964-1.113 2.16-2.2Z"/></svg>' 39 '<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none"> 40 <path d="M21.7142 12.0059L21.0316 12.2611C19.4275 12.8645 18.4491 14.1524 17.8575 15.7883L17.6072 16.4845L17.3569 15.7883C16.7653 14.1524 15.73 12.8645 14.1259 12.2611L13.4433 12.0059L14.1259 11.7506C15.73 11.1473 16.7653 9.85935 17.3569 8.22337L17.6072 7.52721L17.8575 8.22337C18.4491 9.85935 19.4275 11.1473 21.0316 11.7506L21.7142 12.0059Z" fill="#ABAFB2"/> 41 <path d="M3 11.1104C8.94295 11.1104 13.707 16.0116 13.707 22L10.707 22C10.707 17.6126 7.23077 14.1104 3 14.1104L3 11.1104Z" fill="#ABAFB2"/> 42 <path d="M3 9.88965C7.23076 9.88965 10.707 6.38737 10.707 2L13.707 2C13.707 7.98835 8.94295 12.8896 3 12.8896L3 9.88965Z" fill="#ABAFB2"/> 43 </svg>' 40 44 ); 41 45 … … 243 247 <div class="angie-app-start" id="angie-app-start" data-testid="angie-app-start"> 244 248 <h4> 245 <?php esc_html_e( 'Meet Angie,', 'angie' ); ?><br> 246 <?php esc_html_e( 'your new AI assistant', 'angie' ); ?> 249 <?php esc_html_e( 'Let\'s build something new with Angie', 'angie' ); ?><br> 247 250 </h4> 248 <p><?php esc_html_e( 'Build and manage WordPress sites effortlessly with Agentic AI.', 'angie' ); ?></p>251 <p><?php esc_html_e( 'Generate custom Elementor widgets, forms, layouts, and code snippets tailored to your site.', 'angie' ); ?><br><?php esc_html_e( 'Scale your capabilities and experiment freely. Everything is editable and nothing goes live until you approve.', 'angie' ); ?></p> 249 252 <?php if ( $is_in_oauth_flow ) : ?> 250 253 <div class="angie-loading-state" data-testid="angie-loading-state"> -
angie/trunk/modules/code-snippets/classes/deployment-meta-box.php
r3464872 r3470221 147 147 148 148 echo '<p>'; 149 $is_deploy_button_disabled = Dev_Mode_Manager::is_deploy_button_disabled( $dev_time, $prod_time ); 149 150 $deploy_action = ( $dev_time > 0 ) ? 'push-to-production' : 'publish-to-dev'; 150 151 $button_text = ( $dev_time > 0 ) ? esc_html__( 'Push to Production', 'angie' ) : esc_html__( 'Push to Test', 'angie' ); 151 submit_button( $button_text, 'primary', 'angie_push_to_production', false, [ 'data-action' => $deploy_action ] ); 152 $button_attrs = [ 'data-action' => $deploy_action ]; 153 if ( $is_deploy_button_disabled ) { 154 $button_attrs['disabled'] = 'disabled'; 155 } 156 submit_button( $button_text, 'primary', 'angie_push_to_production', false, $button_attrs ); 152 157 echo '</p>'; 153 158 echo '</div>'; -
angie/trunk/modules/code-snippets/classes/dev-mode-admin-ui.php
r3464872 r3470221 28 28 29 29 if ( $is_dev_mode ) { 30 echo '<div class="notice notice-warning is-dismissible">';30 echo '<div class="notice notice-warning">'; 31 31 echo '<p>'; 32 32 echo '<strong>' . esc_html__( 'Test Mode is Active', 'angie' ) . '</strong> - ' . esc_html__( 'Snippets are loading from the development environment.', 'angie' ) . ' '; -
angie/trunk/modules/code-snippets/classes/dev-mode-manager.php
r3464872 r3470221 177 177 } 178 178 179 public static function is_deploy_button_disabled( $dev_time, $prod_time ) { 180 return $dev_time > 0 && $prod_time >= $dev_time; 181 } 182 179 183 public static function push_snippet_to_dev( $post_id ) { 180 184 $files = get_post_meta( $post_id, '_angie_snippet_files', true ); -
angie/trunk/modules/code-snippets/classes/list-table-manager.php
r3464872 r3470221 171 171 $dev_time = $timestamps['dev']; 172 172 $prod_time = $timestamps['prod']; 173 174 if ( $dev_time === 0 ) { 175 $is_disabled = false; 176 } elseif ( $dev_time > 0 && ( $prod_time === 0 || $dev_time > $prod_time ) ) { 177 $is_disabled = false; 178 } else { 179 $is_disabled = true; 180 } 173 $is_deploy_button_disabled = Dev_Mode_Manager::is_deploy_button_disabled( $dev_time, $prod_time ); 181 174 182 175 $post = get_post( $post_id ); 183 176 $snippet_slug = $post ? $post->post_name : ''; 184 177 185 $disabled_attr = $is_d isabled ? ' disabled' : '';178 $disabled_attr = $is_deploy_button_disabled ? ' disabled' : ''; 186 179 $deploy_action = ( $dev_time > 0 ) ? 'push-to-production' : 'publish-to-dev'; 187 180 $button_text = ( $dev_time > 0 ) ? esc_html__( 'Push to Live', 'angie' ) : esc_html__( 'Push to Test', 'angie' ); -
angie/trunk/modules/code-snippets/classes/rest-api-controller.php
r3464872 r3470221 36 36 ], 37 37 ], 38 [ 39 'methods' => \WP_REST_Server::CREATABLE, 40 'callback' => [ $this, 'create_snippet_post' ], 41 'permission_callback' => [ $this, 'check_permission' ], 42 'args' => [ 43 'title' => [ 44 'required' => true, 45 'type' => 'string', 46 'sanitize_callback' => 'sanitize_text_field', 47 ], 48 'type' => [ 49 'required' => false, 50 'type' => 'string', 51 'sanitize_callback' => 'sanitize_text_field', 52 ], 53 ], 54 ], 55 ] 56 ); 57 58 register_rest_route( 59 self::NAMESPACE, 60 '/snippets/(?P<id>\d+)', 61 [ 62 [ 63 'methods' => \WP_REST_Server::DELETABLE, 64 'callback' => [ $this, 'delete_snippet' ], 65 'permission_callback' => [ $this, 'check_permission' ], 66 'args' => [ 67 'id' => [ 68 'required' => true, 69 'type' => 'integer', 70 'sanitize_callback' => 'absint', 71 ], 72 ], 73 ], 74 ] 75 ); 76 77 register_rest_route( 78 self::NAMESPACE, 79 '/snippets/(?P<id>\d+)/files', 80 [ 81 [ 82 'methods' => \WP_REST_Server::READABLE, 83 'callback' => [ $this, 'list_snippet_files' ], 84 'permission_callback' => [ $this, 'check_permission' ], 85 'args' => [ 86 'id' => [ 87 'required' => true, 88 'type' => 'integer', 89 'sanitize_callback' => 'absint', 90 ], 91 ], 92 ], 93 [ 94 'methods' => 'PUT, PATCH', 95 'callback' => [ $this, 'update_snippet_files' ], 96 'permission_callback' => [ $this, 'check_permission' ], 97 'args' => [ 98 'id' => [ 99 'required' => true, 100 'type' => 'integer', 101 'sanitize_callback' => 'absint', 102 ], 103 'files' => [ 104 'required' => true, 105 'type' => 'array', 106 ], 107 'type' => [ 108 'required' => false, 109 'type' => 'string', 110 'sanitize_callback' => 'sanitize_text_field', 111 ], 112 ], 113 ], 114 ] 115 ); 116 117 register_rest_route( 118 self::NAMESPACE, 119 '/snippets/(?P<id>\d+)/files/(?P<filename>.+)', 120 [ 121 [ 122 'methods' => \WP_REST_Server::READABLE, 123 'callback' => [ $this, 'get_snippet_file' ], 124 'permission_callback' => [ $this, 'check_permission' ], 125 'args' => [ 126 'id' => [ 127 'required' => true, 128 'type' => 'integer', 129 'sanitize_callback' => 'absint', 130 ], 131 'filename' => [ 132 'required' => true, 133 'type' => 'string', 134 'sanitize_callback' => 'sanitize_text_field', 135 ], 136 ], 137 ], 138 ] 139 ); 140 141 register_rest_route( 142 self::NAMESPACE, 143 '/snippets/(?P<id>\d+)/publish', 144 [ 145 [ 146 'methods' => \WP_REST_Server::CREATABLE, 147 'callback' => [ $this, 'publish_snippet' ], 148 'permission_callback' => [ $this, 'check_permission' ], 149 'args' => [ 150 'id' => [ 151 'required' => true, 152 'type' => 'integer', 153 'sanitize_callback' => 'absint', 154 ], 155 ], 156 ], 38 157 ] 39 158 ); … … 109 228 'type' => 'string', 110 229 'sanitize_callback' => 'sanitize_text_field', 230 ], 231 'types' => [ 232 'required' => false, 233 'type' => 'array', 234 'items' => [ 235 'type' => 'string', 236 'sanitize_callback' => 'sanitize_text_field', 237 ], 111 238 ], 112 239 ], … … 251 378 252 379 public function list_snippet_files( $request ) { 253 $slug = $request->get_param( 'slug' ); 254 $post = Snippet_Repository::find_snippet_post_by_slug( $slug ); 380 $post = $this->resolve_snippet_post( $request ); 255 381 256 382 if ( ! $post ) { … … 271 397 272 398 public function get_snippet_file( $request ) { 273 $ slug = $request->get_param( 'slug');399 $post = $this->resolve_snippet_post( $request ); 274 400 $filename = $request->get_param( 'filename' ); 275 $post = Snippet_Repository::find_snippet_post_by_slug( $slug );276 401 277 402 if ( ! $post ) { … … 300 425 301 426 return rest_ensure_response( [ 302 'name' => $file['name'],427 'name' => $file['name'], 303 428 'content' => $content, 304 'size' => strlen( $content ),429 'size' => strlen( $content ), 305 430 ] ); 306 431 } … … 310 435 $files = $request->get_param( 'files' ); 311 436 $overwrite = $request->get_param( 'overwrite' ); 312 $type = $request->get_param( 'type' ); 437 $types = $request->get_param( 'types' ); 438 if ( ! is_array( $types ) || empty( $types ) ) { 439 $single_type = $request->get_param( 'type' ); 440 if ( is_string( $single_type ) && '' !== trim( $single_type ) ) { 441 $types = [ sanitize_text_field( $single_type ) ]; 442 } 443 } 313 444 314 445 if ( ! is_array( $files ) || empty( $files ) ) { … … 396 527 } 397 528 398 if ( ! empty( $type ) && ! Taxonomy_Manager::is_valid_type( $type ) ) { 399 return new \WP_Error( 400 'invalid_type', 401 sprintf( 402 /* translators: %s: provided type value */ 403 esc_html__( 'Invalid snippet type: %s. Valid types are: code-snippet, elementor-widget, gutenberg-block, popup, form, visual-app.', 'angie' ), 404 $type 405 ), 406 [ 'status' => 400 ] 407 ); 529 if ( ! empty( $types ) ) { 530 if ( ! is_array( $types ) ) { 531 return new \WP_Error( 532 'invalid_types', 533 esc_html__( 'Types parameter must be an array.', 'angie' ), 534 [ 'status' => 400 ] 535 ); 536 } 537 538 foreach ( $types as $snippet_type ) { 539 if ( ! Taxonomy_Manager::is_valid_type( $snippet_type ) ) { 540 return new \WP_Error( 541 'invalid_type', 542 sprintf( 543 /* translators: %s: provided type value */ 544 esc_html__( 'Invalid snippet type: %s. Valid types are: code-snippet, elementor-widget, gutenberg-block, popup, form, visual-app.', 'angie' ), 545 $snippet_type 546 ), 547 [ 'status' => 400 ] 548 ); 549 } 550 } 408 551 } 409 552 … … 429 572 } 430 573 431 if ( ! empty( $type ) ) {432 wp_set_object_terms( $post_id, $type , Taxonomy_Manager::TAXONOMY_NAME );574 if ( ! empty( $types ) ) { 575 wp_set_object_terms( $post_id, $types, Taxonomy_Manager::TAXONOMY_NAME ); 433 576 } 434 577 … … 466 609 } 467 610 468 if ( ! empty( $type ) ) {469 wp_set_object_terms( $post->ID, $type , Taxonomy_Manager::TAXONOMY_NAME );611 if ( ! empty( $types ) ) { 612 wp_set_object_terms( $post->ID, $types, Taxonomy_Manager::TAXONOMY_NAME ); 470 613 } 471 614 … … 485 628 486 629 public function delete_snippet( $request ) { 487 $slug = $request->get_param( 'slug' ); 488 $post = Snippet_Repository::find_snippet_post_by_slug( $slug ); 630 $post = $this->resolve_snippet_post( $request ); 489 631 490 632 if ( ! $post ) { … … 514 656 'success' => true, 515 657 'message' => esc_html__( 'Snippet deleted successfully.', 'angie' ), 516 ' slug' => $slug,658 'post_id' => $post->ID, 517 659 ] ); 518 660 } … … 549 691 550 692 public function publish_snippet( $request ) { 551 $slug = $request->get_param( 'slug' ); 552 $post = Snippet_Repository::find_snippet_post_by_slug( $slug ); 693 $post = $this->resolve_snippet_post( $request ); 553 694 554 695 if ( ! $post ) { … … 576 717 'success' => true, 577 718 'message' => esc_html__( 'Snippet published to production successfully.', 'angie' ), 578 'slug' => $slug,579 719 'post_id' => $post->ID, 580 'files' => count( $files ),720 'files' => count( $files ), 581 721 ] ); 722 } 723 724 public function create_snippet_post( $request ) { 725 $title = $request->get_param( 'title' ); 726 $type = $request->get_param( 'type' ); 727 728 if ( empty( $title ) ) { 729 return new \WP_Error( 730 'missing_title', 731 esc_html__( 'Title is required.', 'angie' ), 732 [ 'status' => 400 ] 733 ); 734 } 735 736 if ( ! empty( $type ) && ! Taxonomy_Manager::is_valid_type( $type ) ) { 737 return new \WP_Error( 738 'invalid_type', 739 sprintf( 740 /* translators: %s: provided type value */ 741 esc_html__( 'Invalid snippet type: %s.', 'angie' ), 742 $type 743 ), 744 [ 'status' => 400 ] 745 ); 746 } 747 748 $post_id = Snippet_Repository::create_snippet( $title ); 749 750 if ( is_wp_error( $post_id ) ) { 751 return new \WP_Error( 752 'snippet_create_failed', 753 esc_html__( 'Failed to create snippet.', 'angie' ), 754 [ 'status' => 500 ] 755 ); 756 } 757 758 if ( ! empty( $type ) ) { 759 wp_set_object_terms( $post_id, $type, Taxonomy_Manager::TAXONOMY_NAME ); 760 } 761 762 return rest_ensure_response( [ 763 'id' => $post_id, 764 'title' => sanitize_text_field( $title ), 765 ] ); 766 } 767 768 public function update_snippet_files( $request ) { 769 $post = $this->resolve_snippet_post( $request ); 770 771 if ( ! $post ) { 772 return new \WP_Error( 773 'snippet_not_found', 774 esc_html__( 'Snippet not found.', 'angie' ), 775 [ 'status' => 404 ] 776 ); 777 } 778 779 $files = $request->get_param( 'files' ); 780 $type = $request->get_param( 'type' ); 781 782 $existing_files = Snippet_Repository::get_snippet_files_by_post( $post ); 783 $merged_files = Snippet_Repository::merge_snippet_files( $existing_files, $files ); 784 785 $sanitized_files = $this->sanitize_uploaded_files( $merged_files ); 786 if ( is_wp_error( $sanitized_files ) ) { 787 return $sanitized_files; 788 } 789 790 if ( ! Snippet_Repository::has_main_php_file( $sanitized_files ) ) { 791 return new \WP_Error( 792 'main_php_required', 793 esc_html__( 'Snippet must have a main.php file.', 'angie' ), 794 [ 'status' => 400 ] 795 ); 796 } 797 798 if ( ! empty( $type ) && ! Taxonomy_Manager::is_valid_type( $type ) ) { 799 return new \WP_Error( 800 'invalid_type', 801 sprintf( 802 /* translators: %s: provided type value */ 803 esc_html__( 'Invalid snippet type: %s.', 'angie' ), 804 $type 805 ), 806 [ 'status' => 400 ] 807 ); 808 } 809 810 if ( ! empty( $type ) ) { 811 wp_set_object_terms( $post->ID, $type, Taxonomy_Manager::TAXONOMY_NAME ); 812 } 813 814 Snippet_Repository::update_snippet_files( $post->ID, $sanitized_files ); 815 File_System_Handler::write_snippet_files_to_disk( Dev_Mode_Manager::ENV_DEV, $post->ID, $sanitized_files ); 816 Cache_Manager::clear_published_snippet_cache(); 817 818 return rest_ensure_response( [ 819 'success' => true, 820 'message' => esc_html__( 'Snippet files updated successfully.', 'angie' ), 821 'post_id' => $post->ID, 822 'files' => count( $sanitized_files ), 823 ] ); 824 } 825 826 private function resolve_snippet_post( $request ) { 827 $id = $request->get_param( 'id' ); 828 829 if ( $id ) { 830 return Snippet_Repository::find_snippet_post_by_id( $id ); 831 } 832 833 return Snippet_Repository::find_snippet_post_by_slug( $request->get_param( 'slug' ) ); 834 } 835 836 private function sanitize_uploaded_files( $files ) { 837 if ( ! is_array( $files ) || empty( $files ) ) { 838 return new \WP_Error( 839 'invalid_files', 840 esc_html__( 'Files parameter must be a non-empty array.', 'angie' ), 841 [ 'status' => 400 ] 842 ); 843 } 844 845 if ( count( $files ) > self::MAX_FILES_PER_REQUEST ) { 846 return new \WP_Error( 847 'too_many_files', 848 sprintf( 849 /* translators: %d: maximum number of files */ 850 esc_html__( 'Cannot process more than %d files per request.', 'angie' ), 851 self::MAX_FILES_PER_REQUEST 852 ), 853 [ 'status' => 400 ] 854 ); 855 } 856 857 $sanitized_files = []; 858 859 foreach ( $files as $file ) { 860 if ( ! isset( $file['name'] ) || ! isset( $file['content'] ) ) { 861 return new \WP_Error( 862 'invalid_file_format', 863 esc_html__( 'Each file must have "name" and "content" properties.', 'angie' ), 864 [ 'status' => 400 ] 865 ); 866 } 867 868 $name = File_Validator::sanitize_filename( $file['name'] ); 869 $content = $file['content']; 870 871 if ( empty( $name ) ) { 872 return new \WP_Error( 873 'invalid_filename', 874 esc_html__( 'File name cannot be empty or contains invalid characters.', 'angie' ), 875 [ 'status' => 400 ] 876 ); 877 } 878 879 $allowed_extensions = [ 'php', 'css', 'js' ]; 880 $extension = strtolower( pathinfo( $name, PATHINFO_EXTENSION ) ); 881 if ( ! in_array( $extension, $allowed_extensions, true ) ) { 882 return new \WP_Error( 883 'invalid_file_type', 884 sprintf( 885 /* translators: %s: filename */ 886 esc_html__( 'Invalid file type for: %s. Only PHP, CSS, and JS files are allowed.', 'angie' ), 887 $name 888 ), 889 [ 'status' => 400 ] 890 ); 891 } 892 893 if ( strlen( $content ) > self::MAX_FILE_SIZE_BYTES ) { 894 return new \WP_Error( 895 'file_too_large', 896 sprintf( 897 /* translators: %s: filename */ 898 esc_html__( 'File too large: %s. Maximum size is 100KB.', 'angie' ), 899 $name 900 ), 901 [ 'status' => 400 ] 902 ); 903 } 904 905 $sanitized_files[] = [ 906 'name' => $name, 907 'content_b64' => base64_encode( $content ), 908 ]; 909 } 910 911 $validation_result = Snippet_Validator::validate_snippet_files( $sanitized_files ); 912 913 if ( is_wp_error( $validation_result ) ) { 914 return new \WP_Error( 915 $validation_result->get_error_code(), 916 $validation_result->get_error_message(), 917 [ 'status' => 400 ] 918 ); 919 } 920 921 return $sanitized_files; 582 922 } 583 923 … … 607 947 $content = $file['content']; 608 948 609 $check_result = File_Validator::check_forbidden_functions( $content ); 610 if ( ! $check_result['allowed'] ) { 611 return new \WP_Error( 612 'forbidden_function', 613 sprintf( 614 /* translators: %s: function name */ 615 esc_html__( 'Forbidden function detected: %s', 'angie' ), 616 $check_result['function'] 617 ), 618 [ 'status' => 400 ] 619 ); 949 $extension = strtolower( pathinfo( $name, PATHINFO_EXTENSION ) ); 950 if ( 'php' === $extension ) { 951 $check_result = File_Validator::check_forbidden_functions( $content ); 952 if ( ! $check_result['allowed'] ) { 953 return new \WP_Error( 954 'forbidden_function', 955 sprintf( 956 /* translators: %s: function name */ 957 esc_html__( 'Forbidden function detected: %s', 'angie' ), 958 $check_result['function'] 959 ), 960 [ 'status' => 400 ] 961 ); 962 } 620 963 } 621 964 -
angie/trunk/modules/code-snippets/classes/snippet-repository.php
r3464872 r3470221 9 9 10 10 class Snippet_Repository { 11 12 public static function find_snippet_post_by_id( $id ) { 13 $post = get_post( (int) $id ); 14 15 if ( ! $post || Module::CPT_NAME !== $post->post_type ) { 16 return null; 17 } 18 19 return $post; 20 } 11 21 12 22 public static function find_snippet_post_by_slug( $slug ) { … … 44 54 } 45 55 46 public static function create_snippet( $ slug) {47 $post_id =wp_insert_post( [48 'post_title' => $slug,56 public static function create_snippet( $title ) { 57 return wp_insert_post( [ 58 'post_title' => sanitize_text_field( $title ), 49 59 'post_type' => Module::CPT_NAME, 50 60 'post_status' => 'publish', 51 61 ], true ); 52 53 return $post_id;54 62 } 55 63 … … 68 76 public static function update_snippet_files( $post_id, $files ) { 69 77 return update_post_meta( $post_id, '_angie_snippet_files', $files ); 78 } 79 80 public static function get_snippet_files_by_post( $post ) { 81 $files = self::get_snippet_files( $post->ID ); 82 foreach ( $files as &$file ) { 83 if ( ! isset( $file['content_b64'] ) || ! is_string( $file['content_b64'] ) ) { 84 $file['content'] = ''; 85 continue; 86 } 87 $decoded = base64_decode( $file['content_b64'], true ); 88 $file['content'] = ( false === $decoded ) ? '' : $decoded; 89 } 90 return $files; 70 91 } 71 92 … … 106 127 $files = self::get_snippet_files( $post->ID ); 107 128 $terms = wp_get_object_terms( $post->ID, Taxonomy_Manager::TAXONOMY_NAME, [ 'fields' => 'slugs' ] ); 129 $types = is_wp_error( $terms ) ? [] : $terms; 108 130 $timestamps = Dev_Mode_Manager::get_snippet_environment_timestamps( $post->ID ); 109 131 $is_elementor_widget = ! is_wp_error( $terms ) && in_array( 'elementor-widget', $terms, true ); 110 132 111 133 $data = [ 112 'id' => $post->ID,113 'slug' => self::get_snippet_slug_from_post( $post ),114 'title' => $post->post_title,115 'status' => $post->post_status,116 ' files' => self::build_file_list( $files ),117 'type' => is_wp_error( $terms ) ? [] : $terms,118 'deploymentStatus' => $timestamps['status'],134 'id' => $post->ID, 135 'slug' => self::get_snippet_slug_from_post( $post ), 136 'title' => $post->post_title, 137 'status' => $post->post_status, 138 'types' => $types, 139 'files' => self::build_file_list( $files ), 140 'deploymentStatus' => $timestamps['status'], 119 141 ]; 120 142 -
angie/trunk/modules/code-snippets/classes/snippet-validator.php
r3464872 r3470221 10 10 public static function validate_snippet_files( $files ) { 11 11 foreach ( $files as $file ) { 12 $extension = strtolower( pathinfo( $file['name'], PATHINFO_EXTENSION ) ); 13 if ( 'php' !== $extension ) { 14 continue; 15 } 16 12 17 $content_clean = base64_decode( $file['content_b64'] ); 13 18 -
angie/trunk/modules/sidebar/components/sidebar-admin-bar.php
r3363303 r3470221 57 57 $wp_admin_bar->add_node( [ 58 58 'id' => 'angie-sidebar-toggle', 59 'title' => ' Toggle Angie',59 'title' => '', 60 60 'href' => '#', 61 61 'meta' => [ 62 62 'class' => 'angie-sidebar-toggle-item', 63 'title' => ' Toggle Angie',63 'title' => '', 64 64 ], 65 65 ] ); … … 67 67 // No admin bar object = Elementor editor context, just create the basic element JavaScript needs 68 68 echo '<div id="wp-admin-bar-angie-sidebar-toggle" class="angie-sidebar-toggle-item" style="display: none;"> 69 <a href="#" class="ab-item" title=" Toggle Angie">Toggle Angie</a>69 <a href="#" class="ab-item" title=""></a> 70 70 </div>'; 71 71 } -
angie/trunk/readme.txt
r3464872 r3470221 5 5 Tested up to: 6.9 6 6 Requires PHP: 7.4 7 Stable tag: 1.1. 07 Stable tag: 1.1.1 8 8 License: GPLv3 9 9 License URI: https://www.gnu.org/licenses/gpl-3.0.html 10 10 11 == Description == 12 11 13 Angie Code: Your expert WordPress developer, powered by AI. Build anything you can imagine without writing a single line of code. 12 13 == Description ==14 14 15 15 **Disclaimer:** Angie is currently in Beta. While core functionality within WordPress and the Elementor Editor is stable and ready to explore, some actions and integrations with third-party tools are still evolving. We are continuously refining Angie's capabilities and expanding its capabilities. Please ensure you back up your site before use. During this Beta phase, enjoy free daily credits and help us shape the future of AI-driven web creation with your feedback. … … 161 161 162 162 == Changelog == 163 164 = 1.1.1 - 2026-02-26 = 165 * Tweak: Improved Code Snippets user interface 166 * Tweak: Added support for multiple snippet types 167 * Tweak: Refactored Code Snippets feature for improved structure 168 * Tweak: User interface refinements 163 169 164 170 = 1.1.0 - 2026-02-19 =
Note: See TracChangeset
for help on using the changeset viewer.