$id, 'name' => substr( sanitize_text_field( $r['name'] ?? 'Rule' ), 0, 80 ), 'post_type' => sanitize_key( $r['post_type'] ?? 'product' ), 'column' => sanitize_text_field( $r['column'] ), 'op' => in_array( $r['op'] ?? '', array( 'eq', 'lt', 'gt', 'empty', 'contains' ), true ) ? $r['op'] : 'eq', 'value' => sanitize_text_field( (string) ( $r['value'] ?? '' ) ), 'action_column' => sanitize_text_field( $r['action_column'] ?? '' ), 'action_value' => sanitize_text_field( (string) ( $r['action_value'] ?? '' ) ), 'notify' => ! empty( $r['notify'] ) ? 1 : 0, 'enabled' => ! isset( $r['enabled'] ) || ! empty( $r['enabled'] ) ? 1 : 0, 'user_id' => $owners[ $id ] ?? get_current_user_id(), ); } update_option( 'rafgrid_rules', $clean ); return $clean; } /** * Hourly cron: every enabled rule runs as its owner. */ public static function run_all() { foreach ( self::all() as $rule ) { if ( empty( $rule['enabled'] ) ) { continue; } try { $res = Access::as_admin( (int) ( $rule['user_id'] ?? 0 ), static fn() => self::run_rule( $rule ) ); if ( is_wp_error( $res ) ) { error_log( '[rafgrid] rule "' . $rule['name'] . '" skipped: ' . $res->get_error_message() ); // phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_error_log } } catch ( \Throwable $e ) { error_log( '[rafgrid] rule failed: ' . $e->getMessage() ); // phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_error_log } } } /** * "Run all now" from the sheet: runs as the current administrator. */ public static function run_all_now() { foreach ( self::all() as $rule ) { if ( ! empty( $rule['enabled'] ) ) { self::run_rule( $rule ); } } } /** * Evaluate one rule as the current user (an administrator; see run_all). * * @param array $rule Rule. * @return array {matched, changed} */ public static function run_rule( array $rule ): array { if ( ! current_user_can( 'manage_options' ) ) { return array( 'matched' => 0, 'changed' => 0 ); } $ids = get_posts( array( 'post_type' => $rule['post_type'], 'posts_per_page' => 500, 'post_status' => 'any', 'fields' => 'ids', ) ); $matched = 0; $changed = 0; foreach ( $ids as $id ) { $current = Audit::stringify( Columns::get_value( (int) $id, $rule['column'] ) ); $hit = match ( $rule['op'] ) { 'lt' => is_numeric( $current ) && (float) $current < (float) $rule['value'], 'gt' => is_numeric( $current ) && (float) $current > (float) $rule['value'], 'empty' => '' === $current, 'contains' => str_contains( $current, $rule['value'] ), default => $current === $rule['value'], }; if ( ! $hit ) { continue; } ++$matched; if ( '' !== $rule['action_column'] ) { $res = Saving::save_cell( (int) $id, $rule['action_column'], $rule['action_value'] ); if ( ! is_wp_error( $res ) ) { ++$changed; } } } if ( $rule['notify'] && $matched > 0 ) { $settings = new Settings(); $webhook = (string) $settings->get( 'webhook_url', '' ); $text = 'Rafgrid rule "' . $rule['name'] . '": matched=' . $matched . ' changed=' . $changed; if ( '' !== $webhook ) { wp_remote_post( $webhook, array( 'blocking' => false, 'headers' => array( 'Content-Type' => 'application/json' ), 'body' => wp_json_encode( array( 'text' => $text ) ) ) ); } else { wp_mail( get_option( 'admin_email' ), __( 'Rafgrid automation', 'rafgrid' ), $text ); } } return array( 'matched' => $matched, 'changed' => $changed ); } }