|
<?php |
|
namespace Drawbridge\Metaboxes; |
|
|
|
class ACFConverter |
|
{ |
|
/** |
|
* Options to be converted to an ACF metabox. |
|
* |
|
* @var array |
|
*/ |
|
protected $options = []; |
|
|
|
/** |
|
* Prefix to be added to the post meta key. |
|
* |
|
* @var string |
|
*/ |
|
protected $prefix = 'castle'; |
|
|
|
/** |
|
* @param array $options Options to be converted to an ACF metabox. |
|
*/ |
|
public function __construct(array $options = []) |
|
{ |
|
if (empty($options['key'])) { |
|
wp_die(__('The ACF metabox can\'t be registered without a key.', 'acf-php')); |
|
} |
|
|
|
$this->setOptions($options); |
|
} |
|
|
|
/** |
|
* @return array |
|
*/ |
|
public function convert() |
|
{ |
|
return $this->convertFields(); |
|
} |
|
|
|
/** |
|
* @param array $options |
|
*/ |
|
protected function setOptions($options) |
|
{ |
|
$this->options = $options; |
|
} |
|
|
|
/** |
|
* @return array |
|
*/ |
|
protected function getOptions() |
|
{ |
|
return $this->options; |
|
} |
|
|
|
/** |
|
* @param mixed $location |
|
*/ |
|
protected function setLocation($location) |
|
{ |
|
$this->options['location'] = $location; |
|
} |
|
|
|
/** |
|
* @return mixed |
|
*/ |
|
protected function getLocation() |
|
{ |
|
return $this->options['location'] ?? []; |
|
} |
|
|
|
/** |
|
* @param array $fields |
|
*/ |
|
protected function setFields($fields) |
|
{ |
|
if (!is_array($fields)) { |
|
return; |
|
} |
|
|
|
$this->options['fields'] = $fields; |
|
} |
|
/** |
|
* @return array |
|
*/ |
|
protected function getFields() |
|
{ |
|
return $this->options['fields']; |
|
} |
|
|
|
/** |
|
* Set default options before the meta boxes are created. |
|
*/ |
|
protected function setDefaultOptions() |
|
{ |
|
$options = $this->getOptions(); |
|
/** |
|
* Set default options. |
|
*/ |
|
$options = array_merge( |
|
[ |
|
'label_placement' => 'left', |
|
'active' => 1, |
|
'location' => [ |
|
[ |
|
[ |
|
'param' => 'post_type', |
|
'operator' => '==', |
|
'value' => 'post', |
|
], |
|
], |
|
], |
|
'fields' => [], |
|
], |
|
$options |
|
); |
|
|
|
/** |
|
* `after_title` is an alias for `acf_after_title`. |
|
*/ |
|
if (isset($options['position']) && $options['position'] === 'after_title') { |
|
$options['position'] = 'acf_after_title'; |
|
} |
|
|
|
$this->setOptions($options); |
|
} |
|
/** |
|
* Convert location if necessary. |
|
*/ |
|
protected function convertLocation() |
|
{ |
|
$location = $this->getLocation(); |
|
/** |
|
* If the location entered in shorthand declaration, we need to convert it. |
|
* |
|
* Example: 'location' => 'post_type == page' |
|
*/ |
|
if (is_string($location)) { |
|
$location = explode(' ', $location); |
|
/** |
|
* Throw an error if the location entered is not in the correct syntax. |
|
*/ |
|
if (count($location) !== 3) { |
|
wp_die(__('The location entered for the metabox is incorrect.', 'acf-php')); |
|
} |
|
/** |
|
* Convert location to ACF-compatible format. |
|
*/ |
|
$location = [ |
|
[ |
|
[ |
|
'param' => $location[0], |
|
'operator' => $location[1], |
|
'value' => $location[2], |
|
], |
|
], |
|
]; |
|
} |
|
$this->setLocation($location); |
|
} |
|
/** |
|
* Convert conditional logic |
|
* |
|
* @param array $field Field options |
|
* @return array $field Field with conditional logic added |
|
*/ |
|
protected function convertConditionalLogic($field) |
|
{ |
|
/** |
|
* First, make sure conditional logic is definitely entered. |
|
*/ |
|
if (empty($field['conditional_logic'])) { |
|
return; |
|
} |
|
|
|
/** |
|
* Get options for key. |
|
*/ |
|
$options = $this->getOptions(); |
|
$key = $options['key']; |
|
$conditional = $field['conditional_logic']; |
|
|
|
/** |
|
* If it's an array (ACF-compatible version), add the ACF |
|
* `field_` prefix and metabox key to each `field` declaration. |
|
*/ |
|
if (is_array($conditional)) { |
|
foreach ($conditional as $i => $outer) { |
|
foreach ($outer as $j => $inner) { |
|
$fieldValue = $conditional[$i][$j]['field']; |
|
/** |
|
* Only add `field_` and metabox key if the value |
|
* doesn't start with `field_` already. |
|
*/ |
|
if (strpos($fieldValue, 'field_') !== 0) { |
|
$field['conditional_logic'][$i][$j]['field'] = 'field_' . $key . '_' . $fieldValue; |
|
} |
|
} |
|
} |
|
} |
|
/** |
|
* If it's the shorthand declaration, make it ACF-compatible. |
|
* Same logic as used in convertLocation(). |
|
* |
|
* Example: 'conditional_logic' => 'some_checkbox == 1' |
|
*/ |
|
if (is_string($conditional)) { |
|
$conditional = explode(' ', $conditional); |
|
/** |
|
* Throw an error if the conditional logic entered is not in the correct syntax. |
|
*/ |
|
if (count($conditional) !== 3) { |
|
wp_die(__('The conditional logic entered is incorrect.', 'acf-php')); |
|
} |
|
/** |
|
* Check if `field_` has already been added. |
|
*/ |
|
if (strpos($conditional[0], 'field_') !== 0) { |
|
$conditional[0] = 'field_' . $key . '_' . $conditional[0]; |
|
} |
|
/** |
|
* Convert conditional logic to ACF-compatible format. |
|
*/ |
|
$conditional = [ |
|
[ |
|
[ |
|
'field' => $conditional[0], |
|
'operator' => $conditional[1], |
|
'value' => $conditional[2], |
|
], |
|
], |
|
]; |
|
$field['conditional_logic'] = $conditional; |
|
} |
|
return $field; |
|
} |
|
/** |
|
* Set field defaults |
|
* |
|
* @param array $field Field options |
|
* @param string $fieldName Name of the field |
|
* @param bool $isLayout True if the field is a Flexible Content layout |
|
* @return array $field Field with defaults added |
|
*/ |
|
protected function setDefaults($field, $fieldName, $isLayout = false) |
|
{ |
|
/** |
|
* Default tab placement is `left`. |
|
*/ |
|
if (isset($field['type']) && $field['type'] === 'tab' && !isset($field['placement'])) { |
|
$field['placement'] = 'left'; |
|
} |
|
|
|
/** |
|
* Default for sub_fields is repeater. |
|
*/ |
|
if (!$isLayout && !isset($field['type']) && isset($field['sub_fields'])) { |
|
$field['type'] = 'repeater'; |
|
} |
|
|
|
/** |
|
* Default Repeater layout is `block`. |
|
*/ |
|
if (isset($field['type']) && $field['type'] === 'repeater' && !isset($field['layout'])) { |
|
$field['layout'] = 'block'; |
|
} |
|
|
|
/** |
|
* Default Flexible Content display is `row`. |
|
*/ |
|
if ($isLayout && !isset($field['display'])) { |
|
$field['display'] = 'row'; |
|
} |
|
|
|
/** |
|
* If no `type` is set and and it's not a Flexible Content layout, |
|
* set `type` to `text`. |
|
*/ |
|
if (!$isLayout && !isset($field['type'])) { |
|
$field['type'] = 'text'; |
|
} |
|
|
|
/** |
|
* If no label is given, automatically generate label based |
|
* on the field name. |
|
*/ |
|
if (!isset($field['label'])) { |
|
$field['label'] = esc_html(ucwords(str_replace('_', ' ', $fieldName))); |
|
} |
|
|
|
/** |
|
* Escaping for labels and instructions. |
|
*/ |
|
if (isset($field['label'])) { |
|
$field['label'] = esc_html($field['label']); |
|
} |
|
|
|
if (isset($field['instructions'])) { |
|
$field['instructions'] = esc_html($field['instructions']); |
|
} |
|
|
|
/** |
|
* Sensible escaping for messages. |
|
*/ |
|
if (isset($field['type']) && $field['type'] === 'message') { |
|
$field['esc_html'] = 0; |
|
$field['message'] = wp_kses_post($field['message']); |
|
} |
|
|
|
return $field; |
|
} |
|
/** |
|
* Convert field (add key, name and optional settings to fields). |
|
* |
|
* @param array $field Field options |
|
* @param string $fieldName Name of the field |
|
* @param string $parentKey Optional: key of the parent field |
|
* @param bool $isLayout True if the field is a Flexible Content layout |
|
* @return array $field Converted field |
|
*/ |
|
protected function convertField($field, $fieldName, $parentKey = '', $isLayout = false) |
|
{ |
|
/** |
|
* If the field is empty, don't do anything. |
|
*/ |
|
if (empty($field)) { |
|
return; |
|
} |
|
|
|
/** |
|
* Get options. Used to get the key of the metabox, which we |
|
* use to prefix the keys of the individual fields. |
|
*/ |
|
$options = $this->getOptions(); |
|
|
|
/** |
|
* Custom prefix, added to the name of top-level fields. |
|
*/ |
|
$prefix = (!empty($this->prefix) && empty($parentKey)) ? $this->prefix . '_' : ''; |
|
|
|
/** |
|
* If the user only passes a string, this field becomes a text field, |
|
* and the name of the field is the string passed. |
|
* |
|
* PHP automatically changes the key to an integer and assigns the string |
|
* to the value, which is what we use to check for this scenario. |
|
*/ |
|
if (is_int($fieldName) && is_string($field)) { |
|
$fieldName = $field; |
|
} |
|
|
|
/** |
|
* If the field is not an array, turn it into an empty array. |
|
*/ |
|
if (!is_array($field)) { |
|
$field = []; |
|
} |
|
|
|
/** |
|
* Set field name. |
|
*/ |
|
$field['name'] = $prefix . $fieldName; |
|
|
|
/** |
|
* Set field key. |
|
*/ |
|
$key = !empty($parentKey) ? $parentKey . '_' . $fieldName : $fieldName; |
|
|
|
/** |
|
* Check if the field has sub fields. If so, it's either a Repeater field |
|
* or a Flexible Content field layout. We convert all sub fields. |
|
*/ |
|
if (!empty($field['sub_fields']) && is_array($field['sub_fields'])) { |
|
$field['sub_fields'] = array_map(function ($name, $field) use ($key) { |
|
return $this->convertField($field, $name, $key); |
|
}, array_keys($field['sub_fields']), $field['sub_fields']); |
|
} |
|
|
|
/** |
|
* Check if the field is a Flexible Content field. If so, convert all |
|
* layouts. |
|
*/ |
|
if (!empty($field['layouts']) && is_array($field['layouts'])) { |
|
$field['layouts'] = array_map(function ($name, $field) use ($key) { |
|
return $this->convertField($field, $name, $key, true); |
|
}, array_keys($field['layouts']), $field['layouts']); |
|
} |
|
|
|
/** |
|
* Convert conditional logic if necessary. |
|
*/ |
|
if (!empty($field['conditional_logic'])) { |
|
$field = $this->convertConditionalLogic($field); |
|
} |
|
|
|
/** |
|
* After converting all possible sub fields, add `field_` and options |
|
* key to the key of the field. |
|
*/ |
|
if (!isset($field['key'])) { |
|
$field['key'] = 'field_' . $options['key'] . '_' . $key; |
|
} |
|
|
|
/** |
|
* Set defaults. |
|
*/ |
|
return $this->setDefaults($field, $fieldName, $isLayout); |
|
} |
|
/** |
|
* Convert fields. |
|
*/ |
|
protected function convertFields() |
|
{ |
|
if (!is_admin() || !class_exists('ACF') || empty($this->getOptions())) { |
|
return; |
|
} |
|
|
|
$fields = $this->getFields(); |
|
|
|
$this->setDefaultOptions(); |
|
|
|
$this->convertLocation(); |
|
|
|
$convertedFields = array_map(function ($name, $field) { |
|
return $this->convertField($field, $name); |
|
}, array_keys($fields), $fields); |
|
|
|
$this->setFields($convertedFields); |
|
|
|
return $this->getOptions(); |
|
} |
|
} |