<?php
|
namespace JVBase\rest\routes;
|
|
use JVBase\JVB;
|
use JVBase\rest\RestRouteManager;
|
use JVBase\meta\MetaManager;
|
use WP_Query;
|
use WP_Error;
|
use WP_REST_Request;
|
use WP_REST_Response;
|
|
if (!defined('ABSPATH')) {
|
exit; // Exit if accessed directly
|
}
|
|
class AdminRoutes extends RestRouteManager
|
{
|
protected array $fields;
|
protected $meta;
|
protected string $metaType;
|
protected string $content;
|
|
public function __construct()
|
{
|
$this->cache_name = 'itsme';
|
parent::__construct();
|
$this->action = 'itsme';
|
}
|
public function registerRoutes():void
|
{
|
if (!current_user_can('manage_options')) {
|
return;
|
}
|
register_rest_route($this->namespace, '/myster', [
|
[
|
'methods' => 'GET',
|
'callback' => [$this, 'getItems'],
|
'permission_callback' => [$this, 'checkPermission']
|
],
|
[
|
'methods' => 'POST',
|
'callback' => [$this, 'updateItems'],
|
'permission_callback' => [$this, 'checkPermission']
|
]
|
]);
|
|
register_rest_route($this->namespace, '/admin-action', [
|
[
|
'methods' => 'POST',
|
'callback' => [$this, 'adminAction'],
|
'permission_callback' => [$this, 'checkPermission']
|
]
|
]);
|
}
|
|
/**
|
* @param WP_REST_Request $request The Request object
|
*
|
* @return bool whether or not we can proceed
|
*/
|
public function checkPermission(WP_REST_Request $request):bool
|
{
|
if (!current_user_can('manage_options')) {
|
return false;
|
}
|
$this->verifyNonce($request, 'wp_rest');
|
if ($this->action!=='') {
|
$this->verifyNonce($request, $this->action . get_current_user_id(), $request->get_header('action_nonce'));
|
}
|
return true;
|
}
|
|
/**
|
* Handles admin actions from the custom WP Admin pages.
|
* Extended by other managers that register admin subpages
|
* @param WP_REST_Request $request
|
*
|
* @return WP_REST_Response
|
*/
|
public function adminAction(WP_REST_Request $request):WP_REST_Response
|
{
|
error_log('Request Params: '.print_r($request->get_param('action'), true));
|
return apply_filters(
|
BASE.'admin_action_filter',
|
new WP_REST_Response([
|
'success' => false,
|
'message' => 'No filters found'
|
]),
|
$request,
|
sanitize_text_field($request->get_param('action'))
|
);
|
}
|
|
/**
|
* @param WP_REST_Request $request Request object
|
*
|
* @return WP_REST_Response
|
*/
|
public function updateItems(WP_REST_Request $request):WP_REST_Response
|
{
|
error_log('Received Request: '.print_r($request->get_params(), true));
|
$content = $request->get_param('content');
|
if (!$this->checkContent($content, true)) {
|
return new WP_REST_Response([
|
'sucess' => false,
|
'message' => 'Invalid attempt'
|
]);
|
}
|
$data = $request->get_param('data');
|
if (empty($data)) {
|
return new WP_REST_Response([
|
'success' => true,
|
'message' => 'Nothing to Update'
|
]);
|
}
|
|
$type = match ($content) {
|
'shop',
|
'artform',
|
'type',
|
'media',
|
'artstyle',
|
'arttheme',
|
'city',
|
'colour',
|
'offerfor',
|
'pstyle',
|
'style',
|
'theme' => 'term',
|
default => 'post',
|
};
|
$errors = [];
|
$success = [];
|
error_log('Data: '.print_r($data, true));
|
|
foreach ($data as $ID => $fields) {
|
$ID = (int)$ID;
|
$meta = new MetaManager($ID, $type);
|
$allFields = JVB()->getFields($content);
|
foreach ($fields as $name => $value) {
|
$process = true;
|
if (!array_key_exists($name, $allFields)) {
|
$errors[$ID][$name] = __('Field not found', 'jvb');
|
$process = false;
|
}
|
error_log('Proceeding as Normal...');
|
//Switch between config types to extract values for special cases
|
$config = $allFields[$name];
|
|
switch ($config['type']) {
|
case 'radio':
|
//Ensure we only chose one option
|
$temp = explode(',', $value);
|
$value = trim($temp[0]);
|
if (!in_array($value, $config['options'])) {
|
$errors[$ID][$name] = __('Invalid Option', 'jvb');
|
$process = false;
|
}
|
break;
|
case 'set':
|
$temp = explode(',', $value);
|
$value = array_map('trim', $temp);
|
break;
|
case 'repeater':
|
error_log('Repeater: '.print_r($name, true));
|
$single = false;
|
switch ($name) {
|
case 'keywords':
|
$single = 'keyword';
|
break;
|
case 'languages':
|
$single = 'language';
|
break;
|
case 'links':
|
$single = 'url';
|
break;
|
case 'followers':
|
$single = 'count';
|
break;
|
}
|
$items = array_keys($config['fields']);
|
|
//First, separate out any ]
|
if (strpos($value, ']')) {
|
$rows = array_map(function ($item) {
|
return str_replace('[', '', $item);
|
}, explode(']', $value));
|
array_pop($rows);
|
error_log('Rows: '.print_r($rows, true));
|
$value = [];
|
foreach ($rows as $index => $row) {
|
$r = array_map('trim', explode(',', $row));
|
if (count($r) !== count($items)) {
|
$errors[$ID][$name] = __('Not enough fields set. May not save correctly', 'jvb');
|
}
|
//attempt to save fields
|
$new = [];
|
foreach ($items as $key => $i) {
|
$new[$i] = (array_key_exists($key, $r)) ? $r[$key] : (($i==='checked') ? date('Y-m-d'): '');
|
}
|
$value[] = $new;
|
}
|
error_log('Processed Repeater Value for sanitizing: '.print_r($value, true));
|
|
} elseif ($value === '') {
|
$value = [];
|
} else {
|
if (!$single) {
|
$errors[$ID][$name] = __('Must set single key', 'jvb');
|
$process = false;
|
}
|
$rows = array_map('trim', explode(',', $value));
|
$value = [];
|
foreach ($rows as $row) {
|
$new = [];
|
foreach ($items as $i) {
|
$new[$i] = ($i === $single) ? $row : (($i==='checked') ? date('Y-m-d') : '');
|
}
|
error_log('New Repeater Row Output: '.print_r($new, true));
|
$value[] = $new;
|
}
|
}
|
|
break;
|
}
|
|
switch ($name) {
|
case 'shop':
|
case 'type':
|
case 'city':
|
$terms = array_map('trim', explode(',', $value));
|
$set = [];
|
foreach ($terms as $term) {
|
$t = get_term_by('name', $value, BASE.$name);
|
if (!$t) {
|
$errors[$ID][$name][] = __($value.' does not exist yet', 'jvb');
|
} else {
|
$set[] = $t->term_id;
|
}
|
}
|
if (!empty($set)) {
|
$result = wp_set_post_terms($ID, $set, BASE.$name);
|
$result = is_array($result);
|
}
|
break;
|
case 'owner':
|
error_log('Processing Owner Request from Admin Routes...');
|
$users = array_map('trim', explode(',', $value));
|
foreach ($users as $user) {
|
$t = jvbGetUserByDisplayName($user)??jvbGetUserByFirstName($user)??false;
|
error_log('Got user: '.print_r($t, true));
|
if (!$t) {
|
$errors[$ID][$name][] = __($value.' does not exist yet...', 'jvb');
|
$result = false;
|
} else {
|
$result = JVB()->routes('shop')->setShopOwner($t->ID, $ID, true);
|
}
|
}
|
break;
|
case 'managers':
|
error_log('Processing Manager Request from Admin Routes...');
|
$users = array_map('trim', explode(',', $value));
|
foreach ($users as $user) {
|
$t = jvbGetUserByDisplayName($user)??jvbGetUserByFirstName($user)??false;
|
error_log('Got user: '.print_r($t, true));
|
if (!$t) {
|
$result = $errors[$ID][$name][] = __($value.' does not exist yet...', 'jvb');
|
} else {
|
$result = JVB()->routes('shop')->setShopManager($t->ID, $ID, true);
|
}
|
}
|
break;
|
case 'location':
|
$value = [
|
'address' => $value,
|
'lat' => '',
|
'lng' => '',
|
];
|
$result = $meta->updateValue($name, $value);
|
break;
|
case 'hours':
|
$temp = [];
|
foreach ($value as $key => $v) {
|
if (strpos($v['days'], '-')) {
|
$temp[$key]['days'] = jvbExpandDayRange($v['days']);
|
$temp[$key]['time_open'] = $v['time_open'];
|
$temp[$key]['time_closes'] = $v['time_closes'];
|
}
|
}
|
$value = $temp;
|
error_log('Final Hours for processing: '.print_r($value, true));
|
$result = $meta->updateValue($name, $value);
|
break;
|
default:
|
$result = $meta->updateValue($name, $value);
|
break;
|
}
|
|
//Save the value
|
if ($result) {
|
$success[$ID][] = $name;
|
} else {
|
$errors[$ID][] = [
|
$name => 'Could not update value'
|
];
|
}
|
}
|
}
|
return new WP_REST_Response([
|
'success' => true,
|
'successful' => $success,
|
'errors' => $errors
|
]);
|
}
|
|
/**
|
* @param array $filters
|
*
|
* @return array
|
*/
|
protected function checkFilters(array $filters)
|
{
|
global $karma;
|
$out = [];
|
foreach ($filters as $type => $value) {
|
if (!array_key_exists($type, $karma)) {
|
continue;
|
}
|
$out[$type] = jvbSanitizeIDList($value);
|
}
|
return $out;
|
}
|
|
/**
|
* @param WP_REST_Request $request The REST Request
|
*
|
* @return array
|
*/
|
protected function buildParams(WP_REST_Request $request):array
|
{
|
$data = $request->get_params();
|
$this->setMetaType($data['content']);
|
switch ($this->metaType) {
|
case 'post':
|
$key = 'post_type';
|
break;
|
case 'term':
|
$key = 'taxonomy';
|
break;
|
case 'user':
|
$key = 'role';
|
break;
|
default:
|
return [];
|
}
|
global $jvb_everything;
|
return [
|
$key => (array_key_exists('content', $data) &&
|
array_key_exists($data['content'], $jvb_everything)) ?
|
BASE.$data['content'] : BASE.'artist',
|
'order' => (array_key_exists('order', $data) &&
|
in_array(strtolower($data['order']), ['asc', 'desc'])) ?
|
strtolower($data['order']) : 'desc',
|
'orderby' => (array_key_exists('orderby', $data) &&
|
in_array($data['orderby'], ['name', 'date', 'followers', 'karma'])) ?
|
$data['orderby'] : 'name',
|
'paged' => (array_key_exists('page', $data) && is_numeric($data['page'])) ?
|
(int)$data['page'] : 1,
|
'filters' => (array_key_exists('filters', $data)) ?
|
$this->checkFilters($data['filters']) : null,
|
];
|
}
|
|
protected function setMetaType(string $type):void
|
{
|
$this->metaType = match (true) {
|
array_key_exists($type, JVB_CONTENT) => 'post',
|
array_key_exists($type, JVB_TAXONOMY) => 'term',
|
array_key_exists($type, JVB_USER) => 'user',
|
default => false,
|
};
|
}
|
|
/**
|
* @param WP_REST_Request $request The REST Request
|
*
|
* @return WP_REST_Response
|
*/
|
public function getItems(WP_REST_Request $request):WP_REST_Response
|
{
|
|
$args = $this->buildParams($request);
|
$key = $this->cache->generateKey($args);
|
|
$cache = $this->cache->get($key);
|
if ($cache) {
|
return new WP_REST_Response($cache);
|
}
|
|
$this->content = $request->get_param('content');
|
$args['posts_per_page'] = 50;
|
|
$this->fields = jvbGetFields($this->content);
|
// $this->fields = array_filter(JVB()->getFields($data['content']), function($arr){
|
// return array_key_exists('quickEdit', $arr);
|
// });
|
|
$response = match ($this->metaType) {
|
'post' => $this->getPostItems($args),
|
'term' => $this->getTermItems($args),
|
'user' => $this->getUserItems($args),
|
default => []
|
};
|
|
$this->cache->set($key, $response);
|
return new WP_REST_Response($response);
|
}
|
|
/**
|
* @param array $args the data from buildParams method
|
*
|
* @return array
|
*/
|
protected function getTermItems(array $args):array
|
{
|
if (isJVBContentTax($args['taxonomy'])) {
|
return $this->getContentTypeTaxItems($args);
|
}
|
// Build query arguments
|
$args = array_merge($args, [
|
'hide_empty' => false,
|
'number' => $args['posts_per_page'],
|
'offset' => ($args['paged'] - 1) * $args['posts_per_page'],
|
'fields' => 'ids'
|
]);
|
|
// Add ordering
|
switch ($args['orderby']) {
|
case 'date':
|
$args['orderby'] = 'id'; // Terms don't have date, so use ID as a proxy
|
break;
|
case 'karma':
|
// Terms should be ordered by meta value
|
$args['orderby'] = 'meta_value_num';
|
$args['meta_key'] = BASE . 'karma';
|
break;
|
default:
|
$args['orderby'] = 'name';
|
break;
|
}
|
$args['order'] = strtoupper($args['order']);
|
|
// Add any filters
|
if (!empty($args['filters'])) {
|
// Term meta filtering would go here
|
$meta_query = [];
|
|
foreach ($args['filters'] as $filter_type => $filter_values) {
|
if (!empty($filter_values)) {
|
// Example: filter by parent terms
|
if ($filter_type === 'parent') {
|
$args['parent'] = $filter_values[0]; // Assume single parent filter
|
} else {
|
// For meta-based filters
|
$meta_query[] = [
|
'key' => BASE . $filter_type,
|
'value' => $filter_values,
|
'compare' => 'IN'
|
];
|
}
|
}
|
}
|
|
if (!empty($meta_query)) {
|
$args['meta_query'] = $meta_query;
|
}
|
}
|
unset($args['filters']);
|
|
// Get count first for pagination info
|
$count_args = $args;
|
unset($count_args['number']);
|
unset($count_args['offset']);
|
$count_args['fields'] = 'count';
|
$total_items = get_terms($count_args);
|
|
// Get the actual terms
|
$term_ids = get_terms($args);
|
|
// Error handling
|
if (is_wp_error($term_ids)) {
|
return [
|
'items' => [],
|
'has_more' => false,
|
'total_items' => 0,
|
'total_pages' => 0,
|
'error' => $term_ids->get_error_message()
|
];
|
}
|
|
// Format each term
|
$items = array_map([$this, 'formatItem'], $term_ids);
|
|
return [
|
'items' => $items,
|
'has_more' => ($total_items > ($args['offset'] + $args['number'])),
|
'total_items' => (int)$total_items,
|
'total_pages' => ceil($total_items / $args['posts_per_page'])
|
];
|
}
|
|
protected function getUserItems(array $args):array
|
{
|
return [];
|
}
|
|
/**
|
* @param array $data the $data built by buildParams
|
*
|
* @return array
|
*/
|
protected function getContentTypeTaxItems(array $data):array
|
{
|
global $wpdb;
|
$table_name = $wpdb->prefix . BASE . jvbNoBase($data['taxonomy']);
|
|
// Start building the query to get just the term_ids with proper ordering
|
$sql_select = "SELECT s.term_id";
|
$sql_from = " FROM {$table_name} AS s";
|
$sql_where = " WHERE 1=1";
|
$sql_orderby = "";
|
$sql_limit = "";
|
|
$params = [];
|
|
// Add filters if any
|
if (!empty($data['filters'])) {
|
foreach ($data['filters'] as $filter_type => $filter_values) {
|
if ($filter_type === 'city' && !empty($filter_values)) {
|
$placeholders = implode(',', array_fill(0, count($filter_values), '%d'));
|
$sql_where .= $wpdb->prepare(" AND s.city IN ($placeholders)", ...$filter_values);
|
}
|
// Add more filter conditions for other fields as needed
|
}
|
}
|
|
// Determine ORDER BY clause - here we can use any column from the custom table
|
$valid_order_columns = ['name', 'updated_at', 'established_year', 'karma'];
|
$orderby_column = in_array($data['orderby'], $valid_order_columns) ? $data['orderby'] : 'name';
|
$sql_orderby = " ORDER BY s." . $orderby_column;
|
|
// Validate order direction
|
$order_direction = strtoupper($data['order']) === 'DESC' ? 'DESC' : 'ASC';
|
$sql_orderby .= " " . $order_direction;
|
|
// Calculate offset for pagination
|
$per_page = absint($data['posts_per_page']);
|
$page = max(1, absint($data['paged']));
|
$offset = ($page - 1) * $per_page;
|
|
// Add pagination with prepared statement
|
$sql_limit = $wpdb->prepare(" LIMIT %d OFFSET %d", $per_page, $offset);
|
|
// Count query (without limit clause)
|
$count_sql = "SELECT COUNT(s.term_id)" . $sql_from . $sql_where;
|
$total_items = $wpdb->get_var($count_sql);
|
|
// Final query with all components
|
$sql = $sql_select . $sql_from . $sql_where . $sql_orderby . $sql_limit;
|
|
// Execute the query
|
$shop_term_ids = $wpdb->get_col($sql);
|
|
// Now get the full term objects for these IDs
|
$items = [];
|
foreach ($shop_term_ids as $shop_term_id) {
|
$items[] = $this->formatItem($shop_term_id);
|
}
|
|
return [
|
'items' => $items,
|
'has_more' => ($total_items > ($offset + $per_page)),
|
'total_items' => (int)$total_items,
|
'total_pages' => ceil($total_items / $per_page)
|
];
|
}
|
|
/**
|
* @param array $args array returned by buildParams
|
*
|
* @return array
|
*/
|
protected function getPostItems(array $args):array
|
{
|
$args['fields'] = 'ids';
|
$args['post_status'] = ['draft', 'trash', 'publish'];
|
|
$query = new WP_Query($args);
|
$items = array_map([$this, 'formatItem'], $query->posts);
|
|
return [
|
'items' => $items,
|
'has_more' => $query->max_num_pages > $args['paged'],
|
'total_items' => $query->found_posts,
|
'total_pages' => $query->max_num_pages
|
];
|
}
|
|
/**
|
* @param int $ID the ID of the item to format
|
*
|
* @return array
|
*/
|
protected function formatItem(int $ID):array
|
{
|
$meta = new MetaManager($ID, $this->metaType);
|
$item = [
|
'id' => $ID,
|
'public' => !($this->metaType === 'post') || get_post_status($ID) === 'publish',
|
];
|
|
$hierarchical = false;
|
if ($this->metaType === 'term') {
|
$term = get_term($ID);
|
if ($term) {
|
$hierarchical = is_taxonomy_hierarchical($term->taxonomy);
|
}
|
}
|
|
foreach ($this->fields as $key => $config) {
|
switch ($key) {
|
case 'type':
|
case 'city':
|
case 'shop':
|
$terms = get_the_terms($ID, BASE.$key);
|
$value = [];
|
if ($terms && !is_wp_error($terms)) {
|
foreach ($terms as $term) {
|
$value[] = htmlspecialchars_decode($term->name);
|
}
|
}
|
$item[$key] = implode(', ', $value);
|
break;
|
default:
|
$item[$key] = $meta->getValue($key);
|
break;
|
}
|
|
switch ($config['type']) {
|
case 'repeater':
|
if (empty($item[$key]) || !is_array($item[$key])) {
|
$item[$key] = '';
|
} else {
|
$temp = '';
|
foreach ($item[$key] as $row) {
|
if (is_array($row)) {
|
// Format each row as [value1,value2,value3]
|
$rowValues = array_values($row);
|
$temp .= '[' . implode(',', $rowValues) . ']';
|
} else {
|
// Handle simpler cases where rows might not be arrays
|
$temp .= '[' . $row . ']';
|
}
|
}
|
$item[$key] = $temp;
|
}
|
break;
|
case 'location':
|
$item[$key] = $item[$key]['address'];
|
break;
|
}
|
if ($hierarchical && $key === 'term_name') {
|
$item[$key.'_path'] = JVB()->routes('term')->getTermPath($ID, html_entity_decode($term->name), $term->taxonomy);
|
}
|
}
|
|
error_log('Item: '.print_r($item, true));
|
return $item;
|
}
|
}
|