| @@ -59,8 +59,13 @@ | ||
| 59 | 59 | ) ); |
| 60 | 60 | #endregion |
| 61 | 61 | |
| 62 | 62 | #region MISC |
| 63 | + register_rest_route( $this->namespace, '/quick_start', array( | |
| 64 | + 'methods' => 'POST', | |
| 65 | + 'permission_callback' => array( $this->core, 'can_access_settings' ), | |
| 66 | + 'callback' => array( $this, 'rest_quick_start' ), | |
| 67 | + ) ); | |
| 63 | 68 | |
| 64 | 69 | register_rest_route( $this->namespace, '/server_clock', array( |
| 65 | 70 | 'methods' => 'GET', |
| 66 | 71 | 'permission_callback' => array( $this->core, 'can_access_settings' ), |
| @@ -66,8 +71,19 @@ | ||
| 66 | 71 | 'permission_callback' => array( $this->core, 'can_access_settings' ), |
| 67 | 72 | 'callback' => array( $this, 'rest_server_clock' ), |
| 68 | 73 | ) ); |
| 69 | 74 | |
| 75 | + register_rest_route( $this->namespace, '/fetch_posts', array( | |
| 76 | + 'methods' => 'POST', | |
| 77 | + 'permission_callback' => array( $this->core, 'can_access_features' ), | |
| 78 | + 'callback' => array( $this, 'rest_fetch_posts' ), | |
| 79 | + 'args' => array( | |
| 80 | + 'search' => array( 'required' => false ), | |
| 81 | + 'offset' => array( 'required' => false, 'default' => 0 ), | |
| 82 | + 'limit' => array( 'required' => false, 'default' => 10 ), | |
| 83 | + ) | |
| 84 | + ) ); | |
| 85 | + | |
| 70 | 86 | #endregion |
| 71 | 87 | |
| 72 | 88 | #region REST AI Engine |
| 73 | 89 | register_rest_route( $this->namespace, '/ai_read_comments', array( |
| @@ -146,9 +162,15 @@ | ||
| 146 | 162 | 'callback' => array($this, 'rest_replace'), |
| 147 | 163 | 'permission_callback' => array($this->core, 'can_access_settings') |
| 148 | 164 | )); |
| 149 | 165 | |
| 166 | + register_rest_route($this->namespace, '/snippets/delete_duplicates', array( | |
| 167 | + 'methods' => 'POST', | |
| 168 | + 'callback' => array($this, 'rest_delete_duplicates'), | |
| 169 | + 'permission_callback' => array($this->core, 'can_access_settings') | |
| 170 | + )); | |
| 150 | 171 | |
| 172 | + | |
| 151 | 173 | // SNIPPETS ENDPOINTS |
| 152 | 174 | $snippets = $this->snippet->select( |
| 153 | 175 | null, // offset |
| 154 | 176 | -1, // limit |
| @@ -315,10 +337,8 @@ | ||
| 315 | 337 | try { |
| 316 | 338 | $params = $request->get_json_params(); |
| 317 | 339 | $functions = $params['functions']; |
| 318 | 340 | |
| 319 | - error_log( print_r( $functions, 1 ) ); | |
| 320 | - | |
| 321 | 341 | if ( empty( $functions ) ) { |
| 322 | 342 | $functions = []; |
| 323 | 343 | } else { |
| 324 | 344 | // Decode the JSON string into a PHP array |
| @@ -342,8 +362,84 @@ | ||
| 342 | 362 | |
| 343 | 363 | |
| 344 | 364 | #region MISC |
| 345 | 365 | |
| 366 | + function rest_quick_start( $request ) { | |
| 367 | + global $mwai; | |
| 368 | + | |
| 369 | + if ( is_null( $mwai ) || !isset( $mwai ) ) { | |
| 370 | + return new WP_REST_Response([ 'success' => false, 'message' => 'AI Engine is not available.' ], 500 ); | |
| 371 | + } | |
| 372 | + | |
| 373 | + $params = $request->get_json_params(); | |
| 374 | + if( empty( $params['prompt'] ) ) { | |
| 375 | + return new WP_REST_Response([ 'success' => false, 'message' => 'Prompt is required.' ], 500 ); | |
| 376 | + } | |
| 377 | + | |
| 378 | + $user_prompt = "<user_prompt>" . $params['prompt'] . "</user_prompt>"; | |
| 379 | + | |
| 380 | + // Main instructions: explain the categories and how to generate the snippet | |
| 381 | + $prompt_instructions = "<instruction>" | |
| 382 | + . "Create a code snippet based on the user's prompt for a WordPress environment. " | |
| 383 | + . "The snippet can be in PHP or JS. If the prompt does not specify the language, assume PHP. " | |
| 384 | + . "Below are the categories you can use for the code snippet:\n\n" | |
| 385 | + . "1. 'function': This can be either a JS or PHP function. The snippet should only contain the function itself, " | |
| 386 | + . " with no code outside its declaration. The function can take parameters; for each parameter, specify a " | |
| 387 | + . " default value, type ('string', 'number', 'boolean', 'mixed', etc.), required boolean, and a description.\n" | |
| 388 | + . "2. 'scheduled': This can only be PHP. It’s code that will be run by a cron job for scheduled tasks. " | |
| 389 | + . " If the user wants a task to run over time or on a schedule, use this category. The scheduling itself " | |
| 390 | + . " is handled automatically, so you only need to generate the main code. You can specify a target hour " | |
| 391 | + . " and minute.\n" | |
| 392 | + . "3. 'persistent', 'back', 'front': This is a PHP-only snippet for use across the website, typically for WordPress filters or " | |
| 393 | + . " action hooks. You can specify if the snippet should be 'persistent', 'front', or 'back' to indicate " | |
| 394 | + . " whether it loads on the front end, back end, or both. These are know as global snippets.\n" | |
| 395 | + . "4. 'content': This can be either 'content_js' or 'content_php'. A shortcode will be generated for this " | |
| 396 | + . " snippet so the user can place it inside a post.\n" | |
| 397 | + . "</instruction>"; | |
| 398 | + | |
| 399 | + // Goal: specify what data to generate | |
| 400 | + $prompt_goal = "<goal>" | |
| 401 | + . "Generate the snippet’s code and the metadata needed to describe it, including:\n" | |
| 402 | + . "- A snippet name\n" | |
| 403 | + . "- A description\n" | |
| 404 | + . "- The type (one of 'backend', 'frontend', 'function', 'persistent', 'scheduled', 'content_php', 'content_js')\n" | |
| 405 | + . "- Tags (comma-separated)\n" | |
| 406 | + . "For certain types, include additional details:\n" | |
| 407 | + . "- For a 'function' type, provide the scope ('js' or 'php') and its parameters, including default values, types, required and descriptions.\n" | |
| 408 | + . "- For a 'scheduled' snippet, specify the target hour and minute.\n" | |
| 409 | + . "</goal>"; | |
| 410 | + | |
| 411 | + // Warning: any additional information the user should know | |
| 412 | + $prompt_warning = "<warning>" | |
| 413 | + . "Dont use any <?php tags, <script> tags or markdown ``` format, just provide the raw code. " | |
| 414 | + . " When using a 'scheduled' all the cron scheduling is handled automatically, so you only need to generate the main code and dont need to worry about the scheduling logic. " | |
| 415 | + . "If the type is 'function', the code should only contain the function itself, with no code outside its declaration, as well as no comments outside of the function. THIS IS THE CASE ONLY FOR FUNCTIONS. For any other type, the code is invoked using eval() and should be a complete snippet of code. If the snippet is a function type the default values should NOT be deplared in the parameters, this will be done automatically. If the snippet is not a function type but your wirte a function, make sure to call the function at the end of the code. " | |
| 416 | + . " If the snippet is a content type, the code should be the content of the shortcode, not the shortcode itself. and NOT a function, it should be raw code with an echo statement. There are no attributes for content snippets. The shortcode is generated automatically like [code-engine id=''] and the id is the snippet id. " | |
| 417 | + . "</warning>"; | |
| 418 | + | |
| 419 | + // Format: how to structure the final output | |
| 420 | + $prompt_format = "<format>" | |
| 421 | + . "Return all information in JSON format with the following keys:\n" | |
| 422 | + . "- 'code': the code for the snippet. It should not be on one line only, make sure it is well formated.\n" | |
| 423 | + . "- 'name': the snippet name\n" | |
| 424 | + . "- 'description': a description of the snippet\n" | |
| 425 | + . "- 'type': the snippet type\n" | |
| 426 | + . "- 'tags': the snippet tags\n" | |
| 427 | + . "- 'scope': the scope (only applicable if type is 'function')\n" | |
| 428 | + . "- 'parameters': an array of parameter objects (only for 'function'), each with 'name', 'default', 'type', and 'description'\n" | |
| 429 | + . "- 'target_hour': the target hour (only for 'scheduled')\n" | |
| 430 | + . "- 'target_minute': the target minute (only for 'scheduled')\n" | |
| 431 | + . "- 'misc': any additional information. If you had to assume some stuff, like a filter name or a thing the user didn't think about, let it know by adding messages in this key.\n" | |
| 432 | + . "</format>"; | |
| 433 | + | |
| 434 | + // Combine all prompt parts | |
| 435 | + $prompt = $prompt_instructions . $prompt_goal . $prompt_format . $prompt_warning . $user_prompt; | |
| 436 | + | |
| 437 | + $response = $mwai->simpleJsonQuery( $prompt ); | |
| 438 | + | |
| 439 | + return new WP_REST_Response( [ 'success' => true, 'data' => $response ], 200 ); | |
| 440 | + } | |
| 441 | + | |
| 346 | 442 | function rest_server_clock() { |
| 347 | 443 | $now = current_datetime(); |
| 348 | 444 | $now = $now->format( 'H:i:s ( Y/m/d )' ); |
| 349 | 445 | |
| @@ -349,8 +445,68 @@ | ||
| 349 | 445 | |
| 350 | 446 | return new WP_REST_Response( [ 'success' => true, 'time' => $now ], 200 ); |
| 351 | 447 | } |
| 352 | 448 | |
| 449 | + function rest_fetch_posts( $request ) { | |
| 450 | + try { | |
| 451 | + $params = $request->get_json_params(); | |
| 452 | + $search = isset($params['search']) ? $params['search'] : ''; | |
| 453 | + $offset = isset($params['offset']) ? intval($params['offset']) : 0; | |
| 454 | + $limit = isset($params['limit']) ? intval($params['limit']) : 10; | |
| 455 | + | |
| 456 | + global $wpdb; | |
| 457 | + $searchPlaceholder = $search ? '%' . $search . '%' : ''; | |
| 458 | + $where_search_clause = $search ? $wpdb->prepare( | |
| 459 | + "AND ( p.post_title LIKE %s OR p.post_content LIKE %s OR p.post_name LIKE %s ) ", | |
| 460 | + $searchPlaceholder, | |
| 461 | + $searchPlaceholder, | |
| 462 | + $searchPlaceholder | |
| 463 | + ) : ''; | |
| 464 | + | |
| 465 | + $posts = $wpdb->get_results( | |
| 466 | + $wpdb->prepare( | |
| 467 | + "SELECT p.ID, p.post_title, p.post_date, p.post_status, u.display_name as author | |
| 468 | + FROM $wpdb->posts p | |
| 469 | + LEFT JOIN $wpdb->users u ON p.post_author = u.ID | |
| 470 | + WHERE p.post_type = 'post' | |
| 471 | + AND p.post_status IN ('publish', 'draft', 'private') | |
| 472 | + $where_search_clause | |
| 473 | + ORDER BY p.post_date DESC | |
| 474 | + LIMIT %d, %d", | |
| 475 | + $offset, | |
| 476 | + $limit | |
| 477 | + ), | |
| 478 | + OBJECT | |
| 479 | + ); | |
| 480 | + | |
| 481 | + $posts_count = (int)$wpdb->get_var( | |
| 482 | + "SELECT COUNT(*) | |
| 483 | + FROM $wpdb->posts p | |
| 484 | + WHERE p.post_type = 'post' | |
| 485 | + AND p.post_status IN ('publish', 'draft', 'private') | |
| 486 | + $where_search_clause" | |
| 487 | + ); | |
| 488 | + | |
| 489 | + $data = array_map(function($post) { | |
| 490 | + return [ | |
| 491 | + 'id' => $post->ID, | |
| 492 | + 'title' => $post->post_title, | |
| 493 | + 'date' => $post->post_date, | |
| 494 | + 'author' => $post->author, | |
| 495 | + 'status' => $post->post_status | |
| 496 | + ]; | |
| 497 | + }, $posts); | |
| 498 | + | |
| 499 | + return new WP_REST_Response([ | |
| 500 | + 'success' => true, | |
| 501 | + 'data' => $data, | |
| 502 | + 'total' => $posts_count | |
| 503 | + ], 200); | |
| 504 | + } catch (Exception $e) { | |
| 505 | + return new WP_REST_Response(['success' => false, 'message' => $e->getMessage()], 500); | |
| 506 | + } | |
| 507 | + } | |
| 508 | + | |
| 353 | 509 | #endregion |
| 354 | 510 | |
| 355 | 511 | #region AI Engine |
| 356 | 512 | |
| @@ -580,23 +736,13 @@ | ||
| 580 | 736 | $code = $params['code']; |
| 581 | 737 | if ( empty( $name ) || empty( $code ) ) { |
| 582 | 738 | throw new Exception( __( 'Name and code are required.', 'code-engine' ) ); |
| 583 | 739 | } |
| 584 | - $this->snippet->validate( $params ); | |
| 740 | + | |
| 741 | + $res = $this->core->add_snippet( $params ); | |
| 742 | + $snippet = $res['snippet']; | |
| 743 | + $result = $res['result']; | |
| 585 | 744 | |
| 586 | - $params = $this->snippet->formatParamsForDatabase( $params ); | |
| 587 | - $result = $this->snippet->insert( $params ); | |
| 588 | - $snippet = $this->snippet->select_one( $result ); | |
| 589 | - | |
| 590 | - if( $result ) { | |
| 591 | - $params['id'] = (string)$result; | |
| 592 | - | |
| 593 | - $this->snippet->create_or_update_function_snippet( $params ); | |
| 594 | - $this->snippet->create_or_update_interval_snippet( $params ); | |
| 595 | - | |
| 596 | - $this->snippet->get_function_snippets_data( $snippet ); | |
| 597 | - } | |
| 598 | - | |
| 599 | 745 | return new WP_REST_Response([ 'success' => true, 'data' => $result, 'snippet' => $snippet ], 200); |
| 600 | 746 | } catch (Exception $e) { |
| 601 | 747 | return new WP_REST_Response( ['success' => false, 'message' => $e->getMessage() ], 500 ); |
| 602 | 748 | } |
| @@ -666,8 +812,17 @@ | ||
| 666 | 812 | } catch (Exception $e) { |
| 667 | 813 | return new WP_REST_Response( ['success' => false, 'message' => $e->getMessage() ], 500 ); |
| 668 | 814 | } |
| 669 | 815 | } |
| 816 | + | |
| 817 | + public function rest_delete_duplicates() { | |
| 818 | + try { | |
| 819 | + $result = $this->snippet->delete_duplicates(); | |
| 820 | + return new WP_REST_Response([ 'success' => true, 'data' => [ 'deleted' => $result ] ], 200); | |
| 821 | + } catch (Exception $e) { | |
| 822 | + return new WP_REST_Response( ['success' => false, 'message' => $e->getMessage() ], 500 ); | |
| 823 | + } | |
| 824 | + } | |
| 670 | 825 | |
| 671 | 826 | |
| 672 | 827 | #endregion |
| 673 | 828 | |
| @@ -688,9 +843,17 @@ | ||
| 688 | 843 | $params = $request->get_json_params(); |
| 689 | 844 | } |
| 690 | 845 | |
| 691 | 846 | try { |
| 692 | - $output = $this->core->run_snippet( null, [], $params ); | |
| 847 | + $output = "(Code Engine) No output."; | |
| 848 | + $error = null; | |
| 849 | + | |
| 850 | + $scope = $params['scope']; | |
| 851 | + if ( $scope === 'function' ) { | |
| 852 | + $output = $this->core->run_snippet( null, [], $params ); | |
| 853 | + } else { | |
| 854 | + $output = $this->core->run_non_fn_snippet( null, $params['code'], true ); | |
| 855 | + } | |
| 693 | 856 | |
| 694 | 857 | $error = $output['error'] ?? null; |
| 695 | 858 | unset( $output['error'] ); |
| 696 | 859 | |