| @@ -29,8 +29,11 @@ | ||
| 29 | 29 | |
| 30 | 30 | // Create API before plugins_loaded |
| 31 | 31 | $this->mwcode = new Meow_MWCODE_API( $this, $snippet ); |
| 32 | 32 | $mwcode = $this->mwcode; |
| 33 | + | |
| 34 | + // Add the shortcode for the "content" snippets | |
| 35 | + add_shortcode( 'code-engine', [ $this, 'content_shortcode' ] ); | |
| 33 | 36 | |
| 34 | 37 | add_action( 'plugins_loaded', array( $this, 'init' ) ); |
| 35 | 38 | } |
| 36 | 39 | |
| @@ -201,40 +204,110 @@ | ||
| 201 | 204 | |
| 202 | 205 | return $this->snippet->select_one( $id ); |
| 203 | 206 | } |
| 204 | 207 | |
| 205 | - private function sanitize_arg( $name, $value ) { | |
| 208 | + function add_snippet( $params ) { | |
| 209 | + | |
| 210 | + $response = [ | |
| 211 | + "snippet" => null, | |
| 212 | + "result" => false, | |
| 213 | + ]; | |
| 214 | + | |
| 215 | + $this->snippet->validate( $params ); | |
| 216 | + | |
| 217 | + $params = $this->snippet->formatParamsForDatabase( $params ); | |
| 218 | + $result = $this->snippet->insert( $params ); | |
| 219 | + $snippet = $this->snippet->select_one( $result ); | |
| 220 | + | |
| 221 | + if( $result ) { | |
| 222 | + $params['id'] = (string)$result; | |
| 223 | + | |
| 224 | + $this->snippet->create_or_update_function_snippet( $params ); | |
| 225 | + $this->snippet->create_or_update_interval_snippet( $params ); | |
| 226 | + | |
| 227 | + $this->snippet->get_function_snippets_data( $snippet ); | |
| 228 | + } | |
| 229 | + | |
| 230 | + $response['snippet'] = $snippet; | |
| 231 | + $response['result'] = $result; | |
| 232 | + | |
| 233 | + return $response; | |
| 234 | + } | |
| 235 | + | |
| 236 | + private function sanitize_arg( $name, $value, $type = null) { | |
| 237 | + $real_type = gettype( $value ); | |
| 238 | + | |
| 206 | 239 | if ( $name[0] !== '$' ) { $name = '$' . $name; } |
| 240 | + | |
| 241 | + if ( $type == null ) { | |
| 242 | + $type = $real_type; | |
| 243 | + } | |
| 207 | 244 | |
| 208 | - if ( !empty( $value ) && !is_numeric( $value ) && $value[0] !== '"' && $value[strlen( $value ) - 1] !== '"' ) { | |
| 245 | + if ( $type != 'array' && !empty( $value ) && !is_numeric( $value ) && $value[0] !== '"' && $value[strlen( $value ) - 1] !== '"' ) { | |
| 209 | 246 | $value = '"' . esc_sql( $value ) . '"'; |
| 210 | 247 | } |
| 211 | 248 | |
| 249 | + if ( $type === 'array' && $real_type === 'string' ) { | |
| 250 | + // We got a string like this: "["a", "b", "c"]" or "[ 1, 2, 3 ]" | |
| 251 | + // We need to convert it to an array | |
| 252 | + $value = str_replace( '"', '', $value ); | |
| 253 | + $value = str_replace( '[', '', $value ); | |
| 254 | + $value = str_replace( ']', '', $value ); | |
| 255 | + $value = explode( ',', $value ); | |
| 256 | + $value = array_map( 'trim', $value ); | |
| 257 | + } | |
| 258 | + | |
| 259 | + if ( $type === 'array' ) { | |
| 260 | + $value = json_encode( $value ); | |
| 261 | + $value = str_replace( '\\', '', $value ); | |
| 262 | + } | |
| 263 | + | |
| 212 | 264 | return [ $name, $value ]; |
| 213 | 265 | } |
| 214 | 266 | |
| 215 | - function run_non_fn_snippet( $id ) { | |
| 216 | - $snippet = $this->get_snippet( $id ); | |
| 267 | + function run_non_fn_snippet( $id, $code = null, $test = false ) { | |
| 268 | + // Retrieve the snippet code from the provided code or via the snippet ID. | |
| 269 | + if ( $code ) { | |
| 270 | + $snippet = [ 'code' => $code ]; | |
| 271 | + } else { | |
| 272 | + $snippet = $this->get_snippet( $id ); | |
| 273 | + } | |
| 274 | + | |
| 275 | + // Remove any PHP opening tag. | |
| 217 | 276 | $snippet['code'] = preg_replace( '/<\?php/', '', $snippet['code'], 1 ); |
| 277 | + | |
| 218 | 278 | |
| 219 | - $error = null; | |
| 279 | + if ( $test ) { | |
| 280 | + $snippet['code'] = preg_replace( '/echo\s+(.+?);/s', 'echo $1 . "\n";', $snippet['code'] ); | |
| 281 | + } | |
| 282 | + | |
| 283 | + $error = null; | |
| 220 | 284 | $output = null; |
| 221 | - | |
| 285 | + | |
| 222 | 286 | try { |
| 223 | 287 | ob_start(); |
| 224 | 288 | eval( $snippet['code'] ); |
| 225 | 289 | $output = ob_get_clean(); |
| 226 | 290 | } catch ( Throwable $e ) { |
| 227 | - $error = new Exception( ' Error executing the snippet, ' . $e->getMessage() ); | |
| 291 | + $snippet_id = $id ? " ( ID: $id )" : '(Content Gutenberg Block)'; | |
| 292 | + $this->log( '🔴 Error executing the snippet ' . $snippet_id . ' : ' . $e->getMessage() ); | |
| 228 | 293 | ob_clean(); |
| 229 | 294 | } finally { |
| 230 | 295 | restore_error_handler(); |
| 231 | 296 | } |
| 232 | - | |
| 233 | - if ( $error !== null ) { | |
| 234 | - throw $error; | |
| 297 | + | |
| 298 | + // If in test mode, return output as an array of lines with an 'error' key if needed. | |
| 299 | + if ( $test ) { | |
| 300 | + $output = explode( "\n", trim( $output ) ); | |
| 301 | + if ( $error !== null ) { | |
| 302 | + $output['error'] = $error->getMessage(); | |
| 303 | + } | |
| 304 | + } else { | |
| 305 | + if ( $error !== null ) { | |
| 306 | + throw $error; | |
| 307 | + } | |
| 235 | 308 | } |
| 236 | - | |
| 309 | + | |
| 237 | 310 | return $output; |
| 238 | 311 | } |
| 239 | 312 | |
| 240 | 313 | function run_snippet( $id, $args = [], $params = [] ) |
| @@ -260,9 +333,9 @@ | ||
| 260 | 333 | if ( $this->get_option( 'sanitize_arguments', true ) ) { |
| 261 | 334 | |
| 262 | 335 | if ( $args ) { |
| 263 | 336 | foreach ( $args as $name => $value ) { |
| 264 | - list( $sanitizedName, $sanitizedValue ) = $this->sanitize_arg( $name, $value ); | |
| 337 | + list( $sanitizedName, $sanitizedValue ) = $this->sanitize_arg( $name, $value, $value['type'] ); | |
| 265 | 338 | unset( $args[$name] ); |
| 266 | 339 | |
| 267 | 340 | $args[$sanitizedName] = $sanitizedValue; |
| 268 | 341 | } |
| @@ -270,14 +343,14 @@ | ||
| 270 | 343 | |
| 271 | 344 | foreach ( $params['values'] as $name => $value ) { |
| 272 | 345 | |
| 273 | 346 | if( array_key_exists( 'input', $value) ) { |
| 274 | - list( $sanitizedInputName, $sanitizedInputValue ) = $this->sanitize_arg( $name, $value['input'] ); | |
| 347 | + list( $sanitizedInputName, $sanitizedInputValue ) = $this->sanitize_arg( $name, $value['input'], $value['type'] ); | |
| 275 | 348 | $params['values'][$sanitizedInputName]['input'] = $sanitizedInputValue; |
| 276 | 349 | } |
| 277 | 350 | |
| 278 | 351 | if( array_key_exists( 'default', $value) ) { |
| 279 | - list( $sanitizedDefaultValueName, $sanitizedDefaultValue ) = $this->sanitize_arg( $name, $value['default'] ); | |
| 352 | + list( $sanitizedDefaultValueName, $sanitizedDefaultValue ) = $this->sanitize_arg( $name, $value['default'], $value['type'] ); | |
| 280 | 353 | $params['values'][$sanitizedDefaultValueName]['default'] = $sanitizedDefaultValue; |
| 281 | 354 | } |
| 282 | 355 | } |
| 283 | 356 | |
| @@ -282,10 +355,8 @@ | ||
| 282 | 355 | } |
| 283 | 356 | |
| 284 | 357 | } |
| 285 | 358 | |
| 286 | - | |
| 287 | - | |
| 288 | 359 | // Make sure the function is existing and is the one in the snippet |
| 289 | 360 | if ( empty( $params['code'] ) ) { |
| 290 | 361 | throw new Exception( 'Code Engine: The snippet code appears to be empty.' ); |
| 291 | 362 | } |
| @@ -541,8 +612,79 @@ | ||
| 541 | 612 | |
| 542 | 613 | return $snippets; |
| 543 | 614 | } |
| 544 | 615 | |
| 616 | + | |
| 617 | + #endregion | |
| 618 | + | |
| 619 | + #reion Shortcodes | |
| 620 | + | |
| 621 | + function content_shortcode( $atts ) { | |
| 622 | + | |
| 623 | + $atts = shortcode_atts( array( | |
| 624 | + 'id' => null, | |
| 625 | + 'target' => null, | |
| 626 | + 'code' => null, | |
| 627 | + ), $atts ); | |
| 628 | + | |
| 629 | + $id = $atts['id']; | |
| 630 | + $target = $atts['target']; | |
| 631 | + $code = $atts['code']; | |
| 632 | + | |
| 633 | + // If the ID is null, it means it comes from a Guttenberg block | |
| 634 | + $is_block = empty( $id ) && !empty( $code ); | |
| 635 | + if( $is_block ){ | |
| 636 | + | |
| 637 | + // Because the code from Blocks are sanitized, we need to replace the " with " | |
| 638 | + $code = str_replace( '"', '"', $code ); | |
| 639 | + | |
| 640 | + if ( $target === 'js' ) { | |
| 641 | + $output = '<script>' . $code . '</script>'; | |
| 642 | + } | |
| 643 | + | |
| 644 | + if ( $target === 'php' ) { | |
| 645 | + $output = $this->run_non_fn_snippet( null, $code ); | |
| 646 | + } | |
| 647 | + | |
| 648 | + return $output; | |
| 649 | + } | |
| 650 | + | |
| 651 | + // If the ID is not null, it means it comes from a shortcode | |
| 652 | + if ( empty( $id ) && empty( $code ) ) { | |
| 653 | + return '<b>Code Engine:</b> Please provide a snippet ID.'; | |
| 654 | + } | |
| 655 | + | |
| 656 | + $snippet = $this->get_snippet( $id ); | |
| 657 | + | |
| 658 | + if ( empty( $snippet ) ) { | |
| 659 | + return '<b>Code Engine:</b> The snippet does not exist.'; | |
| 660 | + } | |
| 661 | + | |
| 662 | + //Check if the snippet scope is either content_php or content_js | |
| 663 | + $is_content_php = $snippet['scope'] === 'content_php'; | |
| 664 | + $is_content_js = $snippet['scope'] === 'content_js'; | |
| 665 | + | |
| 666 | + if ( !$is_content_php && !$is_content_js ) { | |
| 667 | + return '<b>Code Engine:</b> The snippet is not a content snippet.'; | |
| 668 | + } | |
| 669 | + | |
| 670 | + //Check if the snippet is active | |
| 671 | + if ( !$snippet['active'] ) { | |
| 672 | + return '<b>Code Engine:</b> The snippet is not active.'; | |
| 673 | + } | |
| 674 | + | |
| 675 | + $output = '<b>Code Engine:</b> No output.'; | |
| 676 | + | |
| 677 | + if ( $is_content_js ) { | |
| 678 | + $output = '<script>' . $snippet['code'] . '</script>'; | |
| 679 | + } | |
| 680 | + | |
| 681 | + if ( $is_content_php ) { | |
| 682 | + $output = $this->run_non_fn_snippet( $id ); | |
| 683 | + } | |
| 684 | + | |
| 685 | + return $output; | |
| 686 | + } | |
| 545 | 687 | |
| 546 | 688 | #endregion |
| 547 | 689 | |
| 548 | 690 | #region Logs |