/** * Get meta type so we know where the data should be saved. * * @return string */ protected function get_meta_type() { if ($current_filter = current_filter()) { return papi_get_meta_type(explode('_', $current_filter)[1]); } return papi_get_meta_type(); }
/** * Get meta type value. * * @param Papi_Entry_Type $entry_type * * @return string */ protected function get_meta_type_value($entry_type) { if (in_array($entry_type->get_type(), ['attachment'], true)) { return $entry_type->get_type(); } switch (papi_get_meta_type($entry_type->get_type())) { case 'post': return implode(', ', $entry_type->post_type); case 'term': return implode(', ', $entry_type->taxonomy); default: return 'n/a'; } }
/** * Add custom body class when it's a page type. * * @param string $classes * * @return string */ public function admin_body_class($classes) { $classes .= sprintf(' papi-meta-type-%s', papi_get_meta_type()); if (!in_array($this->post_type, papi_get_post_types(), true)) { return $classes; } if ($entry_type = $this->get_entry_type()) { $arr = $entry_type->get_body_classes(); $arr = is_string($arr) ? [$arr] : $arr; $arr = is_array($arr) ? $arr : []; $classes .= ' ' . implode(' ', $arr); } return $classes; }
/** * Add custom body class when it's a page type. * * @param string $classes * * @return string */ public function admin_body_class($classes) { $classes .= sprintf(' papi-meta-type-%s', papi_get_meta_type()); // Add custom css classes from entry type. if ($entry_type = $this->get_entry_type()) { $arr = $entry_type->get_body_classes(); $arr = is_string($arr) ? [$arr] : $arr; $arr = is_array($arr) ? $arr : []; $classes .= ' ' . implode(' ', $arr); } // Add custom css classes from query string. if ($css = papi_get_qs('papi_css')) { $css = is_array($css) ? $css : []; $css = array_map('sanitize_text_field', $css); $classes .= ' ' . implode(' ', $css); } return $classes; }
/** * Save properties with a post id of zero. */ public function save_properties() { if ($_SERVER['REQUEST_METHOD'] !== 'POST' || papi_get_meta_type() !== 'option') { return; } // Check if our nonce is vailed. if (!wp_verify_nonce(papi_get_sanitized_post('papi_meta_nonce'), 'papi_save_data')) { return; } // Get properties data. $data = $this->get_post_data(); // Prepare properties data. $data = $this->prepare_properties_data($data, 0); foreach ($data as $key => $value) { papi_update_property_meta_value(['id' => 0, 'slug' => $key, 'type' => 'option', 'value' => $value]); } /** * Fire `save_properties` action when all is done. * * @param int $id * @param string $meta_type */ do_action('papi/save_properties', 0, 'option'); }
/** * Prepare properties data for saving. * * @param array $data * @param int $post_id * * @return array */ protected function prepare_properties_data(array $data = [], $post_id = 0) { // Since we are storing witch property it is in the `$data` array // we need to remove that and set the property type to the property // and make a array of the property type and the value. foreach ($data as $key => $value) { if (papi_is_property_type_key($key)) { continue; } $property_type_key = papify(papi_get_property_type_key($key)); // Check if value exists. if (!isset($data[$key]) && !isset($data[$property_type_key])) { continue; } // Pair property value with property type object. $data[$key] = ['type' => $data[$property_type_key], 'value' => $value]; // Remove property type object since it's not needed anymore. unset($data[$property_type_key]); } foreach ($data as $key => $item) { if (papi_is_property_type_key($key)) { continue; } $property = papi_get_property_type($item['type']); unset($data[$key]); if (papi_is_property($property)) { // Run `update_value` method on the property class. $data[$key] = $property->update_value($item['value'], unpapify($key), $post_id); // Apply `update_value` filter so this can be changed from // the theme for specified property type. $data[$key] = papi_filter_update_value($item['type']->type, $data[$key], unpapify($key), $post_id, papi_get_meta_type()); if ($item['type']->overwrite) { $slug = unpapify($key); $this->overwrite[$slug] = $data[$key]; unset($data[$key]); } } } return $data; }
/** * Update property values on the post with the given post id * or update property values on the option page. * * @param array $meta * * @return bool */ function papi_update_property_meta_value(array $meta = []) { $meta = array_merge(['id' => 0, 'slug' => '', 'type' => 'post', 'value' => ''], $meta); $meta = (object) $meta; $meta->type = papi_get_meta_type($meta->type); $save_value = true; // Set the right update value function for the type. $update_value_fn = $meta->type === 'option' ? 'update_option' : 'update_metadata'; /** * Change update function. * * @param string $update_value_fn */ $update_value_fn = apply_filters('papi/core/update_value_fn', $update_value_fn); // Check so the function is callable before using it. if (!is_callable($update_value_fn)) { return; } // Check for string keys in the array if any. foreach (papi_to_array($meta->value) as $key => $value) { if (is_string($key)) { $save_value = false; break; } } // If main value shouldn't be saved it should be array. if (!$save_value && is_array($meta->value)) { $meta->value = [$meta->value]; } // Delete saved value if empty. if (papi_is_empty($meta->value)) { return papi_delete_property_meta_value($meta->id, $meta->slug, $meta->type); } $result = true; foreach (papi_to_array($meta->value) as $key => $value) { // Delete saved value if value is empty. if (papi_is_empty($value) || $value === '[]' || $value === '{}') { return papi_delete_property_meta_value($meta->id, $meta->slug, $meta->type); } // Delete main value cache. papi_cache_delete($meta->slug, $meta->id, $meta->type); // If not a array we can save the value. if (!is_array($value)) { if ($save_value) { $value = $meta->value; } if (papi_get_meta_type($meta->type) === 'option') { $out = call_user_func_array($update_value_fn, [unpapify($meta->slug), $value]); $result = $out ? $result : $out; } else { $out = call_user_func_array($update_value_fn, [$meta->type, $meta->id, unpapify($meta->slug), $value]); $result = $out ? $result : $out; } continue; } // Delete all child value caches. papi_update_property_meta_value_cache_delete($meta, $value); // Update metadata or option value for all child values. foreach ($value as $child_key => $child_value) { if (papi_is_empty($child_value)) { papi_delete_property_meta_value($meta->id, $child_key, $meta->type); } else { if (papi_get_meta_type($meta->type) === 'option') { call_user_func_array($update_value_fn, [unpapify($child_key), $child_value]); } else { call_user_func_array($update_value_fn, [$meta->type, $meta->id, unpapify($child_key), $child_value]); } } } } return $result; }
/** * Update value before it's saved to the database. * * @param mixed $values * @param string $repeater_slug * @param int $post_id * * @return array */ public function update_value($values, $repeater_slug, $post_id) { $rows = intval(papi_get_property_meta_value($post_id, $repeater_slug)); if (!is_array($values)) { $values = []; } list($results, $trash) = $this->get_results($rows, $repeater_slug, $post_id); // Delete trash values. foreach ($trash as $meta) { papi_delete_property_meta_value($post_id, $meta->meta_key); } $values = papi_to_property_array_slugs($values, $repeater_slug); foreach ($values as $slug => $value) { if (papi_is_property_type_key($slug)) { continue; } $property_type_slug = papi_get_property_type_key_f($slug); if (!isset($values[$property_type_slug])) { continue; } // Get real property slug $property_slug = $this->get_child_slug($repeater_slug, $slug); // Get property type $property_type_value = $values[$property_type_slug]->type; $property_type = papi_get_property_type($property_type_value); // Unserialize if needed. $value = papi_maybe_json_decode(maybe_unserialize($value)); // Run update value on each property type class. $value = $property_type->update_value($value, $property_slug, $post_id); // Run update value on each property type filter. $values[$slug] = papi_filter_update_value($property_type_value, $value, $property_slug, $post_id, papi_get_meta_type()); if (is_array($values[$slug])) { foreach ($values[$slug] as $key => $val) { if (!is_string($key)) { continue; } unset($values[$slug][$key]); $key = preg_replace('/^\\_/', '', $key); $values[$slug][$key] = $val; } } } // Find out which keys that should be deleted. $trash = array_diff(array_keys(papi_to_array($results)), array_keys(papi_to_array($values))); // Delete unwanted (trash) values. foreach (array_keys($trash) as $trash_key) { papi_delete_property_meta_value($post_id, $trash_key); } // It's safe to remove all rows in the database here. $this->remove_repeater_rows($post_id, $repeater_slug); // Remove unnecessary property type keys if any is left. foreach (array_keys($values) as $slug) { if (papi_is_property_type_key($slug)) { unset($values[$slug]); } } return $values; }
/** * Switch page type if all checks pass. * * @param int $post_id * @param WP_post $post */ public function save_post($post_id, $post) { // Check if post id and post object is empty or not. if (empty($post_id) || empty($post)) { return false; } // Check if our nonce is vailed. if (!wp_verify_nonce(papi_get_sanitized_post('papi_meta_nonce'), 'papi_save_data')) { return false; } // Check if so both page type keys exists. if (empty($_POST[papi_get_page_type_key()]) || empty($_POST[papi_get_page_type_key('switch')])) { return false; } // Page type information. $page_type_id = sanitize_text_field($_POST[papi_get_page_type_key()]); $page_type_switch_id = sanitize_text_field($_POST[papi_get_page_type_key('switch')]); // Don't update if the same ids. if ($page_type_id === $page_type_switch_id) { return false; } $page_type = papi_get_entry_type_by_id($page_type_id); $page_type_switch = papi_get_entry_type_by_id($page_type_switch_id); $post_type_object = get_post_type_object(papi_get_post_type()); // Check if page type and post type is not empty. if (empty($page_type_switch) || empty($post_type_object)) { return false; } // Check if autosave. if (wp_is_post_autosave($post_id)) { return false; } // Check if revision. if (wp_is_post_revision($post_id)) { return false; } // Check if revision post type. if (in_array($post->post_type, ['revision', 'nav_menu_item'], true)) { return false; } // Check so page type has the post type. if (!$page_type->has_post_type($post->post_type) || !$page_type_switch->has_post_type($post->post_type)) { return false; } // Check page type capabilities. if (!papi_current_user_is_allowed($page_type_switch->capabilities)) { return false; } // Check so user can edit posts and that the user can publish posts on the post type. if (!current_user_can('edit_post', $post_id) || !current_user_can($post_type_object->cap->publish_posts)) { return false; } // Get properties. $properties = $page_type->get_properties(); $properties_switch = $page_type_switch->get_properties(); // Delete only properties that don't have the same type and slug. foreach ($properties as $property) { $delete = true; // Check if the properties are the same or not. foreach ($properties_switch as $property_switch) { if ($property_switch->type === $property->type && $property_switch->match_slug($property->get_slug())) { $delete = false; break; } } if (!$delete) { continue; } // Delete property values. $property->delete_value($property->get_slug(true), $post_id, papi_get_meta_type()); } // Update page type id. return papi_set_page_type_id($post_id, $page_type_switch_id); }
/** * Get entry type id. * * @param int $id * @param string $type * * @return string */ function papi_get_entry_type_id($id = 0, $type = null) { $type = papi_get_meta_type($type); $id = papi_get_meta_id($type, $id); if ($id > 0) { if ($meta_value = get_metadata($type, $id, papi_get_page_type_key(), true)) { return $meta_value; } } $entry_type_id = papi_get_qs('entry_type'); /** * Change entry type id. * * @param string $entry_type_id * @param string $type * * @return string */ return apply_filters('papi/entry_type_id', $entry_type_id, $type); }
/** * Register property with: * * - `register_meta` (WP 4.6+) * * @param string $type * * @return bool */ public function register($type = 'post') { if (version_compare(get_bloginfo('version'), '4.6', '<')) { return false; } $type = papi_get_meta_type($type); // Register option fields with the new `register_setting` function and only for WordPress 4.7. if ($type === 'option' && version_compare(get_bloginfo('version'), '4.7', '>=')) { // The `type` will be the same for each fields, this is just to get it out // to the REST API, the output will be different for different fields and are // handled later on. return register_setting('papi', $this->get_slug(true), ['sanitize_callback' => [$this, 'register_meta_sanitize_callback'], 'show_in_rest' => $this->get_option('show_in_rest'), 'type' => 'string']); } // Register meta fields with the new `register_meta` function. // The `type` will be the same for each fields, this is just to get it out // to the REST API, the output will be different for different fields and are // handled later on. return register_meta($type, $this->get_slug(true), ['auth_callback' => $this->get_option('auth_callback'), 'description' => $this->get_option('description'), 'sanitize_callback' => [$this, 'register_meta_sanitize_callback'], 'show_in_rest' => $this->get_option('show_in_rest'), 'single' => $this->get_convert_type() !== 'array', 'type' => 'string']); }
/** * Format the value of the property before it's returned * to WordPress admin or the site. * * @param mixed $values * @param string $repeater_slug * @param int $post_id * * @return array */ public function format_value($values, $repeater_slug, $post_id) { if (!is_array($values)) { return []; } foreach ($values as $index => $layout) { foreach ($layout as $slug => $value) { if (is_string($value) && preg_match($this->layout_value_regex, $value)) { if (isset($values[$index][$this->layout_key])) { unset($values[$index][$slug]); continue; } $values[$index][$this->layout_key] = $value; unset($values[$index][$slug]); continue; } if (papi_is_property_type_key($slug)) { continue; } $property_type_slug = papi_get_property_type_key_f($slug); if (!isset($values[$index][$property_type_slug])) { continue; } $property_type_value = $values[$index][$property_type_slug]; $property_type = papi_get_property_type($property_type_value); if (!is_object($property_type)) { continue; } // Get property child slug. $child_slug = $this->get_child_slug($repeater_slug, $slug); // Create cache key. $cache_key = sprintf('%s_%d_%s', $repeater_slug, $index, $slug); // Get raw value from cache if enabled. if ($this->cache) { $raw_value = papi_cache_get($cache_key, $post_id, $this->get_meta_type()); } else { $raw_value = false; } // Load the value. if ($raw_value === null || $raw_value === false) { $values[$index][$slug] = $property_type->load_value($value, $child_slug, $post_id); $values[$index][$slug] = papi_filter_load_value($property_type->type, $values[$index][$slug], $child_slug, $post_id, papi_get_meta_type()); if (!papi_is_empty($values[$index][$slug]) && $this->cache) { papi_cache_set($cache_key, $post_id, $values[$index][$slug], $this->get_meta_type()); } } else { $values[$index][$slug] = $raw_value; } if (strtolower($property_type->type) === 'repeater') { $property_type->cache = false; } // Format the value from the property class. $values[$index][$slug] = $property_type->format_value($values[$index][$slug], $child_slug, $post_id); if (!is_admin()) { $values[$index][$slug] = papi_filter_format_value($property_type->type, $values[$index][$slug], $child_slug, $post_id, papi_get_meta_type()); } $values[$index][$property_type_slug] = $property_type_value; } } if (!is_admin()) { foreach ($values as $index => $row) { foreach ($row as $slug => $value) { if (is_string($value) && preg_match($this->layout_value_regex, $value)) { unset($values[$index][$slug]); $values[$index]['_layout'] = preg_replace($this->layout_value_regex, '', $value); } if (papi_is_property_type_key($slug)) { unset($values[$index][$slug]); } if (papi_is_empty($value)) { unset($values[$index][$slug]); } } } } return $values; }
/** * Check if it's a option page or not. * * @return bool */ public function is_option_page() { return $this->get_store() instanceof Papi_Option_Store || papi_get_meta_type() === 'option'; }
/** * Setup action hooks. */ protected function setup_actions() { if (post_type_exists($this->get_post_type()) && papi_get_meta_type() === 'post') { add_action('add_meta_boxes', [$this, 'setup_meta_box']); if ($this->box->context === 'after_title') { add_action('edit_form_after_title', [$this, 'move_meta_box_after_title']); } } else { $this->setup_meta_box(); } // Will be called on when you call do_meta_boxes // even without a real post type. add_action(sprintf('postbox_classes_%s_%s', strtolower($this->get_post_type()), $this->box->id), [$this, 'meta_box_css_classes']); }
/** * Get property value. * * @param Papi_Core_Conditional_Rule $rule * * @return mixed */ private function get_value(Papi_Core_Conditional_Rule $rule) { if (papi_doing_ajax()) { $source = $rule->get_source(); $meta_id = papi_get_meta_id(); $entry_type = papi_get_entry_type_by_meta_id($meta_id); if (!papi_is_empty($source) && $entry_type instanceof Papi_Entry_Type !== false) { if (papi_is_property($entry_type->get_property($rule->slug))) { return $this->get_deep_value($rule->slug, $source); } } } if (!papi_is_empty($rule->get_source())) { return $this->get_deep_value($rule->slug, $rule->get_source()); } $slug = $rule->get_field_slug(); $type = papi_get_meta_type(); $value = papi_get_field($slug, null, $type); return $this->get_deep_value($slug, $value); }
/** * Get store from factory. * * @param int $post_id * @param string $type * * @return mixed */ public static function factory($post_id, $type = 'post') { $type = papi_get_meta_type($type); $class_suffix = '_' . ucfirst($type) . '_Store'; $class_name = 'Papi' . $class_suffix; if (!class_exists($class_name)) { return; } $post_id = papi_get_post_id($post_id); $page = new $class_name($post_id); if (!$page->valid()) { return; } return $page; }
/** * Add custom table column to page or taxonomy type. * * @param string $column_name * @param int $post_id * @param int $term_id */ public function manage_page_type_posts_custom_column($column_name, $post_id, $term_id = null) { if (!in_array($this->post_type, papi_get_post_types(), true) && !in_array($this->taxonomy, papi_get_taxonomies(), true)) { return; } /** * Hide column for post type. Default is false. * * @param string $post_type */ if (apply_filters('papi/settings/column_hide_' . $this->get_meta_type_value(), false)) { return; } // Column name most be `entry_type`. On taxomy the column name is `post_id` variable. if ($column_name !== 'entry_type' && $post_id !== 'entry_type') { return; } // Get the entry type for the post or term. $entry_type = papi_get_entry_type_by_meta_id(is_numeric($post_id) ? $post_id : $term_id, papi_get_meta_type()); if (!is_null($entry_type)) { echo esc_html($entry_type->name); } else { $post = !empty($this->post_type) && empty($this->taxonomy); $arg = $post ? papi_get_post_type() : papi_get_taxonomy(); $type = $post ? 'page' : 'taxonomy'; echo esc_html(call_user_func_array("papi_filter_settings_standard_{$type}_type_name", [$arg])); } }
/** * Get meta type from the store or the default one. * * @return string */ public function get_meta_type() { $store = $this->get_store(); return $store ? $store->get_type() : papi_get_meta_type(); }
/** * Get right meta id column for a meta type. * * @param string $type * * @return string|null */ function papi_get_meta_id_column($type = 'post') { if ($type = papi_get_meta_type($type)) { return sprintf('%s_id', $type); } }