| 1 |
<?php |
| 2 |
|
| 3 |
|
| 4 |
namespace Jet_Form_Builder\Presets; |
| 5 |
|
| 6 |
use Jet_Form_Builder\Classes\Instance_Trait; |
| 7 |
use Jet_Form_Builder\Classes\Tools; |
| 8 |
use Jet_Form_Builder\Exceptions\Plain_Default_Exception; |
| 9 |
use Jet_Form_Builder\Exceptions\Preset_Exception; |
| 10 |
use Jet_Form_Builder\Presets\Sources; |
| 11 |
use Jet_Form_Builder\Presets\Types\Base_Preset; |
| 12 |
use Jet_Form_Builder\Presets\Types\Dynamic_Preset; |
| 13 |
use Jet_Form_Builder\Presets\Types\General_Preset; |
| 14 |
|
| 15 |
if ( ! defined( 'WPINC' ) ) { |
| 16 |
die; |
| 17 |
} |
| 18 |
|
| 19 |
/** |
| 20 |
* @method static Preset_Manager instance() |
| 21 |
* |
| 22 |
* Class Preset_Manager |
| 23 |
* @package Jet_Form_Builder\Presets |
| 24 |
*/ |
| 25 |
class Preset_Manager { |
| 26 |
|
| 27 |
use Instance_Trait; |
| 28 |
|
| 29 |
protected $data = null; |
| 30 |
protected $source = null; |
| 31 |
protected $defaults = array( |
| 32 |
'enabled' => false, |
| 33 |
'from' => 'post', |
| 34 |
'post_from' => 'current_post', |
| 35 |
'user_from' => 'current_user', |
| 36 |
'query_var' => '_post_id', |
| 37 |
'term_from' => 'current_term', |
| 38 |
'fields_map' => array(), |
| 39 |
); |
| 40 |
|
| 41 |
// phpcs:disable PSR2.Classes.PropertyDeclaration.Underscore |
| 42 |
private $_preset_types = array(); |
| 43 |
private $_source_types; |
| 44 |
|
| 45 |
// phpcs:enable PSR2.Classes.PropertyDeclaration.Underscore |
| 46 |
|
| 47 |
|
| 48 |
protected function __construct() { |
| 49 |
$this->register_preset_types(); |
| 50 |
$this->register_source_types(); |
| 51 |
|
| 52 |
do_action( 'jet-form-builder/after-init/preset-manager' ); |
| 53 |
} |
| 54 |
|
| 55 |
public function set_form_id( $form_id ): Preset_Manager { |
| 56 |
if ( ! $form_id ) { |
| 57 |
return $this; |
| 58 |
} |
| 59 |
|
| 60 |
// General_Preset is not unique (Preset_Manager::general() always |
| 61 |
// returns the same shared instance), and Base_Preset::set_init_data() |
| 62 |
// merges into any existing $data rather than replacing it. Reset the |
| 63 |
// instance's $data before loading the current form's preset config, |
| 64 |
// otherwise a page rendering multiple forms could leak one form's |
| 65 |
// `restricted` flag (or other preset config) into another form's |
| 66 |
// permission evaluation via array_merge() carrying over keys the |
| 67 |
// current form's own config doesn't set - see issues-tracker #20359. |
| 68 |
// |
| 69 |
// trust_restriction_flag( true ) is set unconditionally here, not |
| 70 |
// saved/restored like the equivalent call in get_field_value(): this |
| 71 |
// form's config is always admin-authored (post meta), so General |
| 72 |
// Preset's own default already resolves to trusted |
| 73 |
// (trusts_restriction_flag_by_default()). The explicit call exists |
| 74 |
// only to document that intent at the call site, not to override a |
| 75 |
// caller-supplied value - unlike get_field_value(), nothing calls |
| 76 |
// set_form_id() expecting an untrusted evaluation afterwards. |
| 77 |
$this->general() |
| 78 |
->trust_restriction_flag( true ) |
| 79 |
->reset_init_data( $this->general()->preset_source( $form_id ) ); |
| 80 |
|
| 81 |
try { |
| 82 |
$this->general()->get_source(); |
| 83 |
} catch ( Preset_Exception $exception ) { |
| 84 |
// do nothing |
| 85 |
} |
| 86 |
|
| 87 |
return $this; |
| 88 |
} |
| 89 |
|
| 90 |
/** |
| 91 |
* @return Base_Preset[] |
| 92 |
*/ |
| 93 |
protected function preset_types(): array { |
| 94 |
return $this->_preset_types; |
| 95 |
} |
| 96 |
|
| 97 |
/** |
| 98 |
* @param $slug |
| 99 |
* |
| 100 |
* @return Base_Preset|void |
| 101 |
*/ |
| 102 |
public function get_preset_type_raw( $slug ): Base_Preset { |
| 103 |
if ( isset( $this->_preset_types[ $slug ] ) ) { |
| 104 |
return $this->_preset_types[ $slug ]; |
| 105 |
} |
| 106 |
|
| 107 |
_doing_it_wrong( |
| 108 |
esc_html( static::class . '::' . __METHOD__ ), |
| 109 |
'Undefined preset type', |
| 110 |
'1.4.2' |
| 111 |
); |
| 112 |
} |
| 113 |
|
| 114 |
public function get_preset_type( $slug ): Base_Preset { |
| 115 |
$type = $this->get_preset_type_raw( $slug ); |
| 116 |
|
| 117 |
return $type->is_unique() ? clone $type : $type; |
| 118 |
} |
| 119 |
|
| 120 |
/** |
| 121 |
* @return Dynamic_Preset|Base_Preset |
| 122 |
*/ |
| 123 |
public function dynamic() { |
| 124 |
return $this->get_preset_type( Dynamic_Preset::SLUG ); |
| 125 |
} |
| 126 |
|
| 127 |
/** |
| 128 |
* @return General_Preset|Base_Preset |
| 129 |
*/ |
| 130 |
public function general() { |
| 131 |
return $this->get_preset_type( General_Preset::SLUG ); |
| 132 |
} |
| 133 |
|
| 134 |
private function register_source_types() { |
| 135 |
/** @var Sources\Base_Source[] $types */ |
| 136 |
|
| 137 |
$types = apply_filters( |
| 138 |
'jet-form-builder/preset/source-types', |
| 139 |
array( |
| 140 |
new Sources\Preset_Source_Post(), |
| 141 |
new Sources\Preset_Source_User(), |
| 142 |
new Sources\Preset_Source_Query_Var(), |
| 143 |
new Sources\Preset_Source_Term(), |
| 144 |
) |
| 145 |
); |
| 146 |
|
| 147 |
foreach ( $types as $type ) { |
| 148 |
$this->register_source_type( $type ); |
| 149 |
} |
| 150 |
} |
| 151 |
|
| 152 |
public function register_source_type( Sources\Base_Source $source ) { |
| 153 |
if ( ! $source->condition() ) { |
| 154 |
return; |
| 155 |
} |
| 156 |
$this->_source_types[ $source->get_id() ] = $source; |
| 157 |
|
| 158 |
$source->after_register(); |
| 159 |
} |
| 160 |
|
| 161 |
private function register_preset_types() { |
| 162 |
$types = array( |
| 163 |
new Dynamic_Preset(), |
| 164 |
new General_Preset(), |
| 165 |
); |
| 166 |
|
| 167 |
foreach ( $types as $type ) { |
| 168 |
$this->register_preset_type( $type ); |
| 169 |
} |
| 170 |
} |
| 171 |
|
| 172 |
/** |
| 173 |
* @param $args |
| 174 |
* |
| 175 |
* @return Base_Preset |
| 176 |
* @throws Plain_Default_Exception|Preset_Exception |
| 177 |
*/ |
| 178 |
protected function get_preset_type_manager( $args ) { |
| 179 |
foreach ( $this->preset_types() as $type ) { |
| 180 |
$preset = $type->is_unique() ? clone $type : $type; |
| 181 |
|
| 182 |
if ( $preset->is_active_preset( $args ) ) { |
| 183 |
return $preset; |
| 184 |
} |
| 185 |
} |
| 186 |
|
| 187 |
throw new Preset_Exception( 'Preset manager is not installed.' ); |
| 188 |
} |
| 189 |
|
| 190 |
|
| 191 |
protected function register_preset_type( Base_Preset $type ) { |
| 192 |
$this->_preset_types[ $type->get_slug() ] = $type; |
| 193 |
} |
| 194 |
|
| 195 |
/** |
| 196 |
* Returns field value |
| 197 |
* |
| 198 |
* $args here are a form field block's attributes, which live in the |
| 199 |
* form's own post content/meta and require `edit_post` on the form to |
| 200 |
* write. That makes this a trusted origin: a `restricted: false` set by |
| 201 |
* the form author through the editor's "Restrict access" toggle is |
| 202 |
* honoured, exactly as that toggle's help text promises. |
| 203 |
* |
| 204 |
* Untrusted entry points - Rich_Content parsing a submitted field value |
| 205 |
* via jet_fb_parse_dynamic() - deliberately do NOT declare trust, so a |
| 206 |
* `restricted` flag smuggled through a submitted value is ignored and the |
| 207 |
* permission check always runs. Admin-authored call sites use |
| 208 |
* jet_fb_parse_dynamic_trusted() instead. See issues-tracker #20359. |
| 209 |
* |
| 210 |
* @param array $args |
| 211 |
* |
| 212 |
* @return [type] [description] |
| 213 |
*/ |
| 214 |
public function get_field_value( $args = array() ) { |
| 215 |
if ( empty( $args['name'] ) ) { |
| 216 |
return ''; |
| 217 |
} |
| 218 |
|
| 219 |
try { |
| 220 |
$manager = $this->get_preset_type_manager( $args ); |
| 221 |
} catch ( Plain_Default_Exception $exception ) { |
| 222 |
return $exception->getMessage(); |
| 223 |
} catch ( Preset_Exception $exception ) { |
| 224 |
return ''; |
| 225 |
} |
| 226 |
|
| 227 |
/** |
| 228 |
* Save & restore: get_preset_type_manager() only clones unique preset |
| 229 |
* types, so for a non-unique one (General_Preset) $manager IS the |
| 230 |
* process-wide shared instance. Leaving `true` on it would make the |
| 231 |
* trust sticky for every later caller that reaches the same instance |
| 232 |
* without an intervening reset - see issues-tracker #20359. |
| 233 |
*/ |
| 234 |
$restore_trust = $manager->trusts_restriction_flag(); |
| 235 |
|
| 236 |
$manager->trust_restriction_flag( true ); |
| 237 |
|
| 238 |
try { |
| 239 |
return $manager->get_source( $args )->result(); |
| 240 |
} catch ( Preset_Exception $exception ) { |
| 241 |
return ''; |
| 242 |
} finally { |
| 243 |
$manager->trust_restriction_flag( $restore_trust ); |
| 244 |
} |
| 245 |
} |
| 246 |
|
| 247 |
/** |
| 248 |
* @param $type |
| 249 |
* |
| 250 |
* @return Sources\Base_Source |
| 251 |
* @throws Preset_Exception |
| 252 |
*/ |
| 253 |
public function get_source_by_type( $type ): Sources\Base_Source { |
| 254 |
if ( ! isset( $this->_source_types[ $type ] ) ) { |
| 255 |
throw new Preset_Exception( |
| 256 |
esc_html( "Undefined source type: {$type}" ) |
| 257 |
); |
| 258 |
} |
| 259 |
|
| 260 |
return clone $this->_source_types[ $type ]; |
| 261 |
} |
| 262 |
|
| 263 |
public function prepare_result( $field_type, $value ) { |
| 264 |
// Prepare value for date field |
| 265 |
switch ( $field_type ) { |
| 266 |
case 'date-field': |
| 267 |
if ( ! Tools::is_valid_timestamp( $value ) ) { |
| 268 |
return $value; |
| 269 |
} |
| 270 |
|
| 271 |
return date_i18n( 'Y-m-d', $value ); |
| 272 |
case 'datetime-field': |
| 273 |
if ( ! Tools::is_valid_timestamp( $value ) ) { |
| 274 |
return $value; |
| 275 |
} |
| 276 |
|
| 277 |
return date_i18n( 'Y-m-d\TH:i', $value ); |
| 278 |
default: |
| 279 |
return apply_filters( 'jet-form-builder/preset/parse-value', $value, $this ); |
| 280 |
} |
| 281 |
} |
| 282 |
|
| 283 |
} |
| 284 |
|