); if ( post_type_supports( $this->post_type, 'revisions' ) ) { $revisions = wp_get_latest_revision_id_and_total_count( $id ); $revisions_count = ! is_wp_error( $revisions ) ? $revisions['count'] : 0; $revisions_base = sprintf( '/%s/%d/revisions', $base, $id ); $links['version-history'] = array( 'href' => rest_url( $revisions_base ), 'count' => $revisions_count, ); } return $links; } /** * Get the link relations available for the post and current user. * * @since 5.9.0 * @since 6.2.0 Added 'edit-css' action. * @since 6.6.0 Added $post and $request parameters. * * @param WP_Post $post Post object. * @param WP_REST_Request $request Request object. * @return array List of link relations. */ protected function get_available_actions( $post, $request ) { $rels = array(); $post_type = get_post_type_object( $post->post_type ); if ( current_user_can( $post_type->cap->publish_posts ) ) { $rels[] = 'https://api.w.org/action-publish'; } if ( current_user_can( 'edit_css' ) ) { $rels[] = 'https://api.w.org/action-edit-css'; } return $rels; } /** * Retrieves the query params for the global styles collection. * * @since 5.9.0 * * @return array Collection parameters. */ public function get_collection_params() { return array(); } /** * Retrieves the global styles type' schema, conforming to JSON Schema. * * @since 5.9.0 * * @return array Item schema data. */ public function get_item_schema() { if ( $this->schema ) { return $this->add_additional_fields_schema( $this->schema ); } $schema = array( '$schema' => 'http://json-schema.org/draft-04/schema#', 'title' => $this->post_type, 'type' => 'object', 'properties' => array( 'id' => array( 'description' => __( 'ID of global styles config.' ), 'type' => 'string', 'context' => array( 'embed', 'view', 'edit' ), 'readonly' => true, ), 'styles' => array( 'description' => __( 'Global styles.' ), 'type' => array( 'object' ), 'context' => array( 'view', 'edit' ), ), 'settings' => array( 'description' => __( 'Global settings.' ), 'type' => array( 'object' ), 'context' => array( 'view', 'edit' ), ), 'title' => array( 'description' => __( 'Title of the global styles variation.' ), 'type' => array( 'object', 'string' ), 'default' => '', 'context' => array( 'embed', 'view', 'edit' ), 'properties' => array( 'raw' => array( 'description' => __( 'Title for the global styles variation, as it exists in the database.' ), 'type' => 'string', 'context' => array( 'view', 'edit', 'embed' ), ), 'rendered' => array( 'description' => __( 'HTML title for the post, transformed for display.' ), 'type' => 'string', 'context' => array( 'view', 'edit', 'embed' ), 'readonly' => true, ), ), ), ), ); $this->schema = $schema; return $this->add_additional_fields_schema( $this->schema ); } /** * Checks if a given request has access to read a single theme global styles config. * * @since 5.9.0 * * @param WP_REST_Request $request Full details about the request. * @return true|WP_Error True if the request has read access for the item, WP_Error object otherwise. */ public function get_theme_item_permissions_check( $request ) { /* * Verify if the current user has edit_theme_options capability. * This capability is required to edit/view/delete templates. */ if ( ! current_user_can( 'edit_theme_options' ) ) { return new WP_Error( 'rest_cannot_manage_global_styles', __( 'Sorry, you are not allowed to access the global styles on this site.' ), array( 'status' => rest_authorization_required_code(), ) ); } return true; } /** * Returns the given theme global styles config. * * @since 5.9.0 * @since 6.6.0 Added custom relative theme file URIs to `_links`. * * @param WP_REST_Request $request The request instance. * @return WP_REST_Response|WP_Error */ public function get_theme_item( $request ) { if ( get_stylesheet() !== $request['stylesheet'] ) { // This endpoint only supports the active theme for now. return new WP_Error( 'rest_theme_not_found', __( 'Theme not found.' ), array( 'status' => 404 ) ); } $theme = WP_Theme_JSON_Resolver::get_merged_data( 'theme' ); $fields = $this->get_fields_for_response( $request ); $data = array(); if ( rest_is_field_included( 'settings', $fields ) ) { $data['settings'] = $theme->get_settings(); } if ( rest_is_field_included( 'styles', $fields ) ) { $raw_data = $theme->get_raw_data(); $data['styles'] = isset( $raw_data['styles'] ) ? $raw_data['styles'] : array(); } $context = ! empty( $request['context'] ) ? $request['context'] : 'view'; $data = $this->add_additional_fields_to_object( $data, $request ); $data = $this->filter_response_by_context( $data, $context ); $response = rest_ensure_response( $data ); if ( rest_is_field_included( '_links', $fields ) || rest_is_field_included( '_embedded', $fields ) ) { $links = array( 'self' => array( 'href' => rest_url( sprintf( '%s/%s/themes/%s', $this->namespace, $this->rest_base, $request['stylesheet'] ) ), ), ); $resolved_theme_uris = WP_Theme_JSON_Resolver::get_resolved_theme_uris( $theme ); if ( ! empty( $resolved_theme_uris ) ) { $links['https://api.w.org/theme-file'] = $resolved_theme_uris; } $response->add_links( $links ); } return $response; } /** * Checks if a given request has access to read a single theme global styles config. * * @since 6.0.0 * * @param WP_REST_Request $request Full details about the request. * @return true|WP_Error True if the request has read access for the item, WP_Error object otherwise. */ public function get_theme_items_permissions_check( $request ) { /* * Verify if the current user has edit_theme_options capability. * This capability is required to edit/view/delete templates. */ if ( ! current_user_can( 'edit_theme_options' ) ) { return new WP_Error( 'rest_cannot_manage_global_styles', __( 'Sorry, you are not allowed to access the global styles on this site.' ), array( 'status' => rest_authorization_required_code(), ) ); } return true; } /** * Returns the given theme global styles variations. * * @since 6.0.0 * @since 6.2.0 Returns parent theme variations, if they exist. * @since 6.6.0 Added custom relative theme file URIs to `_links` for each item. * * @param WP_REST_Request $request The request instance. * * @return WP_REST_Response|WP_Error */ public function get_theme_items( $request ) { if ( get_stylesheet() !== $request['stylesheet'] ) { // This endpoint only supports the active theme for now. return new WP_Error( 'rest_theme_not_found', __( 'Theme not found.' ), array( 'status' => 404 ) ); } $response = array(); // Register theme-defined variations e.g. from block style variation partials under `/styles`. $partials = WP_Theme_JSON_Resolver::get_style_variations( 'block' ); wp_register_block_style_variations_from_theme_json_partials( $partials ); $variations = WP_Theme_JSON_Resolver::get_style_variations(); foreach ( $variations as $variation ) { $variation_theme_json = new WP_Theme_JSON( $variation ); $resolved_theme_uris = WP_Theme_JSON_Resolver::get_resolved_theme_uris( $variation_theme_json ); $data = rest_ensure_response( $variation ); if ( ! empty( $resolved_theme_uris ) ) { $data->add_links( array( 'https://api.w.org/theme-file' => $resolved_theme_uris, ) ); } $response[] = $this->prepare_response_for_collection( $data ); } return rest_ensure_response( $response ); } /** * Validate style.css as valid CSS. * * Currently just checks for invalid markup. * * @since 6.2.0 * @since 6.4.0 Changed method visibility to protected. * * @param string $css CSS to validate. * @return true|WP_Error True if the input was validated, otherwise WP_Error. */ protected function validate_custom_css( $css ) { if ( preg_match( '# 400 ) ); } return true; } } able; } /** * Get name of ID column * * @return string name of ID column */ public function getID() { return $this->_id; } public function setID( $id ) { $this->_id = $id; } public function getAll( $fields = '*' ) { return $this->get($fields); } public function getById( $id, $fields = '*', $return = 'row' ) { $condition = 'WHERE ' . $this->_alias . '.' . $this->_id . ' = "' . ( (int) $id ) . '"'; return $this->get($fields, $condition, null, $return); } protected function _addJoin() { $res = ''; if (!empty($this->_join)) { $res = ' ' . implode(' ', $this->_join); $this->_join = array(); } return $res; } /** * Add LIMIT to SQL */ public function limit( $limit = '' ) { if (is_numeric($limit)) { $this->_limit = $limit; } else { $this->_limit = ''; } return $this; } public function setLimit( $limit = '' ) { $this->_limit = $limit; return $this; } public function limitFrom( $limit = '' ) { if (is_numeric($limit)) { $this->_limitFrom = (int) $limit; } return $this; } public function limitTo( $limit = '' ) { if (is_numeric($limit)) { $this->_limitTo = (int) $limit; } return $this; } /** * Add ORDER BY to SQL * * @param mixed $fields */ public function orderBy( $fields ) { if (is_array($fields)) { $order = implode(',', $fields); } elseif ('' != $fields) { $order = $fields; } $this->_order = $order; return $this; } /** * Add GROUP BY to SQL * * @param mixed $fields */ public function groupBy( $fields ) { if (is_array($fields)) { $group = implode(',', $fields); } elseif ('' != $fields) { $group = $fields; } $this->_group = $group; return $this; } public function get( $fields = '*', $where = '', $tables = '', $return = 'all' ) { if (!$tables) { $tables = $this->_table . ' ' . $this->_alias; } if (strpos($this->_alias, $fields)) { $fields = $this->_alias . '.' . $fields; } $query = 'SELECT ' . $fields . ' FROM ' . $tables; $query .= $this->_addJoin(); if ($where) { $where = trim($this->_getQueryString($where, 'AND')); if (!empty($where)) { if (!preg_match('/^WHERE/i', $where)) { $where = 'WHERE ' . $where; } $query .= ' ' . $where; } } if ('' != $this->_group) { $query .= ' GROUP BY ' . $this->_group; $this->_group = ''; } if ('' != $this->_order) { $query .= ' ORDER BY ' . $this->_order; $this->_order = ''; } if ('' != $this->_limit) { if (is_numeric($this->_limit)) { $query .= ' LIMIT 0,' . $this->_limit; } else { $query .= ' LIMIT ' . $this->_limit; } $this->_limit = ''; } elseif ( ( '' !== $this->_limitFrom ) && ( '' !== $this->_limitTo ) ) { $query .= ' LIMIT ' . $this->_limitFrom . ',' . $this->_limitTo; $this->_limitFrom = ''; $this->_limitTo = ''; } return DbWpf::get($query, $return); } public function store( $data, $method = 'INSERT', $where = '' ) { $this->_clearErrors(); $method = strtoupper($method); if ($this->_escape) { $data = DbWpf::escape($data); } $query = ''; switch ($method) { case 'INSERT': $query = 'INSERT INTO '; if (isset($data[$this->_id]) && empty($data[$this->_id])) { unset($data[$this->_id]); } break; case 'UPDATE': $query = 'UPDATE '; break; } $fields = $this->_getQueryString($data, ',', true); if (empty($fields)) { $this->_addError(esc_html__('Nothing to update', 'woo-product-filter')); return false; } $query .= $this->_table . ' SET ' . $fields; if (!empty($this->_errors)) { return false; } if ( ( 'UPDATE' == $method ) && !empty($where) ) { $query .= ' WHERE ' . $this->_getQueryString($where, 'AND'); } if (DbWpf::query($query)) { if ('INSERT' == $method) { return DbWpf::lastID(); } else { return true; } } else { $this->_addError(WPF_TEST_MODE ? DbWpf::getError() : esc_html__('Database error. Please contact your developer.', 'woo-product-filter')); } return false; } public function insert( $data ) { return $this->store($data); } public function update( $data, $where ) { if (is_numeric($where)) { $where = array($this->_id => $where); } return $this->store($data, 'UPDATE', $where); } public function alias( $alias = null ) { if (!is_null($alias)) { $this->_alias = $alias; } return $this->_alias; } /** * Delete record(s) * * @param mixed $where condition to use in query, if numeric givven - use delete by ID column * @return query result */ public function delete( $where = '' ) { if ($where) { $q = 'DELETE FROM ' . $this->_table; if (is_numeric($where)) { $where = array($this->_id => $where); } $q .= ' WHERE ' . $this->_getQueryString($where, 'AND'); } else { $q = 'TRUNCATE TABLE ' . $this->_table; } return DbWpf::query($q); } /** * Convert to database query * * @param mixed $data if array given - convert it into string where key - is column name, value - database value to set; * if key == "additionalCondition" then we will just add value to string * if string givven - just return it without changes * @param string $delim delimiter to use in query, recommended - ',', 'AND', 'OR' * @return string query string */ public function _getQueryString( $data, $delim = ',', $validate = false ) { $res = ''; if (is_array($data) && !empty($data)) { foreach ($data as $k => $v) { if (array_key_exists($k, $this->_fields) || $k == $this->_id) { $val = $v; if (isset($this->_fields[$k]) && $this->_fields[$k]->adapt['dbTo']) { $val = FieldAdapterWpf::_($val, $this->_fields[$k]->adapt['dbTo'], FieldAdapterWpf::DB); } if ($validate) { if (isset($this->_fields[$k]) && is_object($this->_fields[$k])) { $objForValidation = clone $this->_fields[$k]; $objForValidation->setValue($val); $errors = ValidatorWpf::_($objForValidation); if ($errors) { $this->_addError($errors); } } } if (isset($this->_fields[$k])) { switch ($this->_fields[$k]->type) { case 'int': case 'tinyint': $res .= $k . ' = ' . ( (int) $val ) . ' ' . $delim . ' '; break; case 'float': $res .= $k . ' = ' . ( (float) $val ) . ' ' . $delim . ' '; break; case 'decimal': $res .= $k . ' = ' . ( (float) $val ) . ' ' . $delim . ' '; break; case 'free': //Just set it as it is $res .= $k . ' = ' . $val . ' ' . $delim . ' '; break; default: $res .= $k . ' = \'' . $val . '\' ' . $delim . ' '; break; } } else { $res .= $k . ' = \'' . $val . '\' ' . $delim . ' '; } } elseif ('additionalCondition' == $k) { //just add some string to query $res .= $v . ' ' . $delim . ' '; } } $res = substr($res, 0, -( strlen($delim) + 1 )); } elseif (is_string($data)) { $res = $data; } return $res; } /** * Add new FieldWpfWpf for children table (@see class field) * * @param string $name name of a field * @param string $html html type of field (text, textarea, etc. @see html class) * @param string $type database type (int, varcahr, etc.) * @param mixed $default default value for this field * @return object $this - pointer to current object */ protected function _addField( $name, $html = 'text', $type = 'other', $default = '', $label = '', $maxlen = 0, $dbAdapt = '', $htmlAdapt = '', $description = '' ) { $this->_fields[$name] = toeCreateObjWpf('FieldWpf', array($name, $html, $type, $default, $label, $maxlen, $dbAdapt, $htmlAdapt, $description)); return $this; } /** * Public alias for _addField() method */ public function addField() { $args = func_get_args(); return call_user_func_array(array($this, '_addField'), $args); } public function getFields() { return $this->_fields; } public function getField( $name ) { return $this->_fields[$name]; } public function exists( $value, $field = '' ) { if (!$field) { $field = $this->_id; } return DbWpf::get('SELECT ' . $this->_id . ' FROM ' . $this->_table . ' WHERE ' . $field . ' = "' . $value . '"', 'one'); } protected function _addError( $error ) { if (is_array($error)) { $this->_errors = array_merge($this->_errors, $error); } else { $this->_errors[] = $error; } } public function getErrors() { return $this->_errors; } protected function _clearErrors() { $this->_errors = array(); } /** * Prepare data before send it to database */ public function prepareInput( $d = array() ) { $ignore = isset($d['ignore']) ? $d['ignore'] : array(); foreach ($this->_fields as $key => $f) { if ('tinyint' == $f->type) { if ('true' == $d[$key]) { $d[$key] = 1; } if (empty($d[$key]) && !in_array($key, $ignore)) { $d[$key] = 0; } } if ('date' == $f->type) { if (empty($d[$key]) && !in_array($key, $ignore)) { $d[$key] = '0000-00-00'; } elseif (!empty($d[$key])) { $d[$key] = DbWpf::timeToDate($d[$key]); } } } $d[$this->_id] = isset($d[$this->_id]) ? intval($d[$this->_id]) : 0; return $d; } /** * Prepare data after extracting it from database */ public function prepareOutput( $d = array()) { $ignore = isset($d['ignore']) ? $d['ignore'] : array(); foreach ($this->_fields as $key => $f) { switch ($f->type) { case 'date': if ('0000-00-00' == $d[$key] || empty($d[$key])) { $d[$key] = ''; } else { $d[$key] = gmdate(WPF_DATE_FORMAT, DbWpf::dateToTime($d[$key])); } break; case 'int': case 'tinyint': if ('true' == $d[$key]) { $d[$key] = 1; } if ('false' == $d[$key]) { $d[$key] = 0; } $d[$key] = (int) $d[$key]; break; } } $d[$this->_id] = isset($d[$this->_id]) ? intval($d[$this->_id]) : 0; return $d; } public function install( $d = array() ) { } public function uninstall( $d = array() ) { } public function activate() { } public function getLastInsertID() { return DbWpf::get('SELECT MAX(' . $this->_id . ') FROM ' . $this->_table, 'one'); } public function adaptHtml( $val ) { return htmlspecialchars($val, ENT_COMPAT); } }