bulk_action( $action_id ); if ( ! $action ) { wp_send_json_error( array( 'message' => __( 'This report does not offer that bulk action.', 'ai-eshop-optimizer' ) ), 400 ); } $is_set = ! empty( $action['set_column'] ); $cap = (string) $action['capability']; $may = ( '' !== $cap ) ? current_user_can( $cap ) : $def->user_can( $is_set ? 'edit' : 'execute' ); if ( ! $may ) { wp_send_json_error( array( 'message' => __( 'You do not have permission to run this action.', 'ai-eshop-optimizer' ) ), 403 ); } $param_value = null; $param_spec = isset( $action['param'] ) && is_array( $action['param'] ) ? $action['param'] : array(); if ( $param_spec ) { $given = ( null !== $raw_value ) ? (string) $raw_value : ''; if ( '' === $given ) { if ( ! empty( $param_spec['required'] ) ) { wp_send_json_error( array( 'message' => __( 'Pick a value first.', 'ai-eshop-optimizer' ) ), 400 ); } } else { switch ( (string) $param_spec['type'] ) { case 'term': $tid = absint( $given ); if ( ! $tid || ! term_exists( $tid, (string) $param_spec['taxonomy'] ) ) { wp_send_json_error( array( 'message' => __( 'That is not one of the taxonomy’s terms.', 'ai-eshop-optimizer' ) ), 400 ); } $param_value = $tid; break; case 'select': $keys = array_map( 'strval', array_keys( (array) $param_spec['options'] ) ); if ( ! in_array( $given, $keys, true ) ) { wp_send_json_error( array( 'message' => __( 'That is not one of the offered choices.', 'ai-eshop-optimizer' ) ), 400 ); } $param_value = $given; break; case 'number': $n = str_replace( ',', '.', $given ); if ( ! is_numeric( $n ) ) { wp_send_json_error( array( 'message' => __( 'That value is not a number.', 'ai-eshop-optimizer' ) ), 400 ); } $param_value = ( false === strpos( $n, '.' ) ) ? (int) $n : (float) $n; break; default: $param_value = $given; break; } } } if ( 'filter' === $scope && 'filter' !== (string) $action['scope'] ) { wp_send_json_error( array( 'message' => __( 'This action applies to selected rows only.', 'ai-eshop-optimizer' ) ), 400 ); } $done = true; $next = ''; $remaining = 0; if ( 'filter' === $scope ) { $query = new AIEO_Report_Query( $def, array( 'params' => $params, 'search' => $search ) ); $keys = $query->row_keys( $after, self::BULK_CHUNK ); if ( null === $keys ) { wp_send_json_error( array( 'message' => __( 'That report could not be run.', 'ai-eshop-optimizer' ) ), 500 ); } $ids = $keys; $done = ( count( $keys ) < self::BULK_CHUNK ); $next = $ids ? (string) end( $ids ) : $after; if ( ! $done ) { $remaining = max( 0, $query->count_filtered() - count( $keys ) ); } } else { $ids = array_values( array_unique( array_filter( $ids, 'strlen' ) ) ); if ( count( $ids ) > self::BULK_CHUNK ) { $ids = array_slice( $ids, 0, self::BULK_CHUNK ); } } if ( ! $ids ) { if ( 'filter' === $scope ) { wp_send_json_success( array( 'updated' => 0, 'skipped' => 0, 'done' => true, 'after' => $next, 'remaining' => 0, 'message' => '', ) ); } wp_send_json_error( array( 'message' => __( 'Select at least one row first.', 'ai-eshop-optimizer' ) ), 400 ); } if ( $is_set ) { $column = (string) $action['set_column']; $updated = 0; $skipped = 0; $why = ''; foreach ( $ids as $one ) { $saved = AIEO_Report_Edit::save( $def->id(), (string) $one, $column, $param_value ); if ( ! empty( $saved['ok'] ) ) { $updated++; } else { $skipped++; $why = isset( $saved['message'] ) ? (string) $saved['message'] : $why; } } if ( 0 === $updated && '' !== $why ) { wp_send_json_error( array( 'message' => $why ), 400 ); } wp_send_json_success( array( 'updated' => $updated, 'skipped' => $skipped, 'message' => '', 'done' => true, 'after' => '', 'remaining' => 0, ) ); } $result = apply_filters( 'aieo_report_bulk', false, array( 'report' => $def->id(), 'action' => (string) $action['id'], 'scope' => $scope, 'ids' => $ids, 'params' => $params, 'search' => $search, 'param' => $param_value, ), $def ); if ( is_string( $result ) && '' !== $result ) { wp_send_json_error( array( 'message' => $result ), 400 ); } if ( ! is_array( $result ) || ! isset( $result['updated'] ) ) { wp_send_json_error( array( 'message' => __( 'No screen handles this bulk action yet.', 'ai-eshop-optimizer' ) ), 501 ); } wp_send_json_success( array( 'updated' => (int) $result['updated'], 'skipped' => isset( $result['skipped'] ) ? (int) $result['skipped'] : 0, 'message' => isset( $result['message'] ) ? (string) $result['message'] : '', 'done' => $done, 'after' => $next, 'remaining' => $remaining, ) ); } public static function export() { $id = isset( $_GET['report'] ) ? sanitize_key( wp_unslash( $_GET['report'] ) ) : ''; if ( ! isset( $_GET['nonce'] ) || ! wp_verify_nonce( sanitize_text_field( wp_unslash( $_GET['nonce'] ) ), AIEO_DMM::NONCE_ACTION ) ) { wp_die( esc_html__( 'That download link has expired. Reload the report and try again.', 'ai-eshop-optimizer' ), 403 ); } $def = $id ? AIEO_Report_Registry::get( $id ) : null; if ( ! $def ) { wp_die( esc_html__( 'No such report.', 'ai-eshop-optimizer' ), 404 ); } $params = isset( $_GET['params'] ) ? self::sanitise_params( wp_unslash( $_GET['params'] ) ) : array(); $request = array( 'sort' => isset( $_GET['sort'] ) ? self::column_key( wp_unslash( $_GET['sort'] ) ) : '', 'dir' => isset( $_GET['dir'] ) ? sanitize_key( wp_unslash( $_GET['dir'] ) ) : '', 'search' => isset( $_GET['search'] ) ? sanitize_text_field( wp_unslash( $_GET['search'] ) ) : '', 'params' => $params, ); $source = array_merge( $_GET, $_POST ); $request = AIEO_Report_Render::export_request( $def, $request, $source ); if ( ! class_exists( '\Rooster\Admin\Export' ) && function_exists( 'rooster_admin_builders' ) ) { rooster_admin_builders(); } if ( ! class_exists( '\Rooster\Admin\Export' ) ) { wp_die( esc_html__( 'That report could not be exported.', 'ai-eshop-optimizer' ), 500 ); } $policy = AIEO_Report_Export::policy( $def ); $asked = \Rooster\Admin\Export::request( AIEO_Report_Export::spec( AIEO_Report_Export::columns( $def, $policy ), $policy ), array(), array( 'source' => wp_unslash( $source ), 'prefix' => AIEO_Report_Render::prefix( $def->id() ), ) ); if ( $asked['ids'] ) { $request['keys'] = $asked['ids']; } $request['columns'] = array_keys( $asked['columns'] ); $result = AIEO_Report_Export::stream( $def->id(), $request, $asked['format'] ); if ( is_array( $result ) && isset( $result['error'] ) ) { if ( 'forbidden' === $result['error'] ) { wp_die( esc_html__( 'You do not have permission to export this report.', 'ai-eshop-optimizer' ), 403 ); } wp_die( esc_html__( 'That report could not be exported.', 'ai-eshop-optimizer' ), 500 ); } exit; } public static function save_cell() { AIEO_DMM::verify_ajax_request(); $report = isset( $_POST['report'] ) ? sanitize_key( wp_unslash( $_POST['report'] ) ) : ''; $row = isset( $_POST['row'] ) ? sanitize_text_field( wp_unslash( $_POST['row'] ) ) : ''; $field = isset( $_POST['field'] ) ? self::column_key( wp_unslash( $_POST['field'] ) ) : ''; $value = isset( $_POST['value'] ) ? wp_unslash( $_POST['value'] ) : ''; $library = '' === $field && isset( $_POST['key'], $_POST['context'] ); if ( $library ) { $field = self::column_key( wp_unslash( $_POST['key'] ) ); $report = isset( $_REQUEST['report'] ) ? sanitize_key( wp_unslash( $_REQUEST['report'] ) ) : $report; } $result = AIEO_Report_Edit::save( $report, $row, $field, $value ); if ( $library ) { if ( empty( $result['ok'] ) ) { wp_send_json( array( 'message' => (string) $result['message'] ), 403 ); } $saved = isset( $result['value'] ) && is_scalar( $result['value'] ) ? (string) $result['value'] : (string) $value; wp_send_json( array( 'value' => $saved, 'html' => esc_html( $saved ), ) ); } if ( empty( $result['ok'] ) ) { wp_send_json_error( array( 'message' => $result['message'] ), 403 ); } wp_send_json_success( $result ); } public static function fetch() { $def = self::gate(); if ( self::is_library() ) { self::fetch_library( $def ); } $params = array(); if ( isset( $_POST['params'] ) ) { $params = self::sanitise_params( wp_unslash( $_POST['params'] ) ); } $request = array( 'page' => isset( $_POST['page'] ) ? absint( $_POST['page'] ) : 1, 'per_page' => isset( $_POST['per_page'] ) ? absint( $_POST['per_page'] ) : 0, 'sort' => isset( $_POST['sort'] ) ? self::column_key( wp_unslash( $_POST['sort'] ) ) : '', 'dir' => isset( $_POST['dir'] ) ? sanitize_key( wp_unslash( $_POST['dir'] ) ) : '', 'search' => isset( $_POST['search'] ) ? sanitize_text_field( wp_unslash( $_POST['search'] ) ) : '', 'params' => $params, ); if ( ! empty( $_POST['remember'] ) && $request['per_page'] > 0 && class_exists( 'AIEO_Table_Registry' ) ) { $rid = (string) $def->get( 'registry_id', '' ); if ( '' !== $rid ) { AIEO_Table_Registry::set_user_per_page( $rid, $request['per_page'] ); } } $envelope = AIEO_Report_Render::run( $def->id(), $request ); if ( isset( $envelope['error'] ) ) { wp_send_json_error( array( 'message' => __( 'That report could not be run.', 'ai-eshop-optimizer' ), 'code' => (string) $envelope['error'], ), 500 ); } $envelope['empty_state'] = class_exists( 'AIEO_Report_Render' ) ? AIEO_Report_Render::empty_text( $def ) : self::translate( (string) $def->get( 'empty_state', '' ) ); $envelope['label'] = self::translate( (string) $def->get( 'label', '' ) ); unset( $envelope['rows_assoc'] ); wp_send_json_success( $envelope ); } public static function filter_options() { $def = self::gate(); if ( self::is_library() ) { self::filter_options_library( $def ); } $column = isset( $_POST['column'] ) ? self::column_key( wp_unslash( $_POST['column'] ) ) : ''; if ( '' === $column ) { wp_send_json_error( array( 'message' => __( 'No column given.', 'ai-eshop-optimizer' ) ), 400 ); } $params = isset( $_POST['params'] ) ? self::sanitise_params( wp_unslash( $_POST['params'] ) ) : array(); $query = new AIEO_Report_Query( $def, array( 'params' => $params ) ); wp_send_json_success( $query->filter_options( $column ) ); } private static function gate() { $library = self::is_library(); if ( $library ) { if ( ! check_ajax_referer( AIEO_DMM::NONCE_ACTION, '_wpnonce', false ) ) { self::refuse( __( 'That page has expired. Reload it and try again.', 'ai-eshop-optimizer' ), 403, true ); } if ( ! current_user_can( AIEO_CAPABILITY ) ) { self::refuse( __( 'Insufficient permissions.', 'ai-eshop-optimizer' ), 403, true ); } } else { AIEO_DMM::verify_ajax_request(); } $id = isset( $_REQUEST['report'] ) ? sanitize_key( wp_unslash( $_REQUEST['report'] ) ) : ''; $def = $id ? AIEO_Report_Registry::get( $id ) : null; if ( ! $def ) { self::refuse( __( 'No such report.', 'ai-eshop-optimizer' ), 404, $library ); } if ( ! $def->user_can( 'view' ) ) { self::refuse( __( 'You do not have permission to view this report.', 'ai-eshop-optimizer' ), 403, $library, 'capability' ); } if ( 'pro' === AIEO_Report_Registry::tier( $id ) && ! self::is_pro() ) { self::refuse( __( 'This report is part of Rooster One Pro.', 'ai-eshop-optimizer' ), 403, $library, 'tier' ); } return $def; } private static function is_library() { return isset( $_GET['context'] ) && in_array( sanitize_key( wp_unslash( $_GET['context'] ) ), array( 'admin', 'storefront' ), true ); } private static function refuse( $message, $status, $library, $code = '' ) { if ( $library ) { wp_send_json( array( 'message' => (string) $message ), (int) $status ); } wp_send_json_error( array_filter( array( 'message' => (string) $message, 'code' => (string) $code, ), 'strlen' ), (int) $status ); } private static function fetch_library( $def ) { $params = isset( $_GET['params'] ) ? self::sanitise_params( wp_unslash( $_GET['params'] ) ) : array(); $request = array( 'page' => isset( $_GET['page'] ) ? absint( $_GET['page'] ) : 1, 'per_page' => isset( $_GET['per_page'] ) ? absint( $_GET['per_page'] ) : 0, 'sort' => isset( $_GET['orderby'] ) ? self::column_key( wp_unslash( $_GET['orderby'] ) ) : '', 'dir' => isset( $_GET['order'] ) ? sanitize_key( wp_unslash( $_GET['order'] ) ) : '', 'search' => isset( $_GET['search'] ) ? sanitize_text_field( wp_unslash( $_GET['search'] ) ) : '', 'params' => AIEO_Report_Render::query_params( $def, $params ), ); $rid = (string) $def->get( 'registry_id', '' ); if ( $request['per_page'] > 0 && '' !== $rid && class_exists( 'AIEO_Table_Registry' ) ) { $usual = AIEO_Report_Query::normalise_request( array(), $def ); if ( (int) $usual['per_page'] !== $request['per_page'] ) { AIEO_Table_Registry::set_user_per_page( $rid, $request['per_page'] ); } } $answer = AIEO_Report_Render::answer( $def->id(), $request ); if ( isset( $answer['error'] ) ) { self::refuse( __( 'That report could not be run.', 'ai-eshop-optimizer' ), 500, true ); } wp_send_json( $answer ); } private static function filter_options_library( $def ) { $params = AIEO_Report_Render::query_params( $def, isset( $_GET['params'] ) ? self::sanitise_params( wp_unslash( $_GET['params'] ) ) : array() ); $search = isset( $_GET['search'] ) ? sanitize_text_field( wp_unslash( $_GET['search'] ) ) : ''; $out = array(); foreach ( AIEO_Report_Render::lazy_filters( $def ) as $key => $labels ) { $found = ( new AIEO_Report_Query( $def, array( 'params' => $params ) ) )->filter_options( $key ); $counts = AIEO_Report_Render::counts_choices( $def, $key ) ? AIEO_Report_Render::choice_counts( $def, $key, $params, $search ) : null; $list = array(); foreach ( (array) ( $found['options'] ?? array() ) as $value ) { $pair = array( (string) $value, isset( $labels[ (string) $value ] ) ? (string) $labels[ (string) $value ] : (string) $value ); if ( null !== $counts ) { $pair[] = (int) ( $counts[ (string) $value ] ?? 0 ); } $list[] = $pair; } $out[ $key ] = $list; } wp_send_json( (object) $out ); } private static function is_pro() { if ( class_exists( 'AIEO_DMM_Module_Base' ) && method_exists( 'AIEO_DMM_Module_Base', 'is_aieo_pro_user' ) ) { return (bool) AIEO_DMM_Module_Base::is_aieo_pro_user(); } return false; } private static function translate( $text ) { if ( '' === $text ) { return ''; } return __( $text, 'ai-eshop-optimizer' ); } private static function column_key( $key ) { return (string) preg_replace( '/[^a-zA-Z0-9_]/', '', (string) $key ); } private static function sanitise_params( $params ) { if ( ! is_array( $params ) ) { return array(); } $out = array(); foreach ( $params as $key => $value ) { $key = self::column_key( (string) $key ); if ( '' === $key ) { continue; } if ( is_array( $value ) ) { $inner = array(); foreach ( $value as $k2 => $v2 ) { if ( is_array( $v2 ) ) { continue; } $inner[ self::column_key( (string) $k2 ) ] = sanitize_text_field( (string) $v2 ); } $out[ $key ] = $inner; continue; } $out[ $key ] = sanitize_text_field( (string) $value ); } return $out; } }