├── .babelrc ├── .editorconfig ├── CHANGELOG.md ├── CODE_OF_CONDUCT.md ├── LICENSE ├── README.md ├── composer.json ├── dist ├── css │ ├── style.css.map │ ├── style.min.css │ └── style.min.css.map ├── images │ ├── logotype.png │ └── plus-minus.png └── js │ ├── main.min.js │ └── main.min.js.map ├── docker-compose.yml ├── languages ├── papi-fr_FR.mo ├── papi-fr_FR.po ├── papi-sv_SE.mo ├── papi-sv_SE.po └── papi.pot ├── package-lock.json ├── papi-loader.php └── src ├── admin ├── class-papi-admin-ajax.php ├── class-papi-admin-assets.php ├── class-papi-admin-columns.php ├── class-papi-admin-entry-post.php ├── class-papi-admin-entry-taxonomy.php ├── class-papi-admin-entry.php ├── class-papi-admin-menu.php ├── class-papi-admin-meta-box-tabs.php ├── class-papi-admin-meta-box.php ├── class-papi-admin-meta-handler.php ├── class-papi-admin-option-handler.php ├── class-papi-admin-page-type-switcher.php ├── class-papi-admin-view.php ├── class-papi-admin.php └── views │ ├── add-new-page.php │ └── partials │ └── add-new-item.php ├── cli ├── class-papi-cli-command.php ├── class-papi-cli-post-command.php ├── class-papi-cli-term-command.php ├── class-papi-cli-type-command.php └── class-papi-cli.php ├── core ├── class-papi-core-autoload.php ├── class-papi-core-box.php ├── class-papi-core-conditional-rule.php ├── class-papi-core-conditional-rules.php ├── class-papi-core-conditional.php ├── class-papi-core-container.php ├── class-papi-core-data-handler.php ├── class-papi-core-data.php ├── class-papi-core-meta-store.php ├── class-papi-core-property.php ├── class-papi-core-tab.php └── class-papi-core-type.php ├── lib ├── core │ ├── cache.php │ ├── conditional.php │ ├── data.php │ ├── deprecated.php │ ├── io.php │ ├── meta.php │ ├── post.php │ ├── property.php │ ├── slug.php │ ├── tabs.php │ ├── taxonomy.php │ ├── template.php │ ├── url.php │ └── utilities.php ├── fields │ ├── option.php │ ├── page.php │ └── taxonomy.php ├── hooks │ ├── actions.php │ ├── filters-page-type.php │ ├── filters-taxonomy-type.php │ └── filters.php └── types │ ├── entry.php │ ├── page.php │ └── taxonomy.php ├── papi-loader.php ├── properties ├── class-papi-property-bool.php ├── class-papi-property-checkbox.php ├── class-papi-property-color.php ├── class-papi-property-datetime.php ├── class-papi-property-divider.php ├── class-papi-property-dropdown.php ├── class-papi-property-editor.php ├── class-papi-property-email.php ├── class-papi-property-file.php ├── class-papi-property-flexible.php ├── class-papi-property-gallery.php ├── class-papi-property-group.php ├── class-papi-property-hidden.php ├── class-papi-property-html.php ├── class-papi-property-image.php ├── class-papi-property-link.php ├── class-papi-property-module.php ├── class-papi-property-number.php ├── class-papi-property-post.php ├── class-papi-property-radio.php ├── class-papi-property-reference.php ├── class-papi-property-relationship.php ├── class-papi-property-repeater.php ├── class-papi-property-sidebar.php ├── class-papi-property-string.php ├── class-papi-property-table.php ├── class-papi-property-term.php ├── class-papi-property-text.php ├── class-papi-property-url.php ├── class-papi-property-user.php └── class-papi-property.php ├── query └── class-papi-query.php ├── rest-api ├── class-papi-rest-api-post.php ├── class-papi-rest-api-settings.php └── class-papi-rest-api.php ├── stores ├── class-papi-option-store.php ├── class-papi-post-store.php └── class-papi-term-store.php └── types ├── class-papi-attachment-type.php ├── class-papi-entry-type.php ├── class-papi-front-page-type.php ├── class-papi-module-type.php ├── class-papi-option-type.php ├── class-papi-page-type.php └── class-papi-taxonomy-type.php /.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": ["@jitesoft/main"] 3 | } 4 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | charset = utf-8 5 | end_of_line = lf 6 | indent_style = space 7 | indent_size = 2 8 | insert_final_newline = true 9 | trim_trailing_whitespace = true 10 | 11 | [*.php] 12 | indent_style = tab 13 | indent_size = 4 14 | 15 | [*.md] 16 | trim_trailing_whitespace = false 17 | 18 | [Makefile] 19 | indent_style = tab 20 | indent_size = 8 21 | 22 | [*.{js,jsx,ts,tsx,vue}] 23 | quote_type = single 24 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Contributor Covenant Code of Conduct 2 | 3 | ## Our Pledge 4 | 5 | In the interest of fostering an open and welcoming environment, we as 6 | contributors and maintainers pledge to making participation in our project and 7 | our community a harassment-free experience for everyone, regardless of age, body 8 | size, disability, ethnicity, gender identity and expression, level of experience, 9 | nationality, personal appearance, race, religion, or sexual identity and 10 | orientation. 11 | 12 | ## Our Standards 13 | 14 | Examples of behavior that contributes to creating a positive environment 15 | include: 16 | 17 | * Using welcoming and inclusive language 18 | * Being respectful of differing viewpoints and experiences 19 | * Gracefully accepting constructive criticism 20 | * Focusing on what is best for the community 21 | * Showing empathy towards other community members 22 | 23 | Examples of unacceptable behavior by participants include: 24 | 25 | * The use of sexualized language or imagery and unwelcome sexual attention or 26 | advances 27 | * Trolling, insulting/derogatory comments, and personal or political attacks 28 | * Public or private harassment 29 | * Publishing others' private information, such as a physical or electronic 30 | address, without explicit permission 31 | * Other conduct which could reasonably be considered inappropriate in a 32 | professional setting 33 | 34 | ## Our Responsibilities 35 | 36 | Project maintainers are responsible for clarifying the standards of acceptable 37 | behavior and are expected to take appropriate and fair corrective action in 38 | response to any instances of unacceptable behavior. 39 | 40 | Project maintainers have the right and responsibility to remove, edit, or 41 | reject comments, commits, code, wiki edits, issues, and other contributions 42 | that are not aligned to this Code of Conduct, or to ban temporarily or 43 | permanently any contributor for other behaviors that they deem inappropriate, 44 | threatening, offensive, or harmful. 45 | 46 | ## Scope 47 | 48 | This Code of Conduct applies both within project spaces and in public spaces 49 | when an individual is representing the project or its community. Examples of 50 | representing a project or community include using an official project e-mail 51 | address, posting via an official social media account, or acting as an appointed 52 | representative at an online or offline event. Representation of a project may be 53 | further defined and clarified by project maintainers. 54 | 55 | ## Enforcement 56 | 57 | Instances of abusive, harassing, or otherwise unacceptable behavior may be 58 | reported by contacting the project team at [INSERT EMAIL ADDRESS]. All 59 | complaints will be reviewed and investigated and will result in a response that 60 | is deemed necessary and appropriate to the circumstances. The project team is 61 | obligated to maintain confidentiality with regard to the reporter of an incident. 62 | Further details of specific enforcement policies may be posted separately. 63 | 64 | Project maintainers who do not follow or enforce the Code of Conduct in good 65 | faith may face temporary or permanent repercussions as determined by other 66 | members of the project's leadership. 67 | 68 | ## Attribution 69 | 70 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, 71 | available at [http://contributor-covenant.org/version/1/4][version] 72 | 73 | [homepage]: http://contributor-covenant.org 74 | [version]: http://contributor-covenant.org/version/1/4/ 75 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2013-2019 Fredrik Forsmo 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of 6 | this software and associated documentation files (the "Software"), to deal in 7 | the Software without restriction, including without limitation the rights to 8 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 9 | the Software, and to permit persons to whom the Software is furnished to do so, 10 | subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 17 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 18 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 19 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 20 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Papi 2 | 3 | ![Papi](https://cloud.githubusercontent.com/assets/14610/9073902/16a6d906-3b05-11e5-9287-5644a96e9a82.png) 4 | 5 | [![Latest Version](https://img.shields.io/github/release/wp-papi/papi.svg?style=flat)](https://github.com/wp-papi/papi/releases) 6 | [![License](https://img.shields.io/packagist/l/wp-papi/papi.svg)](https://packagist.org/packages/wp-papi/papi) 7 | [![Gitter](https://badges.gitter.im/wp-papi/papi.svg)](https://gitter.im/wp-papi/papi?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge) 8 | [![No Maintenance Intended](http://unmaintained.tech/badge.svg)](http://unmaintained.tech/) 9 | 10 | > `master` is unsafe. `3.x` is the stable branch. 11 | 12 | Papi has a different approach on how to work with fields and page types in WordPress. The idea is coming from how Page Type Builder in EPiServer works and has been loved by the developers. 13 | 14 | So we though why don’t use the same approach in WordPress? Papi is today running in production and has been easy to work with when it came to add new fields. Papi don’t have any admin user interface where you should add all fields, we use classes in PHP, where one class represents one page type and in your class you add all fields you need. It’s that easy! 15 | 16 | [Visit Papi’s project page](https://wp-papi.github.io/) 17 | 18 | ## A message from the author 19 | 20 | v3.2.0 will likely be the last release I plan to work on as the core maintainer of Papi since my focus has shifted from WordPress to doing more JavaScript and Go projects. I hope you understand my decision to step back from the project, if you have any questions or would be interested in take over some of the maintenance of the project please let me know. I will still be around answering questions and helping any new maintainers. Some bug fixes and/or pull request may be added (but no new versions) since me and my colleagues use Papi internally and will be continuing doing it. 21 | 22 | ## Installation 23 | 24 | If you're using Composer to manage WordPress, add Papi to your project's dependencies. Run: 25 | 26 | ```sh 27 | composer require wp-papi/papi 28 | ``` 29 | 30 | Or manually add it to your `composer.json`: 31 | 32 | ```json 33 | "require": { 34 | "php": "^^7.4", 35 | "wordpress": "^4.6", 36 | "wp-papi/papi": "^3.2" 37 | } 38 | ``` 39 | 40 | ## Build CSS and JavaScript 41 | 42 | Install dependencies: 43 | 44 | ``` 45 | make deps 46 | ``` 47 | 48 | Build CSS: 49 | 50 | ``` 51 | make css 52 | ``` 53 | 54 | Build JavaScript: 55 | 56 | ``` 57 | make js 58 | ``` 59 | 60 | ## Testing 61 | 62 | Visit the [readme](tests/README.md) file for testing. 63 | 64 | ## Coding style 65 | 66 | You can check if your contribution passes the styleguide by installing [PHP CodeSniffer](https://github.com/squizlabs/PHP_CodeSniffer) and running the following in your Papi directory: 67 | 68 | ``` 69 | make lint:php 70 | ``` 71 | 72 | ## Contributing 73 | 74 | Visit the [contributing](CONTRIBUTING.md) file. 75 | 76 | ## Security 77 | 78 | If you discover a security vulnerability within this package, please send an e-mail to Fredrik Forsmo at security@frozzare.com. All security vulnerabilities will be promptly addressed. 79 | 80 | ## License 81 | 82 | MIT © [Fredrik Forsmo](https://github.com/frozzare) 83 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "wp-papi/papi", 3 | "type": "wordpress-plugin", 4 | "license": "MIT", 5 | "description": "WordPress Page Type API with custom fields", 6 | "homepage": "https://wp-papi.github.io", 7 | "authors": [{ 8 | "name": "Fredrik Forsmo", 9 | "email": "fredrik.forsmo@gmail.com", 10 | "homepage": "https://github.com/frozzare" 11 | }], 12 | "keywords": [ 13 | "wordpress", 14 | "custom fields", 15 | "page type", 16 | "page", 17 | "taxonomy", 18 | "options" 19 | ], 20 | "require": { 21 | "php": "^7.4" 22 | }, 23 | "require-dev": { 24 | "frozzare/wp-test-suite": "^1.0", 25 | "wp-coding-standards/wpcs": "^0.13" 26 | }, 27 | "scripts": { 28 | "post-install-cmd": "if [ -f vendor/bin/phpcs ]; then \"vendor/bin/phpcs\" --config-set installed_paths vendor/wp-coding-standards/wpcs; fi", 29 | "post-update-cmd": "if [ -f vendor/bin/phpcs ]; then \"vendor/bin/phpcs\" --config-set installed_paths vendor/wp-coding-standards/wpcs; fi" 30 | }, 31 | "minimum-stability": "dev", 32 | "prefer-stable": true, 33 | "extra": { 34 | "branch-alias": { 35 | "dev-master": "3.3.x-dev" 36 | } 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /dist/images/logotype.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wp-papi/papi/c6621f5acbf20aa687dc98776cf3f78a4f1d87dd/dist/images/logotype.png -------------------------------------------------------------------------------- /dist/images/plus-minus.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wp-papi/papi/c6621f5acbf20aa687dc98776cf3f78a4f1d87dd/dist/images/plus-minus.png -------------------------------------------------------------------------------- /docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: '3' 2 | 3 | services: 4 | mysql: 5 | image: mariadb 6 | environment: 7 | - MYSQL_ROOT_PASSWORD=root 8 | phpunit: 9 | build: 10 | context: ./tests 11 | volumes: 12 | - .:/var/www/html 13 | depends_on: 14 | - mysql 15 | entrypoint: bash -c "composer install && vendor/frozzare/wp-test-suite/bin/install-wp-tests.sh wordpress_test root root mysql latest && vendor/bin/phpunit" 16 | -------------------------------------------------------------------------------- /languages/papi-fr_FR.mo: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wp-papi/papi/c6621f5acbf20aa687dc98776cf3f78a4f1d87dd/languages/papi-fr_FR.mo -------------------------------------------------------------------------------- /languages/papi-sv_SE.mo: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wp-papi/papi/c6621f5acbf20aa687dc98776cf3f78a4f1d87dd/languages/papi-sv_SE.mo -------------------------------------------------------------------------------- /papi-loader.php: -------------------------------------------------------------------------------- 1 | __( 'Close', 'default' ), 52 | 'edit' => __( 'Edit', 'default' ), 53 | 'new' => __( 'New', 'default' ), 54 | 'remove' => __( 'Remove', 'default' ), 55 | 'requiredError' => __( 'This fields are required:', 'papi' ), 56 | ] ); 57 | } 58 | } 59 | 60 | if ( papi_is_admin() ) { 61 | new Papi_Admin_Assets; 62 | } 63 | -------------------------------------------------------------------------------- /src/admin/class-papi-admin-entry-post.php: -------------------------------------------------------------------------------- 1 | post_type = papi_get_post_type(); 17 | 18 | $this->setup_actions(); 19 | $this->setup_filters(); 20 | } 21 | 22 | /** 23 | * Filters the fields displayed in the post revision diff UI. 24 | * 25 | * @param array $return 26 | * @param WP_Post $compare_from 27 | * @param WP_Post $compare_to 28 | * 29 | * @return array 30 | */ 31 | public function get_revision_ui_diff( $return, $compare_from, $compare_to ) { 32 | $meta = get_post_meta( $compare_from->ID ); 33 | $meta = is_array( $meta ) ? $meta : []; 34 | 35 | foreach ( $meta as $key => $value ) { 36 | if ( $key[0] === '_' && $key !== papi_get_page_type_key() ) { 37 | continue; 38 | } 39 | 40 | $content_from = papi_data_get( $compare_from->ID, $key ); 41 | $content_to = papi_data_get( $compare_to->ID, $key ); 42 | 43 | $diff = wp_text_diff( $content_from, $content_to, [ 44 | 'show_split_view' => true 45 | ] ); 46 | 47 | if ( $diff ) { 48 | $return[] = [ 49 | 'id' => $key, 50 | 'name' => $key, 51 | 'diff' => $diff, 52 | ]; 53 | } 54 | } 55 | 56 | return $return; 57 | } 58 | 59 | /** 60 | * Output hidden meta boxes. 61 | */ 62 | public function hidden_meta_boxes() { 63 | global $_wp_post_type_features; 64 | 65 | if ( isset( $_wp_post_type_features[$this->post_type]['editor'] ) ) { 66 | return; 67 | } 68 | 69 | add_meta_box( 'papi-hidden-editor', 'Papi hidden editor', [$this, 'hidden_meta_box_editor'], $this->post_type ); 70 | } 71 | 72 | /** 73 | * Output hidden WordPress editor. 74 | */ 75 | public function hidden_meta_box_editor() { 76 | wp_editor( '', 'papiHiddenEditor' ); 77 | } 78 | 79 | /** 80 | * Load post new action 81 | * Redirect to right url if no page type is set. 82 | */ 83 | public function load_post_new() { 84 | $request_uri = $_SERVER['REQUEST_URI']; 85 | $post_types = papi_get_post_types(); 86 | 87 | if ( in_array( $this->post_type, $post_types, true ) && strpos( $request_uri, 'page_type=' ) === false ) { 88 | $parsed_url = parse_url( $request_uri ); 89 | 90 | $only_page_type = papi_filter_settings_only_page_type( $this->post_type ); 91 | $page_types = papi_get_all_page_types( $this->post_type ); 92 | $show_standard = false; 93 | 94 | if ( count( $page_types ) === 1 && empty( $only_page_type ) ) { 95 | $show_standard = $page_types[0]->standard_type; 96 | $show_standard = $show_standard ? papi_filter_settings_show_standard_page_type( $this->post_type ) : $show_standard; 97 | $only_page_type = $show_standard ? '' : $page_types[0]->get_id(); 98 | } 99 | 100 | // Check if we should show one post type or not and 101 | // create the right url for that. 102 | if ( ! empty( $only_page_type ) && ! $show_standard ) { 103 | $url = papi_get_page_new_url( $only_page_type, false ); 104 | } else { 105 | $page = 'page=papi-add-new-page,' . $this->post_type; 106 | 107 | if ( $this->post_type !== 'post' ) { 108 | $page = '&' . $page; 109 | } 110 | 111 | $url = 'edit.php?' . $parsed_url['query'] . $page; 112 | } 113 | 114 | wp_safe_redirect( $url ); 115 | papi_is_admin() && exit; 116 | } 117 | } 118 | 119 | /** 120 | * Redirect post location when post is in iframe mode. 121 | * 122 | * @param string $location 123 | * 124 | * @return string 125 | */ 126 | public function redirect_post_location( $location ) { 127 | if ( ! isset( $_SERVER['HTTP_REFERER'] ) ) { 128 | return $location; 129 | } 130 | 131 | $referer = $_SERVER['HTTP_REFERER']; 132 | $referer = strtolower( $referer ); 133 | 134 | if ( strpos( $referer, 'papi-iframe-mode' ) === false ) { 135 | return $location; 136 | } 137 | 138 | return sprintf( '%s&papi_css[]=papi-iframe-mode', $location ); 139 | } 140 | 141 | /** 142 | * Setup admin entry. 143 | */ 144 | public function setup() { 145 | // Preload all page types. 146 | foreach ( papi_get_post_types() as $post_type ) { 147 | papi_get_all_entry_types( [ 148 | 'args' => $post_type 149 | ] ); 150 | } 151 | 152 | return ! in_array( $this->post_type, ['revision', 'nav_menu_item'], true ); 153 | } 154 | 155 | /** 156 | * Setup actions. 157 | */ 158 | protected function setup_actions() { 159 | add_action( 'load-post-new.php', [$this, 'load_post_new'] ); 160 | add_action( 'add_meta_boxes', [$this, 'hidden_meta_boxes'], 10 ); 161 | add_action( 'redirect_post_location', [$this, 'redirect_post_location'] ); 162 | } 163 | 164 | /** 165 | * Setup filters. 166 | */ 167 | protected function setup_filters() { 168 | add_filter( 'wp_get_revision_ui_diff', [$this, 'get_revision_ui_diff'], 10, 3 ); 169 | } 170 | } 171 | 172 | if ( papi_is_admin() ) { 173 | Papi_Admin_Entry_Post::instance(); 174 | } 175 | -------------------------------------------------------------------------------- /src/admin/class-papi-admin-entry-taxonomy.php: -------------------------------------------------------------------------------- 1 | setup_actions(); 20 | } 21 | 22 | /** 23 | * Add form fields to edit tags page. 24 | */ 25 | public function add_form_fields() { 26 | $html_name = esc_attr( papi_get_page_type_key() ); 27 | $taxonomy = papi_get_qs( 'taxonomy' ); 28 | $taxonomy_object = get_taxonomy( $taxonomy ); 29 | 30 | // Get only the taxonomy types that has the taxonomy. 31 | $taxonomy_types = array_filter( $this->taxonomy_types, function ( $taxonomy_type ) use ( $taxonomy ) { 32 | return in_array( $taxonomy, $taxonomy_type->taxonomy, true ) && $taxonomy_type->display( $taxonomy ); 33 | } ); 34 | $taxonomy_types = array_values( $taxonomy_types ); 35 | 36 | // Do not display empty select if no taxonomy types. 37 | if ( empty( $taxonomy_types ) ) { 38 | return; 39 | } 40 | 41 | // Prepare taxonomy types with standard taxonomy type. 42 | $taxonomy_types = $this->prepare_taxonomy_types( $taxonomy_types ); 43 | 44 | // Render a dropdown if more than one taxonomy types 45 | // exists on the taxonomy. 46 | if ( count( $taxonomy_types ) > 1 ): 47 | ?> 48 |
49 | 52 | 63 |
64 | $taxonomy_types[0]->redirect_after_create, 68 | 'data-papi-page-type-key' => true, 69 | 'name' => esc_attr( $html_name ), 70 | 'type' => 'hidden', 71 | 'value' => esc_attr( $taxonomy_types[0]->get_id() ) 72 | ] ); 73 | endif; 74 | } 75 | 76 | /** 77 | * Prepare taxonomy types, add standard taxonomy if it should be added. 78 | * 79 | * @param array $taxonomy_types 80 | * 81 | * @return array 82 | */ 83 | protected function prepare_taxonomy_types( array $taxonomy_types ) { 84 | $taxonomy = papi_get_qs( 'taxonomy' ); 85 | 86 | if ( papi_filter_settings_show_standard_taxonomy_type( $taxonomy ) ) { 87 | $id = sprintf( 'papi-standard-%s-type', $taxonomy ); 88 | $taxonomy_type = new Papi_Taxonomy_Type( $id ); 89 | $taxonomy_type->id = $id; 90 | $taxonomy_type->name = papi_filter_settings_standard_taxonomy_type_name( $taxonomy ); 91 | $taxonomy_type->taxonomy = [$taxonomy]; 92 | $taxonomy_types[] = $taxonomy_type; 93 | } 94 | 95 | usort( $taxonomy_types, function ( $a, $b ) { 96 | return strcmp( $a->name, $b->name ); 97 | } ); 98 | 99 | return papi_sort_order( array_reverse( $taxonomy_types ) ); 100 | } 101 | 102 | /** 103 | * Setup actions. 104 | */ 105 | protected function setup_actions() { 106 | add_action( 'admin_init', [$this, 'setup_taxonomies_hooks'] ); 107 | } 108 | 109 | /** 110 | * Setup hooks for all taxonomies. 111 | */ 112 | public function setup_taxonomies_hooks() { 113 | $this->taxonomy_types = papi_get_all_entry_types( [ 114 | 'types' => 'taxonomy' 115 | ] ); 116 | 117 | $taxonomies = array_reduce( $this->taxonomy_types, function ( $taxonomies, $taxonomy_type ) { 118 | return array_merge( $taxonomies, $taxonomy_type->taxonomy ); 119 | }, [] ); 120 | $taxonomies = array_unique( $taxonomies ); 121 | 122 | foreach ( $taxonomies as $taxonomy ) { 123 | if ( is_string( $taxonomy ) && taxonomy_exists( $taxonomy ) ) { 124 | add_action( $taxonomy . '_add_form_fields', [$this, 'add_form_fields'] ); 125 | } 126 | } 127 | } 128 | } 129 | 130 | if ( papi_is_admin() ) { 131 | Papi_Admin_Entry_Taxonomy::instance(); 132 | } 133 | -------------------------------------------------------------------------------- /src/admin/class-papi-admin-entry.php: -------------------------------------------------------------------------------- 1 | setup_actions(); 13 | } 14 | 15 | /** 16 | * Fill labels on admin bar. 17 | */ 18 | public function admin_bar_menu() { 19 | if ( $entry_type = $this->get_entry_type() ) { 20 | $this->override_labels( $entry_type ); 21 | } 22 | } 23 | 24 | /** 25 | * Get current type class. 26 | * 27 | * @return Papi_Entry_Type 28 | */ 29 | protected function get_entry_type() { 30 | if ( $entry_type = papi_get_entry_type_by_id( papi_get_entry_type_id() ) ) { 31 | return $entry_type; 32 | } 33 | } 34 | 35 | /** 36 | * Override labels with labels from the entry type. 37 | * 38 | * @param Papi_Entry_Type $entry_type 39 | */ 40 | protected function override_labels( Papi_Entry_Type $entry_type ) { 41 | global $wp_post_types, $wp_taxonomies; 42 | 43 | if ( $entry_type->get_type() === 'taxonomy' ) { 44 | $meta_type_value = papi_get_taxonomy(); 45 | } else { 46 | $meta_type_value = papi_get_post_type(); 47 | } 48 | 49 | if ( empty( $meta_type_value ) || ( ! isset( $wp_post_types[$meta_type_value] ) && ! isset( $wp_taxonomies[$meta_type_value] ) ) ) { 50 | return; 51 | } 52 | 53 | foreach ( $entry_type->get_labels() as $key => $value ) { 54 | if ( empty( $value ) ) { 55 | continue; 56 | } 57 | 58 | if ( $entry_type->get_type() === 'taxonomy' && isset( $wp_taxonomies[$meta_type_value]->labels->$key ) ) { 59 | $wp_taxonomies[$meta_type_value]->labels->$key = $value; 60 | } else if ( isset( $wp_post_types[$meta_type_value]->labels->$key ) ) { 61 | $wp_post_types[$meta_type_value]->labels->$key = $value; 62 | } 63 | } 64 | } 65 | 66 | /** 67 | * Page items menu. 68 | * 69 | * This function will register all entry types 70 | * that has a fake post type. Like option types. 71 | */ 72 | public function page_items_menu() { 73 | $entry_types = papi_get_all_entry_types( [ 74 | 'mode' => 'exclude', 75 | 'types' => 'page' 76 | ] ); 77 | 78 | foreach ( $entry_types as $entry_type ) { 79 | if ( empty( $entry_type->get_menu() ) || empty( $entry_type->name ) ) { 80 | continue; 81 | } 82 | 83 | $slug = sprintf( 84 | 'papi/%s/%s', 85 | $entry_type->get_type(), 86 | $entry_type->get_id() 87 | ); 88 | 89 | add_submenu_page( 90 | $entry_type->get_menu(), 91 | $entry_type->name, 92 | $entry_type->name, 93 | $entry_type->capability, 94 | $slug, 95 | [$entry_type, 'render'] 96 | ); 97 | } 98 | } 99 | 100 | /** 101 | * Setup menu items for real post types. 102 | */ 103 | public function post_types_menu() { 104 | global $submenu; 105 | 106 | $post_types = papi_get_post_types(); 107 | 108 | foreach ( $post_types as $post_type ) { 109 | if ( ! post_type_exists( $post_type ) ) { 110 | continue; 111 | } 112 | 113 | if ( $post_type === 'post' ) { 114 | $edit_url = 'edit.php'; 115 | } else { 116 | $edit_url = 'edit.php?post_type=' . $post_type; 117 | } 118 | 119 | if ( ! isset( $submenu[$edit_url], $submenu[$edit_url][10], $submenu[$edit_url][10][2] ) ) { 120 | $post_type_object = get_post_type_object( $post_type ); 121 | 122 | if ( $post_type_object->show_in_menu !== true ) { 123 | $submenu[$edit_url] = [ 124 | 10 => [ 125 | __( 'Add New', 'papi' ), 126 | 'edit_posts', 127 | 'post-new.php' 128 | ] 129 | ]; 130 | } else { 131 | continue; 132 | } 133 | } 134 | 135 | $only_page_type = papi_filter_settings_only_page_type( $post_type ); 136 | $page_types = papi_get_all_page_types( $post_type ); 137 | $show_standard = false; 138 | 139 | // Don't change menu item when no page types is found. 140 | if ( empty( $page_types ) ) { 141 | continue; 142 | } 143 | 144 | if ( count( $page_types ) === 1 && empty( $only_page_type ) ) { 145 | $show_standard = papi_filter_settings_show_standard_page_type( $post_type ); 146 | $only_page_type = $show_standard ? '' : $page_types[0]->get_id(); 147 | } 148 | 149 | if ( ! empty( $only_page_type ) && ! $show_standard ) { 150 | $submenu[$edit_url][10][2] = papi_get_page_new_url( 151 | $only_page_type, 152 | false, 153 | $post_type, 154 | [ 155 | 'post_parent', 156 | 'lang' 157 | ] 158 | ); 159 | } else { 160 | $page = 'papi-add-new-page,' . $post_type; 161 | $start = strpos( $edit_url, 'post_type' ) === false ? '?' : '&'; 162 | 163 | $submenu[$edit_url][10][2] = sprintf( 164 | '%s%spage=%s', 165 | $edit_url, 166 | $start, 167 | $page 168 | ); 169 | 170 | // Add menu item. 171 | add_menu_page( 172 | __( 'Add New', 'papi' ), 173 | __( 'Add New', 'papi' ), 174 | 'read', 175 | $page, 176 | [$this, 'render_view'] 177 | ); 178 | 179 | // Remove the menu item so it's hidden. 180 | remove_menu_page( $page ); 181 | } 182 | } 183 | } 184 | 185 | /** 186 | * Menu callback that loads right view depending on what the `page` query string says. 187 | */ 188 | public function render_view() { 189 | if ( strpos( papi_get_qs( 'page' ), 'papi' ) !== false ) { 190 | $page = str_replace( 'papi-', '', papi_get_qs( 'page' ) ); 191 | $res = preg_replace( '/\,.*/', '', $page ); 192 | 193 | if ( is_string( $res ) ) { 194 | $page_view = $res; 195 | } 196 | } 197 | 198 | if ( ! isset( $page_view ) ) { 199 | $page_view = null; 200 | } 201 | 202 | if ( ! is_null( $page_view ) ) { 203 | $view = new Papi_Admin_View; 204 | $view->render( $page_view ); 205 | } else { 206 | echo '

Papi - 404

'; 207 | } 208 | } 209 | 210 | /** 211 | * Setup actions. 212 | */ 213 | protected function setup_actions() { 214 | if ( papi_is_admin() ) { 215 | add_action( 'admin_init', [$this, 'admin_bar_menu'] ); 216 | add_action( 'admin_menu', [$this, 'page_items_menu'] ); 217 | add_action( 'admin_menu', [$this, 'post_types_menu'] ); 218 | } else { 219 | add_action( 'admin_bar_menu', [$this, 'admin_bar_menu'] ); 220 | } 221 | } 222 | } 223 | 224 | new Papi_Admin_Menu; 225 | -------------------------------------------------------------------------------- /src/admin/class-papi-admin-meta-box-tabs.php: -------------------------------------------------------------------------------- 1 | tabs = papi_tabs_setup( $tabs ); 28 | 29 | if ( $render ) { 30 | $this->html(); 31 | } 32 | } 33 | 34 | /** 35 | * Get the tabs that are registered. 36 | * 37 | * @return array 38 | */ 39 | public function get_tabs() { 40 | return $this->tabs; 41 | } 42 | 43 | /** 44 | * Generate html for tabs and properties. 45 | */ 46 | protected function html() { 47 | ?> 48 |
49 |
50 |
51 | 75 |
76 | tabs as $tab ): 78 | ?> 79 |
80 | properties ); ?> 81 |
82 | 85 |
86 |
87 |
88 | capabilities ) ) { 23 | return; 24 | } 25 | 26 | if ( $box->display ) { 27 | $this->box = $box; 28 | $this->setup_actions(); 29 | } 30 | } 31 | 32 | /** 33 | * Add custom css for hiding boxes with no frame in screen options. 34 | * 35 | * @return string 36 | */ 37 | public function admin_head() { 38 | if ( ! $this->box->frame ) { 39 | echo sprintf( 40 | '', 41 | esc_attr( $this->box->id ), 42 | esc_attr( $this->box->id ), 43 | esc_attr( $this->box->id ) 44 | ); 45 | } 46 | } 47 | 48 | /** 49 | * Add css classes to meta box. 50 | * 51 | * @param array $classes 52 | * 53 | * @return string[] 54 | */ 55 | public function meta_box_css_classes( array $classes ) { 56 | return array_merge( $classes, [ 57 | 'papi-box' 58 | ] ); 59 | } 60 | 61 | /** 62 | * Move meta boxes after title. 63 | */ 64 | public function move_meta_box_after_title() { 65 | global $post, $wp_meta_boxes; 66 | do_meta_boxes( get_current_screen(), $this->box->context, $post ); 67 | unset( $wp_meta_boxes[get_post_type( $post )][$this->box->context] ); 68 | } 69 | 70 | /** 71 | * Get meta post type. 72 | * 73 | * @return string 74 | */ 75 | protected function get_post_type() { 76 | if ( papi_get_meta_type() === 'post' ) { 77 | if ( $post_id = papi_get_post_id() ) { 78 | return get_post_type( $post_id ); 79 | } 80 | 81 | if ( $post_type = papi_get_post_type() ) { 82 | return $post_type; 83 | } 84 | } 85 | 86 | return $this->box->id; 87 | } 88 | 89 | /** 90 | * Get meta box title. 91 | * 92 | * @return string 93 | */ 94 | protected function get_title() { 95 | $title = $this->box->title; 96 | 97 | if ( $this->box->get_option( 'required' ) ) { 98 | $title .= papi_property_required_html( 99 | $this->box->properties[0], 100 | true 101 | ); 102 | } 103 | 104 | return $title; 105 | } 106 | 107 | /** 108 | * Render the meta box 109 | * 110 | * @param array $post 111 | * @param array $args 112 | */ 113 | public function render_meta_box( $post, array $args ) { 114 | if ( ! isset( $args['args'] ) ) { 115 | return; 116 | } 117 | 118 | // Do a last check before all properties is rendered. 119 | $args['args'] = array_filter( $args['args'], 'papi_is_property' ); 120 | 121 | // Inherit options from the box. 122 | foreach ( $args['args'] as $index => $property ) { 123 | if ( $property->layout === 'horizontal' ) { 124 | $args['args'][$index]->layout = $this->box->layout; 125 | } 126 | } 127 | 128 | // Render the properties. 129 | papi_render_properties( papi_sort_order( array_reverse( $args['args'] ) ) ); 130 | } 131 | 132 | /** 133 | * Setup action hooks. 134 | */ 135 | protected function setup_actions() { 136 | if ( post_type_exists( $this->get_post_type() ) && papi_get_meta_type() === 'post' ) { 137 | add_action( 'add_meta_boxes', [$this, 'setup_meta_box'] ); 138 | 139 | if ( $this->box->context === 'after_title' ) { 140 | add_action( 'edit_form_after_title', [$this, 'move_meta_box_after_title'] ); 141 | } 142 | } else { 143 | $this->setup_meta_box(); 144 | } 145 | 146 | // Will be called on when you call do_meta_boxes 147 | // even without a real post type. 148 | add_action( 149 | sprintf( 150 | 'postbox_classes_%s_%s', 151 | strtolower( $this->get_post_type() ), 152 | $this->box->id 153 | ), 154 | [$this, 'meta_box_css_classes'] 155 | ); 156 | 157 | add_action( 'admin_head', [$this, 'admin_head'] ); 158 | } 159 | 160 | /** 161 | * Setup meta box. 162 | */ 163 | public function setup_meta_box() { 164 | $properties = $this->box->properties; 165 | 166 | // Check all properties and remove them that can't be rendered. 167 | foreach ( $properties as $index => $property ) { 168 | if ( $property instanceof Papi_Property && ! $property->can_render() ) { 169 | unset( $properties[$index] ); 170 | } 171 | } 172 | 173 | // Bail if properties array is empty, 174 | // no need to render a empty meta box. 175 | if ( empty( $properties ) ) { 176 | return; 177 | } 178 | 179 | add_meta_box( 180 | $this->box->id, 181 | $this->get_title(), 182 | [$this, 'render_meta_box'], 183 | $this->get_post_type(), 184 | $this->box->context, 185 | $this->box->priority, 186 | $properties 187 | ); 188 | } 189 | } 190 | -------------------------------------------------------------------------------- /src/admin/class-papi-admin-option-handler.php: -------------------------------------------------------------------------------- 1 | get_post_data(); 23 | 24 | // Prepare properties data. 25 | $data = $this->prepare_properties_data( $data, 0 ); 26 | 27 | foreach ( $data as $key => $value ) { 28 | papi_data_update( 0, $key, $value, 'option' ); 29 | } 30 | 31 | /** 32 | * Fire `save_properties` action when all is done. 33 | * 34 | * @param int $id 35 | * @param string $meta_type 36 | */ 37 | do_action( 'papi/save_properties', 0, 'option' ); 38 | } 39 | 40 | /** 41 | * Setup actions. 42 | */ 43 | protected function setup_actions() { 44 | add_action( 'admin_init', [$this, 'save_properties'] ); 45 | } 46 | } 47 | 48 | if ( papi_is_admin() ) { 49 | new Papi_Admin_Option_Handler; 50 | } 51 | -------------------------------------------------------------------------------- /src/admin/class-papi-admin-page-type-switcher.php: -------------------------------------------------------------------------------- 1 | name, $b->name ); 41 | } ); 42 | 43 | $page_types = papi_sort_order( array_reverse( $page_types ) ); 44 | 45 | // Don't do anything without any page types. 46 | if ( empty( $page_type ) || empty( $page_types ) ) { 47 | return; 48 | } ?> 49 | 50 |
51 | 52 | name ); ?> 53 | 54 | capabilities ) && $page_type->switcher ): ?> 55 | 56 |
57 | 73 | 74 | 75 |
76 | 77 |
78 | post_type ) === $page_type_id ) { 116 | $page_type = papi_get_standard_page_type( $post->post_type ); 117 | } else { 118 | $page_type = papi_get_entry_type_by_id( $page_type_id ); 119 | } 120 | 121 | // Fetch right page type switch if standard page type id. 122 | if ( papi_get_standard_page_type_id( $post->post_type ) === $page_type_switch_id ) { 123 | $page_type_switch = papi_get_standard_page_type( $post->post_type ); 124 | $page_type_switch_id = ''; 125 | } else { 126 | $page_type_switch = papi_get_entry_type_by_id( $page_type_switch_id ); 127 | } 128 | 129 | $post_type_object = get_post_type_object( $post->post_type ); 130 | 131 | // Check if page type and post type is not empty. 132 | if ( empty( $page_type_switch ) || empty( $post_type_object ) ) { 133 | return false; 134 | } 135 | 136 | // Check if autosave. 137 | if ( wp_is_post_autosave( $post_id ) ) { 138 | return false; 139 | } 140 | 141 | // Check if revision. 142 | if ( wp_is_post_revision( $post_id ) ) { 143 | return false; 144 | } 145 | 146 | // Check if revision post type. 147 | if ( in_array( $post->post_type, ['revision', 'nav_menu_item'], true ) ) { 148 | return false; 149 | } 150 | 151 | // Check so page type has the post type. 152 | if ( ! $page_type->has_post_type( $post->post_type ) || ! $page_type_switch->has_post_type( $post->post_type ) ) { 153 | return false; 154 | } 155 | 156 | // Check page type capabilities. 157 | if ( ! papi_current_user_is_allowed( $page_type_switch->capabilities ) ) { 158 | return false; 159 | } 160 | 161 | // Check so user can edit posts and that the user can publish posts on the post type. 162 | if ( ! current_user_can( 'edit_post', $post_id ) || ! current_user_can( $post_type_object->cap->publish_posts ) ) { 163 | return false; 164 | } 165 | 166 | // Get properties. 167 | $properties = $page_type->get_properties(); 168 | $properties_switch = $page_type_switch->get_properties(); 169 | 170 | // Delete only properties that don't have the same type and slug. 171 | foreach ( $properties as $property ) { 172 | $delete = true; 173 | 174 | // Check if the properties are the same or not. 175 | foreach ( $properties_switch as $property_switch ) { 176 | if ( $property_switch->type === $property->type && $property_switch->match_slug( $property->get_slug() ) ) { 177 | $delete = false; 178 | break; 179 | } 180 | } 181 | 182 | if ( ! $delete ) { 183 | continue; 184 | } 185 | 186 | // Delete property values. 187 | $property->delete_value( $property->get_slug( true ), $post_id, papi_get_meta_type() ); 188 | } 189 | 190 | // Delete page type switch id. 191 | if ( empty( $page_type_switch_id ) ) { 192 | return delete_post_meta( $post_id, papi_get_page_type_key() ); 193 | } 194 | 195 | // Update page type id. 196 | return papi_set_page_type_id( $post_id, $page_type_switch_id ); 197 | } 198 | } 199 | 200 | if ( papi_is_admin() ) { 201 | new Papi_Admin_Page_Type_Switcher; 202 | } 203 | -------------------------------------------------------------------------------- /src/admin/class-papi-admin-view.php: -------------------------------------------------------------------------------- 1 | path = empty( $path ) ? PAPI_PLUGIN_DIR . '/admin/views/' : $path; 22 | } 23 | 24 | /** 25 | * Check if file exists. 26 | * 27 | * @param string $file 28 | * 29 | * @return bool 30 | */ 31 | public function exists( $file ) { 32 | return file_exists( $this->file( $file ) ); 33 | } 34 | 35 | /** 36 | * Render file. 37 | * 38 | * @param string $file 39 | * 40 | * @return string 41 | */ 42 | public function render( $file ) { 43 | if ( ! empty( $file ) && $this->exists( $file ) ) { 44 | require $this->file( $file ); 45 | } 46 | } 47 | 48 | /** 49 | * Get full path to file with php exstention. 50 | * 51 | * @param string $file 52 | * 53 | * @return string 54 | */ 55 | protected function file( $file ) { 56 | return $this->path . $file . '.php'; 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /src/admin/views/add-new-page.php: -------------------------------------------------------------------------------- 1 | 6 |
7 |

8 | labels->singular_name ) ); ?> 9 | 10 | 13 | 14 | 15 |

16 | 17 |
18 | get_child_types(); 25 | $page_types = empty( $child_types ) ? $page_types : $child_types; 26 | 27 | if ( ! $show_standard ) { 28 | $show_standard = $parent_page_type->standard_type; 29 | } 30 | } 31 | 32 | if ( $show_standard ) { 33 | $page_types[] = papi_get_standard_page_type( $post_type_name ); 34 | } 35 | 36 | usort( $page_types, function ( $a, $b ) { 37 | return strcmp( $a->name, $b->name ); 38 | } ); 39 | 40 | $page_types = papi_sort_order( array_reverse( $page_types ) ); 41 | $use_thumbnail = false; 42 | 43 | foreach ( $page_types as $key => $page_type ) { 44 | if ( ! empty( $page_type->get_thumbnail() ) ) { 45 | $use_thumbnail = true; 46 | } 47 | } 48 | 49 | foreach ( $page_types as $key => $page_type ) { 50 | if ( ! papi_display_page_type( $page_type ) ) { 51 | continue; 52 | } 53 | 54 | papi_include_template( 'admin/views/partials/add-new-item.php', [ 55 | 'title' => $page_type->name, 56 | 'description' => $page_type->description, 57 | 'thumbnail' => $page_type->get_thumbnail(), 58 | 'url' => papi_get_page_new_url( $page_type->get_id(), true, null ), 59 | 'use_thumbnail' => $use_thumbnail 60 | ] ); 61 | } 62 | ?> 63 |
64 |
65 | -------------------------------------------------------------------------------- /src/admin/views/partials/add-new-item.php: -------------------------------------------------------------------------------- 1 | 2 | 3 |
4 | 5 |
6 | 7 |
8 |
9 |
10 |
11 |
12 | 13 |
14 | 15 | 16 |
17 |

18 |

19 |
20 | 21 |
22 | 23 |
24 |
25 | -------------------------------------------------------------------------------- /src/cli/class-papi-cli-command.php: -------------------------------------------------------------------------------- 1 | get_format_args( $assoc_args ); 17 | 18 | return new \WP_CLI\Formatter( $args ); 19 | } 20 | 21 | /** 22 | * Get default fields for formatter. 23 | * 24 | * Class that extends Papi_CLI_Command should override this method. 25 | * 26 | * @return null|string|array 27 | */ 28 | protected function get_default_format_fields() { 29 | return null; 30 | } 31 | 32 | /** 33 | * Get format args that will be passed into CLI Formatter. 34 | * 35 | * @param array $assoc_args Associative args from CLI 36 | * 37 | * @return array Formatter args 38 | */ 39 | protected function get_format_args( $assoc_args ) { 40 | $format_args = [ 41 | 'fields' => $this->get_default_format_fields(), 42 | 'field' => null, 43 | 'format' => 'table', 44 | ]; 45 | 46 | if ( isset( $assoc_args['fields'] ) ) { 47 | $format_args['fields'] = $assoc_args['fields']; 48 | } 49 | 50 | if ( isset( $assoc_args['field'] ) ) { 51 | $format_args['field'] = $assoc_args['field']; 52 | } 53 | 54 | if ( ! empty( $assoc_args['format'] ) && in_array( $assoc_args['format'], ['count', 'ids', 'table', 'csv', 'json'], true ) ) { 55 | $format_args['format'] = $assoc_args['format']; 56 | } 57 | 58 | return $format_args; 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /src/cli/class-papi-cli-post-command.php: -------------------------------------------------------------------------------- 1 | 23 | * : Post ID 24 | * 25 | * [--field=] 26 | * : Instead of returning the whole post fields, returns the value of a single fields. 27 | * 28 | * [--fields=] 29 | * : Get a specific subset of the post's fields. 30 | * 31 | * [--format=] 32 | * : Accepted values: table, json, csv. Default: table. 33 | * 34 | * ## AVAILABLE FIELDS 35 | * 36 | * These fields are available for get command: 37 | * 38 | * * slug 39 | * * type 40 | * * exists 41 | * 42 | * ## EXAMPLES 43 | * 44 | * wp papi post get 123 --format=json 45 | * 46 | * wp papi post get 123 --field=slug 47 | * 48 | * @param array $args 49 | * @param array $assoc_args 50 | */ 51 | public function get( $args, $assoc_args ) { 52 | try { 53 | // Set query string that we need. 54 | $_GET['post'] = $args[0]; 55 | 56 | // Get the page type that the post has. 57 | $entry_type = papi_get_entry_type_by_meta_id( $args[0] ); 58 | 59 | if ( empty( $entry_type ) || $entry_type instanceof Papi_Page_Type === false ) { 60 | WP_CLI::error( 'No page type exists on the post' ); 61 | } 62 | 63 | $properties = []; 64 | 65 | foreach ( $entry_type->get_boxes() as $box ) { 66 | foreach ( $box->properties as $property ) { 67 | $properties[] = [ 68 | 'slug' => $property->get_slug( true ), 69 | 'type' => $property->type, 70 | 'has value' => $property->get_value() !== null ? 'true' : 'false', 71 | 'box' => $box->title 72 | ]; 73 | } 74 | } 75 | 76 | // Render types as a table. 77 | $formatter = $this->get_formatter( $assoc_args ); 78 | $formatter->display_items( $properties ); 79 | } catch ( WC_CLI_Exception $e ) { 80 | WP_CLI::error( $e->getMessage() ); 81 | } 82 | } 83 | 84 | /** 85 | * Rename meta key for page type. 86 | * 87 | * ## OPTIONS 88 | * 89 | * 90 | * : Page type id 91 | * 92 | * 93 | * : Old meta key 94 | * 95 | * 96 | * : New meta key 97 | * 98 | * ## EXAMPLES 99 | * 100 | * wp papi post rename about-page-type name title 101 | * 102 | * @param array $args 103 | * @param array $assoc_args 104 | */ 105 | public function rename( $args, $assoc_args ) { 106 | $type = $args[0]; 107 | $old_key = $args[1]; 108 | $new_key = $args[2]; 109 | 110 | $posts = ( new Papi_Query( [ 111 | 'entry_type' => $type, 112 | 'fields' => 'ids' 113 | ] ) )->get_result(); 114 | 115 | if ( empty( $posts ) ) { 116 | WP_CLI::error( 'No posts found' ); 117 | } 118 | 119 | foreach ( $posts as $post ) { 120 | $meta = get_post_meta( $post, $old_key, true ); 121 | 122 | if ( papi_is_empty( $meta ) ) { 123 | continue; 124 | } 125 | 126 | if ( delete_post_meta( $post, $old_key ) === false ) { 127 | WP_CLI::error( 'Could not delete post meta with key: ' . $old_key ); 128 | 129 | } 130 | 131 | if ( update_post_meta( $post, $new_key, $meta ) === false ) { 132 | WP_CLI::error( 'Could not update post meta with key: ' . $new_key ); 133 | } 134 | } 135 | 136 | WP_CLI::success( 'Done' ); 137 | } 138 | } 139 | -------------------------------------------------------------------------------- /src/cli/class-papi-cli-term-command.php: -------------------------------------------------------------------------------- 1 | 23 | * : Term ID 24 | * 25 | * [--field=] 26 | * : Instead of returning the whole term fields, returns the value of a single fields. 27 | * 28 | * [--fields=] 29 | * : Get a specific subset of the term's fields. 30 | * 31 | * [--format=] 32 | * : Accepted values: table, json, csv. Default: table. 33 | * 34 | * ## AVAILABLE FIELDS 35 | * 36 | * These fields are available for get command: 37 | * 38 | * * slug 39 | * * type 40 | * * exists 41 | * 42 | * ## EXAMPLES 43 | * 44 | * wp papi term get 123 --format=json 45 | * 46 | * wp papi term get 123 --field=slug 47 | * 48 | * @param array $args 49 | * @param array $assoc_args 50 | */ 51 | public function get( $args, $assoc_args ) { 52 | try { 53 | // Set query string that we need. 54 | $_GET['meta_type'] = 'term'; 55 | 56 | // Get the taxonomy type that the term has. 57 | $entry_type = papi_get_entry_type_by_meta_id( $args[0] ); 58 | 59 | if ( empty( $entry_type ) || $entry_type instanceof Papi_Taxonomy_Type === false ) { 60 | WP_CLI::error( 'No taxonomy type exists on the term' ); 61 | } 62 | 63 | $properties = []; 64 | 65 | foreach ( $entry_type->get_boxes() as $box ) { 66 | foreach ( $box->properties as $property ) { 67 | $properties[] = [ 68 | 'slug' => $property->get_slug( true ), 69 | 'type' => $property->type, 70 | 'has value' => $property->get_value() !== null ? 'true' : 'false', 71 | 'box' => $box->title 72 | ]; 73 | } 74 | } 75 | 76 | // Render types as a table. 77 | $formatter = $this->get_formatter( $assoc_args ); 78 | $formatter->display_items( $properties ); 79 | } catch ( WC_CLI_Exception $e ) { 80 | WP_CLI::error( $e->getMessage() ); 81 | } 82 | } 83 | 84 | /** 85 | * Rename meta key for taxonomy type. 86 | * 87 | * ## OPTIONS 88 | * 89 | * 90 | * : Taxonomy type id 91 | * 92 | * 93 | * : Old meta key 94 | * 95 | * 96 | * : New meta key 97 | * 98 | * ## EXAMPLES 99 | * 100 | * wp papi term rename about-page-type name title 101 | * 102 | * @param array $args 103 | * @param array $assoc_args 104 | */ 105 | public function rename( $args, $assoc_args ) { 106 | $type = $args[0]; 107 | $old_key = $args[1]; 108 | $new_key = $args[2]; 109 | 110 | $terms = ( new Papi_Query( [ 111 | 'entry_type' => $type, 112 | 'fields' => 'ids' 113 | ] ) )->get_result(); 114 | 115 | if ( empty( $terms ) ) { 116 | WP_CLI::error( 'No terms found' ); 117 | } 118 | 119 | foreach ( $terms as $term ) { 120 | $meta = get_term_meta( $term, $old_key, true ); 121 | 122 | if ( papi_is_empty( $meta ) ) { 123 | continue; 124 | } 125 | 126 | if ( delete_term_meta( $term, $old_key ) === false ) { 127 | WP_CLI::error( 'Could not delete term meta with key: ' . $old_key ); 128 | 129 | } 130 | 131 | if ( update_term_meta( $term, $new_key, $meta ) === false ) { 132 | WP_CLI::error( 'Could not update term meta with key: ' . $new_key ); 133 | } 134 | } 135 | 136 | WP_CLI::success( 'Done' ); 137 | } 138 | } 139 | -------------------------------------------------------------------------------- /src/cli/class-papi-cli-type-command.php: -------------------------------------------------------------------------------- 1 | get_type(), ['attachment'], true ) ) { 26 | return $entry_type->get_type(); 27 | } 28 | 29 | switch ( papi_get_meta_type( $entry_type->get_type() ) ) { 30 | case 'post': 31 | return implode( ', ', $entry_type->post_type ); 32 | case 'term': 33 | return implode( ', ', $entry_type->taxonomy ); 34 | default: 35 | return 'n/a'; 36 | } 37 | } 38 | 39 | /** 40 | * List Papi types. 41 | * 42 | * ## Options 43 | * 44 | * [--=] 45 | * : Filter types based on type property. 46 | * 47 | * [--field=] 48 | * : Prints the value of a single field for each type. 49 | * 50 | * [--fields=] 51 | * : Limit the output to specific type fields. 52 | * 53 | * [--format=] 54 | * : Acceptec values: table, csv, json, count, ids. Default: table. 55 | * 56 | * ## AVAILABLE FIELDS 57 | * 58 | * These fields will be displayed by default for each type: 59 | * 60 | * * name 61 | * * id 62 | * * post_type 63 | * * template 64 | * * number_of_pages 65 | * * type 66 | * 67 | * Not all fields exists on a Papi type so some fields will have `n/a` 68 | * as value when no value can be displayed. 69 | * 70 | * ## EXAMPLES 71 | * 72 | * wp papi type list 73 | * 74 | * @subcommand list 75 | */ 76 | public function list_( $args, $assoc_args ) { 77 | // Get all entry types. 78 | $entry_types = papi_get_all_entry_types(); 79 | 80 | if ( empty( $entry_types ) ) { 81 | WP_CLI::error( 'No Papi types exists.' ); 82 | } 83 | 84 | // Create type item with the fields that 85 | // will be displayed. 86 | $entry_types = array_map( function( $entry_type ) { 87 | return [ 88 | 'id' => $entry_type->get_id(), 89 | 'name' => $entry_type->name, 90 | 'meta type value' => $this->get_meta_type_value( $entry_type ), 91 | 'template' => empty( $entry_type->template ) ? 'n/a' : $entry_type->template, 92 | 'type' => $entry_type->get_type(), 93 | 'db count' => $entry_type->get_type() === 'option' ? 'n/a' : papi_get_entry_type_count( $entry_type ) 94 | ]; 95 | }, $entry_types ); 96 | 97 | // Render types as a table. 98 | $formatter = $this->get_formatter( $assoc_args ); 99 | $formatter->display_items( $entry_types ); 100 | } 101 | } 102 | -------------------------------------------------------------------------------- /src/cli/class-papi-cli.php: -------------------------------------------------------------------------------- 1 | setup_args( $args ); 101 | $this->setup_properties( $properties ); 102 | } 103 | 104 | /** 105 | * Get box option. 106 | * 107 | * @param string $key 108 | * 109 | * @return mixed 110 | */ 111 | public function get_option( $key ) { 112 | return isset( $this->options[$key] ) ? $this->options[$key] : null; 113 | } 114 | 115 | /** 116 | * Set box option. 117 | * 118 | * @param string $key 119 | * @param mixed $value 120 | */ 121 | public function set_option( $key, $value ) { 122 | $this->options[$key] = $value; 123 | } 124 | 125 | /** 126 | * Setup arguments. 127 | * 128 | * @param array $args 129 | */ 130 | protected function setup_args( array $args ) { 131 | $excluded_keys = ['options', 'properties']; 132 | 133 | foreach ( $args as $key => $value ) { 134 | if ( isset( $this->$key ) && ! in_array( $key, $excluded_keys, true ) ) { 135 | $this->$key = papi_esc_html( $value ); 136 | } 137 | } 138 | 139 | if ( empty( $this->id ) ) { 140 | $this->id = papi_slugify( strtolower( papi_f( papi_underscorify( papify( $this->title ) ) ) ) ); 141 | $this->id = sanitize_text_field( $this->id ); 142 | } 143 | } 144 | 145 | /** 146 | * Setup properties. 147 | * 148 | * @param array $properties 149 | */ 150 | protected function setup_properties( array $properties ) { 151 | $this->properties = papi_populate_properties( $properties ); 152 | } 153 | 154 | /** 155 | * Get a string representation of the object. 156 | * 157 | * @return string 158 | */ 159 | public function __toString() { 160 | return $this->id; 161 | } 162 | } 163 | -------------------------------------------------------------------------------- /src/core/class-papi-core-conditional-rule.php: -------------------------------------------------------------------------------- 1 | setup( $rule ); 43 | } 44 | 45 | /** 46 | * Get field slug. 47 | * 48 | * @return string 49 | */ 50 | public function get_field_slug() { 51 | if ( preg_match( '/\[|\]/', $this->slug ) ) { 52 | $slug = preg_replace( '/\[|\]/', '.', $this->slug ); 53 | $slug = str_replace( '..', '.', $slug ); 54 | return substr( $slug, 0, -1 ); 55 | } 56 | 57 | return $this->slug; 58 | } 59 | 60 | /** 61 | * Get the source value. 62 | * 63 | * @return mixed 64 | */ 65 | public function get_source() { 66 | if ( is_callable( $this->source ) ) { 67 | return call_user_func_array( $this->source, [$this->slug] ); 68 | } 69 | 70 | if ( is_string( $this->source ) && strpos( $this->source, '#' ) !== false ) { 71 | $source = explode( '#', $this->source ); 72 | 73 | if ( empty( $source[0] ) || empty( $source[1] ) ) { 74 | return $this->source; 75 | } 76 | 77 | $source[0] = new $source[0](); 78 | 79 | if ( method_exists( $source[0], $source[1] ) ) { 80 | return call_user_func_array( $source, [$this->slug] ); 81 | } 82 | 83 | return; 84 | } 85 | 86 | return $this->source; 87 | } 88 | 89 | /** 90 | * Setup source callable. 91 | * 92 | * @param string $value 93 | * 94 | * @return string 95 | */ 96 | public function setup_source( $value ) { 97 | if ( is_array( $value ) && count( $value ) === 2 && is_object( $value[0] ) && is_string( $value[1] ) ) { 98 | return sprintf( '%s#%s', get_class( $value[0] ), $value[1] ); 99 | } 100 | 101 | if ( is_string( $value ) && is_callable( $value ) ) { 102 | return $value; 103 | } 104 | 105 | // No support for closure. 106 | if ( is_object( $value ) && $value instanceof Closure ) { 107 | return ''; 108 | } 109 | 110 | return $value; 111 | } 112 | 113 | /** 114 | * Setup the rule and assign properties with values. 115 | * 116 | * @param array $rule 117 | */ 118 | protected function setup( array $rule ) { 119 | foreach ( $rule as $key => $value ) { 120 | if ( $key === 'operator' ) { 121 | $value = strtoupper( $value ); 122 | $value = html_entity_decode( $value ); 123 | } else if ( $key === 'slug' ) { 124 | $value = papify( $value ); 125 | } else if ( $key === 'source' ) { 126 | $value = $this->setup_source( $value ); 127 | } 128 | 129 | $this->$key = $value; 130 | } 131 | } 132 | } 133 | -------------------------------------------------------------------------------- /src/core/class-papi-core-conditional.php: -------------------------------------------------------------------------------- 1 | prepare_rules( $rules, $property ); 33 | 34 | if ( in_array( $rules['relation'], $this->relations, true ) ) { 35 | return $this->display_by_relation( $rules ); 36 | } 37 | 38 | return true; 39 | } 40 | 41 | /** 42 | * Get the display by relation. 43 | * 44 | * @param array $rules 45 | * 46 | * @return bool 47 | */ 48 | protected function display_by_relation( array $rules ) { 49 | if ( $rules['relation'] === 'AND' ) { 50 | $display = true; 51 | 52 | foreach ( $rules as $rule ) { 53 | if ( ! $display ) { 54 | break; 55 | } 56 | 57 | if ( papi_is_rule( $rule ) ) { 58 | /** 59 | * Modify rule allowed. 60 | * 61 | * @param bool $result 62 | * @param Papi_Core_Conditional_Rule $rule 63 | * 64 | * @return bool 65 | */ 66 | $display = apply_filters( 'papi/conditional/rule_allowed', papi_filter_conditional_rule_allowed( $rule ), $rule ); 67 | } 68 | } 69 | 70 | return $display; 71 | } 72 | 73 | $empty = array_filter( $rules, function ( $rule ) { 74 | return papi_is_rule( $rule ) ? true : null; 75 | } ); 76 | 77 | if ( empty( $empty ) ) { 78 | return true; 79 | } 80 | 81 | $result = []; 82 | 83 | foreach ( $rules as $rule ) { 84 | if ( papi_is_rule( $rule ) ) { 85 | /** 86 | * Modify rule allowed. 87 | * 88 | * @param bool $result 89 | * @param Papi_Core_Conditional_Rule $rule 90 | * 91 | * @return bool 92 | */ 93 | $result[] = apply_filters( 'papi/conditional/rule_allowed', papi_filter_conditional_rule_allowed( $rule ), $rule ); 94 | } 95 | } 96 | 97 | $result = array_filter( $result, function ( $res ) { 98 | return $res === true ? true : null; 99 | } ); 100 | 101 | return ! empty( $result ); 102 | } 103 | 104 | /** 105 | * Get rule slug. 106 | * 107 | * @param Papi_Core_Conditional_Rule $rule 108 | * @param Papi_Core_Property $property 109 | * 110 | * @return string 111 | */ 112 | protected function get_rule_slug( $rule, $property ) { 113 | $arr_reg = '/\[\d+\](\[\w+\])$/'; 114 | $slug = $property->get_slug(); 115 | 116 | $page_type = papi_get_entry_type_by_meta_id(); 117 | 118 | if ( $page_type instanceof Papi_Page_Type === false ) { 119 | return $rule->slug; 120 | } 121 | 122 | if ( preg_match( $arr_reg, $slug, $out ) ) { 123 | $slug = str_replace( $out[1], '[' . unpapify( $rule->slug ) . ']', $slug ); 124 | $property = $page_type->get_property( $slug ); 125 | 126 | if ( papi_is_property( $property ) ) { 127 | return $slug; 128 | } 129 | } 130 | 131 | return $rule->slug; 132 | } 133 | 134 | /** 135 | * Prepare rules. 136 | * 137 | * @param array $rules 138 | * @param Papi_Core_Property $property 139 | * 140 | * @return array 141 | */ 142 | public function prepare_rules( array $rules, $property = null ) { 143 | if ( ! isset( $rules['relation'] ) ) { 144 | $rules['relation'] = 'OR'; 145 | } else { 146 | $rules['relation'] = strtoupper( $rules['relation'] ); 147 | } 148 | 149 | foreach ( $rules as $index => $value ) { 150 | if ( is_string( $index ) ) { 151 | continue; 152 | } 153 | 154 | if ( is_array( $value ) ) { 155 | $rules[$index] = new Papi_Core_Conditional_Rule( $value ); 156 | 157 | if ( strpos( $rules[$index]->slug, '.' ) === false && papi_is_property( $property ) ) { 158 | $rules[$index]->slug = $this->get_rule_slug( 159 | $rules[$index], 160 | $property 161 | ); 162 | } 163 | } 164 | } 165 | 166 | return $rules; 167 | } 168 | } 169 | -------------------------------------------------------------------------------- /src/core/class-papi-core-data-handler.php: -------------------------------------------------------------------------------- 1 | setup_actions(); 20 | } 21 | 22 | /** 23 | * Decode property. 24 | * 25 | * @param string $key 26 | * @param string $value 27 | * 28 | * @return mixed 29 | */ 30 | protected function decode_property( $key, $value ) { 31 | if ( papi_is_property_type_key( $key ) && is_string( $value ) ) { 32 | $value = base64_decode( $value ); 33 | $value = papi_maybe_json_decode( $value ); 34 | } 35 | 36 | return $value; 37 | } 38 | 39 | /** 40 | * Get post data. 41 | * 42 | * @param string $pattern 43 | * 44 | * @return array 45 | */ 46 | protected function get_post_data( $pattern = '/^papi\_.*/' ) { 47 | $data = []; 48 | $keys = preg_grep( $pattern, array_keys( $_POST ) ); 49 | 50 | foreach ( $keys as $key ) { 51 | // Remove page type keys with suffix. This should not be saved. 52 | if ( 53 | strpos( $key, papi_get_page_type_key() ) === 0 54 | && 55 | strlen( papi_get_page_type_key() ) !== strlen( $key ) 56 | ) { 57 | continue; 58 | } 59 | 60 | // Fix for input fields that should be true on `on` value. 61 | if ( $_POST[$key] === 'on' ) { 62 | $data[$key] = true; 63 | } else { 64 | $value = $this->decode_property( $key, $_POST[$key] ); 65 | $data[$key] = $this->prepare_post_data( $value ); 66 | $data[$key] = $this->santize_data( $data[$key] ); 67 | } 68 | } 69 | 70 | // Don't wont to save meta nonce field. 71 | if ( isset( $data['papi_meta_nonce'] ) ) { 72 | unset( $data['papi_meta_nonce'] ); 73 | } 74 | 75 | return $data; 76 | } 77 | 78 | /** 79 | * Get pre data that should be saved before all properties data. 80 | */ 81 | protected function get_pre_data() { 82 | return $this->get_post_data( '/^\_papi\_.*/' ); 83 | } 84 | 85 | /** 86 | * Pre get deep keys and value. 87 | * 88 | * Used for saving pre data when properties are in a flexible or repeater. 89 | * 90 | * @param array $arr 91 | * 92 | * @return array 93 | */ 94 | protected function get_pre_deep_keys_value( array $arr ) { 95 | $keys = []; 96 | $value = null; 97 | 98 | foreach ( $arr as $key => $v ) { 99 | if ( is_array( $v ) ) { 100 | $keys[] = $key; 101 | list( $ks, $val ) = $this->get_pre_deep_keys_value( $v ); 102 | $keys = array_merge( $keys, $ks ); 103 | $value = $val; 104 | } else { 105 | $keys[] = $key; 106 | $value = $v; 107 | } 108 | } 109 | 110 | return [$keys, $value]; 111 | } 112 | 113 | /** 114 | * Prepare post data. 115 | * Will decode property options recursive. 116 | * 117 | * @param mixed $data 118 | * 119 | * @return mixed 120 | */ 121 | protected function prepare_post_data( $data ) { 122 | if ( ! is_array( $data ) ) { 123 | return $data; 124 | } 125 | 126 | foreach ( $data as $key => $value ) { 127 | if ( is_array( $value ) ) { 128 | $data[$key] = $this->prepare_post_data( $value ); 129 | } else { 130 | $data[$key] = $this->decode_property( $key, $value ); 131 | } 132 | } 133 | 134 | return $data; 135 | } 136 | 137 | /** 138 | * Prepare properties data for saving. 139 | * 140 | * @param array $data 141 | * @param int $post_id 142 | * 143 | * @return array 144 | */ 145 | protected function prepare_properties_data( array $data = [], $post_id = 0 ) { 146 | // Since we are storing witch property it is in the `$data` array 147 | // we need to remove that and set the property type to the property 148 | // and make a array of the property type and the value. 149 | foreach ( $data as $key => $value ) { 150 | if ( papi_is_property_type_key( $key ) ) { 151 | continue; 152 | } 153 | 154 | $property_type_key = papify( papi_get_property_type_key( $key ) ); 155 | 156 | // Check if value exists. 157 | if ( ! isset( $data[$key] ) || ! isset( $data[$property_type_key] ) ) { 158 | continue; 159 | } 160 | 161 | // Pair property value with property type object. 162 | $data[$key] = [ 163 | 'type' => $data[$property_type_key], 164 | 'value' => $value 165 | ]; 166 | 167 | // Remove property type object since it's not needed anymore. 168 | unset( $data[$property_type_key] ); 169 | } 170 | 171 | foreach ( $data as $key => $item ) { 172 | if ( papi_is_property_type_key( $key ) ) { 173 | if ( isset( $data[$key] ) ) { 174 | unset( $data[$key] ); 175 | } 176 | continue; 177 | } 178 | 179 | if ( empty( $item['type'] ) ) { 180 | continue; 181 | } 182 | 183 | $property = papi_get_property_type( $item['type'] ); 184 | 185 | unset( $data[ $key ] ); 186 | 187 | if ( papi_is_property( $property ) ) { 188 | // Run `update_value` method on the property class. 189 | $data[$key] = $property->update_value( 190 | $item['value'], 191 | unpapify( $key ), 192 | $post_id 193 | ); 194 | 195 | // Apply `update_value` filter so this can be changed from 196 | // the theme for specified property type. 197 | $data[$key] = papi_filter_update_value( 198 | $item['type']->type, 199 | $data[$key], 200 | unpapify( $key ), 201 | $post_id, 202 | papi_get_meta_type() 203 | ); 204 | 205 | if ( $item['type']->overwrite ) { 206 | $slug = unpapify( $key ); 207 | $this->overwrite[$slug] = $data[$key]; 208 | unset( $data[$key] ); 209 | } 210 | } 211 | } 212 | 213 | return $data; 214 | } 215 | 216 | /** 217 | * Sanitize data before saving it. 218 | * 219 | * @param mixed $value 220 | * 221 | * @return mixed 222 | */ 223 | protected function santize_data( $value ) { 224 | if ( is_array( $value ) ) { 225 | foreach ( $value as $k => $v ) { 226 | if ( is_string( $v ) ) { 227 | $value[$k] = $this->santize_data( $v ); 228 | } 229 | } 230 | } else if ( is_string( $value ) ) { 231 | $value = wp_unslash( $value ); 232 | } 233 | 234 | return $value; 235 | } 236 | 237 | /** 238 | * Setup actions. 239 | * 240 | * @codeCoverageIgnore 241 | */ 242 | protected function setup_actions() { 243 | } 244 | } 245 | -------------------------------------------------------------------------------- /src/core/class-papi-core-data.php: -------------------------------------------------------------------------------- 1 | type = is_string( $type ) ? papi_get_meta_type( $type ) : 'post'; 26 | $this->id = in_array( $this->type, ['post', 'term'], true ); 27 | } 28 | 29 | /** 30 | * Delete property value. 31 | * 32 | * @param int $id 33 | * @param string $slug 34 | * 35 | * @return bool 36 | */ 37 | public function delete( $id, $slug ) { 38 | $fn = $this->get_function( 'delete' ); 39 | 40 | // Check so the function is callable before using it. 41 | if ( ! is_callable( $fn ) ) { 42 | return false; 43 | } 44 | 45 | // Delete cached value. 46 | papi_cache_delete( $slug, $id, $this->type ); 47 | 48 | if ( $this->id ) { 49 | return call_user_func_array( $fn, [$this->type, $id, unpapify( $slug )] ); 50 | } 51 | 52 | return call_user_func_array( $fn, [unpapify( $slug )] ); 53 | } 54 | 55 | /** 56 | * Get right meta function for right type and context. 57 | * 58 | * @param string $context 59 | * 60 | * @return string 61 | */ 62 | public function get_function( $context = 'get' ) { 63 | switch ( $this->type ) { 64 | case 'option': 65 | return sprintf( '%s_option', $context ); 66 | case 'site': 67 | case 'network': 68 | return sprintf( '%s_site_option', $context ); 69 | case 'post': 70 | case 'term': 71 | return sprintf( '%s_metadata', $context ); 72 | default: 73 | break; 74 | } 75 | } 76 | 77 | /** 78 | * Geta property value for right meta type. 79 | * 80 | * @param int $id 81 | * @param string $slug 82 | * 83 | * @return mixed 84 | */ 85 | public function get( $id, $slug ) { 86 | $fn = $this->get_function( 'get' ); 87 | 88 | if ( ! is_callable( $fn ) ) { 89 | return; 90 | } 91 | 92 | if ( $this->id ) { 93 | $value = call_user_func_array( $fn, [$this->type, $id, unpapify( $slug ), true] ); 94 | } else { 95 | $value = call_user_func_array( $fn, [unpapify( $slug ), null] ); 96 | } 97 | 98 | if ( papi_is_empty( $value ) ) { 99 | return; 100 | } 101 | 102 | return $value; 103 | } 104 | 105 | /** 106 | * Update property meta. 107 | * 108 | * @param int $id 109 | * @param string $slug 110 | * @param mixed $value 111 | * 112 | * @return bool 113 | */ 114 | public function update( $id, $slug, $value ) { 115 | $save_value = true; 116 | 117 | // Get right update function to use. 118 | $fn = $this->get_function( 'update' ); 119 | 120 | if ( ! is_callable( $fn ) ) { 121 | return false; 122 | } 123 | 124 | // Check for string keys in the array if any. 125 | foreach ( array_keys( papi_to_array( $value ) ) as $key ) { 126 | if ( is_string( $key ) ) { 127 | $save_value = false; 128 | break; 129 | } 130 | } 131 | 132 | // If main value shouldn't be saved it should be array. 133 | if ( ! $save_value && is_array( $value ) ) { 134 | $value = [$value]; 135 | } 136 | 137 | // Delete saved value if empty. 138 | if ( papi_is_empty( $value ) ) { 139 | return $this->delete( $id, $slug ); 140 | } 141 | 142 | $result = true; 143 | 144 | foreach ( papi_to_array( $value ) as $key => $val ) { 145 | // Delete saved value if value is empty. 146 | if ( papi_is_empty( $val ) || $val === '[]' || $val === '{}' ) { 147 | return $this->delete( $id, $slug ); 148 | } 149 | 150 | // Delete main value cache. 151 | papi_cache_delete( $slug, $id, $this->type ); 152 | 153 | // If not a array we can save the value. 154 | if ( ! is_array( $val ) ) { 155 | if ( $save_value ) { 156 | $val = $value; 157 | } 158 | 159 | if ( $this->id ) { 160 | $out = call_user_func_array( $fn, [$this->type, $id, unpapify( $slug ), $val] ); 161 | $result = $out ? $result : $out; 162 | } else { 163 | $out = call_user_func_array( $fn, [unpapify( $slug ), $val] ); 164 | $result = $out ? $result : $out; 165 | } 166 | 167 | continue; 168 | } 169 | 170 | // Clear cache for all child values. 171 | $this->update_clear_cache( $id, $value ); 172 | 173 | // Update metadata or option value for all child values. 174 | foreach ( $val as $child_key => $child_value ) { 175 | if ( papi_is_empty( $child_value ) ) { 176 | $this->delete( $id, $child_key ); 177 | } else { 178 | if ( $this->id ) { 179 | call_user_func_array( $fn, [$this->type, $id, unpapify( $child_key ), $child_value] ); 180 | } else { 181 | call_user_func_array( $fn, [unpapify( $child_key ), $child_value] ); 182 | } 183 | } 184 | } 185 | } 186 | 187 | return $result; 188 | } 189 | 190 | /** 191 | * Clear cache values on update property meta. 192 | * 193 | * @param int $id 194 | * @param mixed $value 195 | */ 196 | public function update_clear_cache( $id, $value ) { 197 | $value = is_array( $value ) ? $value : []; 198 | 199 | foreach ( $value as $child_key => $child_value ) { 200 | papi_cache_delete( $child_key, $id, $this->type ); 201 | 202 | if ( is_array( $child_value ) ) { 203 | $this->update_clear_cache( $id, $child_value ); 204 | } 205 | } 206 | } 207 | } 208 | -------------------------------------------------------------------------------- /src/core/class-papi-core-tab.php: -------------------------------------------------------------------------------- 1 | setup_args( $args ); 77 | $this->setup_properties( $properties ); 78 | } 79 | 80 | /** 81 | * Setup arguments. 82 | * 83 | * @param array $args 84 | */ 85 | protected function setup_args( array $args ) { 86 | foreach ( $args as $key => $value ) { 87 | if ( isset( $this->$key ) ) { 88 | $this->$key = papi_esc_html( $value ); 89 | } 90 | } 91 | 92 | if ( empty( $this->id ) ) { 93 | $this->id = strtolower( papi_f( papi_underscorify( papify( $this->title ) ) ) ); 94 | } 95 | } 96 | 97 | /** 98 | * Setup properties. 99 | * 100 | * @param array $properties 101 | */ 102 | protected function setup_properties( array $properties ) { 103 | $this->properties = array_merge( $this->properties, papi_populate_properties( $properties ) ); 104 | } 105 | } 106 | -------------------------------------------------------------------------------- /src/core/class-papi-core-type.php: -------------------------------------------------------------------------------- 1 | setup_file( $file_path ); 60 | $this->setup_meta_data(); 61 | } 62 | } 63 | 64 | /** 65 | * Determine if the entry type is allowed. 66 | * 67 | * @return bool 68 | */ 69 | public function allowed() { 70 | return true; 71 | } 72 | 73 | /** 74 | * Boot page type. 75 | * 76 | * @codeCoverageIgnore 77 | */ 78 | public function boot() { 79 | $this->setup_actions(); 80 | $this->setup_filters(); 81 | } 82 | 83 | /** 84 | * Get the page type class name with namespace if exists. 85 | * 86 | * @return string 87 | */ 88 | public function get_class_name() { 89 | return $this->_class_name; 90 | } 91 | 92 | /** 93 | * Get the page type file pat.h 94 | * 95 | * @return string 96 | */ 97 | public function get_file_path() { 98 | return $this->_file_path; 99 | } 100 | 101 | /** 102 | * Get the page type identifier. 103 | * 104 | * @return string 105 | */ 106 | public function get_id() { 107 | if ( ! empty( $this->_id ) ) { 108 | return $this->_id; 109 | } 110 | 111 | return papi_get_core_type_base_path( $this->_file_path ); 112 | } 113 | 114 | /** 115 | * Get meta data from type class and merge 116 | * with the parent meta data. 117 | * 118 | * @return array 119 | */ 120 | protected function get_meta() { 121 | $method = 'meta'; 122 | 123 | if ( ! method_exists( $this, $method ) ) { 124 | return []; 125 | } 126 | 127 | $child_meta = call_user_func( [$this, $method] ); 128 | $child_meta = is_array( $child_meta ) ? $child_meta : []; 129 | 130 | $parent_class = get_parent_class( $this ); 131 | $parent_exists = method_exists( $parent_class, $method ); 132 | $parent_meta = []; 133 | 134 | while ( $parent_exists ) { 135 | $rc = new ReflectionClass( $parent_class ); 136 | 137 | // Bail if not instantiable. 138 | if ( ! $rc->isInstantiable() ) { 139 | break; 140 | } 141 | 142 | $parent = $rc->newInstance(); 143 | $output = call_user_func( [$parent, $method] ); 144 | $output = is_array( $output ) ? $output : []; 145 | $parent_meta = array_merge( $parent_meta, $output ); 146 | $parent_class = get_parent_class( $parent_class ); 147 | $parent_exists = method_exists( $parent_class, $method ); 148 | } 149 | 150 | return array_merge( $parent_meta, $child_meta ); 151 | } 152 | 153 | /** 154 | * Get type name. 155 | * 156 | * @return string 157 | */ 158 | public function get_type() { 159 | return strtolower( $this->type ); 160 | } 161 | 162 | /** 163 | * Check so we have a name on the page type. 164 | * 165 | * @return bool 166 | */ 167 | public function has_name() { 168 | return ! empty( $this->name ); 169 | } 170 | 171 | /** 172 | * Check if the the given identifier match the page type identifier. 173 | * 174 | * @param string $id 175 | * 176 | * @return bool 177 | */ 178 | public function match_id( $id ) { 179 | return $this->get_id() === $id; 180 | } 181 | 182 | /** 183 | * Create a new instance of the page type file. 184 | * 185 | * @return null|object 186 | */ 187 | public function new_class() { 188 | if ( empty( $this->_file_path ) ) { 189 | return; 190 | } 191 | 192 | return new $this->_class_name; 193 | } 194 | 195 | /** 196 | * Set custom id. 197 | * 198 | * @param string $id 199 | */ 200 | public function set_id( $id ) { 201 | $this->_id = $id; 202 | } 203 | 204 | /** 205 | * Setup actions. 206 | * 207 | * @codeCoverageIgnore 208 | */ 209 | protected function setup_actions() { 210 | } 211 | 212 | /** 213 | * Load the file and setup file path, file name and class name properties. 214 | * 215 | * @param string $file_path 216 | */ 217 | protected function setup_file( $file_path ) { 218 | $this->_file_path = $file_path; 219 | $this->_class_name = papi_get_class_name( $this->_file_path ); 220 | } 221 | 222 | /** 223 | * Setup filters. 224 | * 225 | * @codeCoverageIgnore 226 | */ 227 | protected function setup_filters() { 228 | } 229 | 230 | /** 231 | * Setup meta data. 232 | */ 233 | protected function setup_meta_data() { 234 | foreach ( $this->get_meta() as $key => $value ) { 235 | if ( substr( $key, 0, 1 ) === '_' ) { 236 | continue; 237 | } 238 | 239 | $this->$key = papi_esc_html( $value ); 240 | } 241 | 242 | if ( $this->sort_order === 1000 ) { 243 | $this->sort_order = papi_filter_settings_sort_order(); 244 | } 245 | } 246 | } 247 | -------------------------------------------------------------------------------- /src/lib/core/cache.php: -------------------------------------------------------------------------------- 1 | data( $type )->delete( $id, $slug, $type ); 14 | } 15 | 16 | /** 17 | * Get data from database. 18 | * 19 | * @param int $id 20 | * @param string $slug 21 | * @param string $type 22 | */ 23 | function papi_data_get( $id, $slug, $type = 'post' ) { 24 | return papi()->data( $type )->get( $id, $slug ); 25 | } 26 | 27 | /** 28 | * Updata data in database. 29 | * 30 | * @param int $id 31 | * @param string $slug 32 | * @param mixed $value 33 | * @param string $type 34 | * 35 | * @return bool 36 | */ 37 | function papi_data_update( $id, $slug, $value, $type = 'post' ) { 38 | return papi()->data( $type )->update( $id, $slug, $value ); 39 | } 40 | -------------------------------------------------------------------------------- /src/lib/core/deprecated.php: -------------------------------------------------------------------------------- 1 | $dir ) { 31 | if ( ! file_exists( $dir ) || ! is_dir( $dir ) ) { 32 | unset( $directory[$index] ); 33 | } 34 | } 35 | 36 | return array_merge( $directories, $directory ); 37 | } ); 38 | } 39 | 40 | /** 41 | * Get all files in directory. 42 | * 43 | * @param string $directory 44 | * 45 | * @return string 46 | */ 47 | function papi_get_all_files_in_directory( $directory = '' ) { 48 | $result = []; 49 | 50 | if ( empty( $directory ) || ! is_string( $directory ) ) { 51 | return $result; 52 | } 53 | 54 | if ( file_exists( $directory ) && $handle = opendir( $directory ) ) { 55 | while ( false !== ( $file = readdir( $handle ) ) ) { 56 | if ( ! in_array( $file, ['..', '.'], true ) && $file[0] !== '.' ) { 57 | if ( is_dir( $directory . '/' . $file ) ) { 58 | $result = array_merge( $result, papi_get_all_files_in_directory( $directory . '/' . $file ) ); 59 | } else { 60 | $file = $directory . '/' . $file; 61 | $result[] = preg_replace( '/\/\//si', '/', $file ); 62 | } 63 | } 64 | } 65 | 66 | closedir( $handle ); 67 | } 68 | 69 | return $result; 70 | } 71 | 72 | /** 73 | * Get core type file path, this allows classes to be overridden. 74 | * 75 | * @param string $file_path 76 | * 77 | * @return string 78 | */ 79 | function papi_get_core_type_file_path( $file_path ) { 80 | if ( empty( $file_path ) ) { 81 | return []; 82 | } 83 | 84 | $directories = papi_filter_settings_directories(); 85 | $result = []; 86 | 87 | foreach ( $directories as $directory ) { 88 | $directory = rtrim( $directory, '/' ) . '/'; 89 | $file_path = str_replace( $directory, '', $file_path ); 90 | $path = $directory . $file_path; 91 | 92 | if ( file_exists( $path ) ) { 93 | $result[] = $path; 94 | } 95 | } 96 | 97 | return array_pop( $result ); 98 | } 99 | 100 | /** 101 | * Get all core type files from the register directories. 102 | * 103 | * @return array 104 | */ 105 | function papi_get_all_core_type_files() { 106 | return papi()->once( __FUNCTION__, function() { 107 | $directories = papi_filter_settings_directories(); 108 | $result = []; 109 | 110 | foreach ( $directories as $directory ) { 111 | $result = array_merge( $result, papi_get_all_files_in_directory( $directory ) ); 112 | } 113 | 114 | // Get the last file path from directories. 115 | $result = array_map( 'papi_get_core_type_file_path', $result ); 116 | 117 | // Only unique path, no duplicated path is allowed. 118 | return array_unique( $result ); 119 | } ); 120 | } 121 | 122 | /** 123 | * Get core type file path from file name. 124 | * 125 | * @param string $file 126 | * 127 | * @return null|string 128 | */ 129 | function papi_get_file_path( $file ) { 130 | if ( empty( $file ) || ! is_string( $file ) ) { 131 | return; 132 | } 133 | 134 | $directories = papi_filter_settings_directories(); 135 | $file = '/' . str_replace( ' ', '-', str_replace( '_', '-', $file ) ); 136 | 137 | foreach ( $directories as $directory ) { 138 | if ( file_exists( $directory . $file ) ) { 139 | return $directory . $file; 140 | } 141 | 142 | if ( file_exists( $directory . $file . '.php' ) ) { 143 | return $directory . $file . '.php'; 144 | } 145 | } 146 | } 147 | 148 | /** 149 | * Get core type base path. 150 | * 151 | * @param string $file 152 | * 153 | * @return string 154 | */ 155 | function papi_get_core_type_base_path( $file ) { 156 | if ( empty( $file ) || ! is_string( $file ) ) { 157 | return ''; 158 | } 159 | 160 | $directories = papi_filter_settings_directories(); 161 | 162 | foreach ( $directories as $directory ) { 163 | if ( strpos( $file, $directory ) !== false ) { 164 | $file = str_replace( $directory . '/', '', $file ); 165 | } 166 | } 167 | 168 | $file = explode( '.', $file ); 169 | 170 | return $file[0]; 171 | } 172 | -------------------------------------------------------------------------------- /src/lib/core/meta.php: -------------------------------------------------------------------------------- 1 | term_id ) ) { 106 | return 'term'; 107 | } 108 | 109 | if ( $obj instanceof WP_Post || isset( $obj->post_id ) ) { 110 | return 'post'; 111 | } 112 | } 113 | 114 | // Default was has to be set here since we trying to figure out 115 | // which url conform which meta type. 116 | if ( is_null( $type ) ) { 117 | $type = 'post'; 118 | } 119 | 120 | // If meta type exists as a filter we can return it. 121 | if ( function_exists( "get_{$type}_meta" ) ) { 122 | return $type; 123 | } 124 | } 125 | -------------------------------------------------------------------------------- /src/lib/core/post.php: -------------------------------------------------------------------------------- 1 | ID ) ) { 12 | return $post_id->ID; 13 | } 14 | 15 | if ( is_numeric( $post_id ) && is_string( $post_id ) && $post_id !== '0' ) { 16 | return intval( $post_id ); 17 | } 18 | 19 | if ( is_null( $post_id ) || intval( $post_id ) === 0 ) { 20 | if ( isset( $_POST['action'] ) ) { 21 | if ( $_POST['action'] === 'query-attachments' && isset( $_POST['query']['item'] ) ) { 22 | return intval( $_POST['query']['item'] ); 23 | } 24 | } 25 | 26 | if ( get_post() ) { 27 | return get_the_ID(); 28 | } 29 | 30 | if ( $value = papi_get_qs( 'post' ) ) { 31 | return is_array( $value ) ? 0 : intval( $value ); 32 | } 33 | 34 | if ( $value = papi_get_qs( 'page_id' ) ) { 35 | return intval( $value ); 36 | } 37 | 38 | return intval( $post_id ); 39 | } 40 | 41 | return intval( $post_id ); 42 | } 43 | 44 | /** 45 | * Get post parent id. 46 | * 47 | * @return int 48 | */ 49 | function papi_get_parent_post_id() { 50 | return intval( papi_get_qs( 'post_parent' ) ); 51 | } 52 | 53 | /** 54 | * Get WordPress post type in various ways. 55 | * 56 | * @param int $post_id 57 | * 58 | * @return string 59 | */ 60 | function papi_get_post_type( $post_id = null ) { 61 | if ( $post_type = papi_get_or_post( 'post_type' ) ) { 62 | return $post_type; 63 | } 64 | 65 | $post_id = papi_get_post_id( $post_id ); 66 | 67 | if ( $post_id !== 0 ) { 68 | return strtolower( get_post_type( $post_id ) ); 69 | } 70 | 71 | $page = papi_get_qs( 'page' ); 72 | 73 | if ( is_string( $page ) && strpos( strtolower( $page ), 'papi-add-new-page,' ) !== false ) { 74 | $exploded = explode( ',', $page ); 75 | 76 | if ( empty( $exploded[1] ) ) { 77 | return ''; 78 | } 79 | 80 | return $exploded[1]; 81 | } 82 | 83 | // If only `post-new.php` without any querystrings 84 | // it would be the post post type. 85 | $req_uri = $_SERVER['REQUEST_URI']; 86 | $exploded = explode( '/', $req_uri ); 87 | $last = end( $exploded ); 88 | 89 | if ( $last === 'post-new.php' ) { 90 | return 'post'; 91 | } 92 | 93 | return ''; 94 | } 95 | 96 | /** 97 | * Get post type label. 98 | * 99 | * @param string $post_type 100 | * @param string $label 101 | * @param string $default 102 | * 103 | * @return string 104 | */ 105 | function papi_get_post_type_label( $post_type, $label, $default = '' ) { 106 | if ( ! post_type_exists( $post_type ) ) { 107 | return $default; 108 | } 109 | 110 | return get_post_type_object( $post_type )->labels->$label; 111 | } 112 | -------------------------------------------------------------------------------- /src/lib/core/slug.php: -------------------------------------------------------------------------------- 1 | capabilities ) ) { 19 | $_tabs[] = $tab; 20 | } 21 | } 22 | 23 | $tabs = papi_sort_order( $_tabs ); 24 | 25 | // Generate unique names for all tabs. 26 | $len = count( $tabs ); 27 | for ( $i = 0; $i < $len; $i ++ ) { 28 | $tabs[$i]->id = papi_html_name( $tabs[$i]->title ) . '_' . $i; 29 | } 30 | 31 | return $tabs; 32 | } 33 | 34 | /** 35 | * Create a new tab array or rendering a template tab file. 36 | * 37 | * @param string|array $file_or_options 38 | * @param array $properties 39 | * 40 | * @return Papi_Core_Tab 41 | */ 42 | function papi_tab( $file_or_options, $properties = [] ) { 43 | list( $options, $properties ) = papi_get_options_and_properties( $file_or_options, $properties, false ); 44 | 45 | return new Papi_Core_Tab( $options, $properties ); 46 | } 47 | -------------------------------------------------------------------------------- /src/lib/core/taxonomy.php: -------------------------------------------------------------------------------- 1 | term_id ) ) { 12 | return $term_id->term_id; 13 | } 14 | 15 | if ( is_numeric( $term_id ) && is_string( $term_id ) && $term_id !== '0' ) { 16 | return intval( $term_id ); 17 | } 18 | 19 | if ( is_null( $term_id ) || intval( $term_id ) === 0 ) { 20 | if ( ! papi_is_admin() && ( is_category() || is_tag() || is_tax() ) ) { 21 | return get_queried_object_id(); 22 | } else if ( $term_id = papi_get_or_post( 'term_id' ) ) { 23 | return intval( $term_id ); 24 | } else if ( $tag_id = papi_get_or_post( 'tag_ID' ) ) { 25 | return intval( $tag_id ); 26 | } 27 | } 28 | 29 | return intval( $term_id ); 30 | } 31 | 32 | /** 33 | * Get WordPress taxonomy in various ways. 34 | * 35 | * @param int $term_id 36 | * 37 | * @return string 38 | */ 39 | function papi_get_taxonomy( $term_id = null ) { 40 | if ( $taxonomy = papi_get_or_post( 'taxonomy' ) ) { 41 | return $taxonomy; 42 | } 43 | 44 | $term_id = papi_get_term_id( $term_id ); 45 | 46 | if ( ! empty( $term_id ) ) { 47 | $term = get_term( $term_id, '' ); 48 | 49 | if ( is_object( $term ) && ! is_wp_error( $term ) ) { 50 | return strtolower( $term->taxonomy ); 51 | } 52 | } 53 | 54 | return ''; 55 | } 56 | 57 | /** 58 | * Get taxonomy label. 59 | * 60 | * @param string $taxonomy 61 | * @param string $label 62 | * @param string $default 63 | * 64 | * @return string 65 | */ 66 | function papi_get_taxonomy_label( $taxonomy, $label, $default = '' ) { 67 | if ( ! taxonomy_exists( $taxonomy ) ) { 68 | return $default; 69 | } 70 | 71 | return get_taxonomy( $taxonomy )->labels->$label; 72 | } 73 | -------------------------------------------------------------------------------- /src/lib/core/template.php: -------------------------------------------------------------------------------- 1 | $value ) { 93 | $template->set_option( $key, $value ); 94 | } 95 | 96 | $result = $template; 97 | } else { 98 | $result = array_merge( (array) $template, $values ); 99 | } 100 | 101 | if ( $convert_to_object ) { 102 | return (object) $result; 103 | } 104 | 105 | return $result; 106 | } 107 | 108 | /** 109 | * Include template files from Papis custom page template meta field. 110 | * 111 | * @param string $original_template 112 | * 113 | * @return string 114 | */ 115 | function papi_template_include( $original_template ) { 116 | global $post; 117 | 118 | // Check so we only change template on single and page posts. 119 | if ( ! is_single() && ! is_page() && ! is_tag() ) { 120 | return $original_template; 121 | } 122 | 123 | /** 124 | * Modify which template is included before Papi looks for the right template. 125 | * 126 | * @param string $original_template 127 | * 128 | * @return string 129 | */ 130 | $template = apply_filters( 'papi/pre_template_include', $original_template ); 131 | 132 | if ( ! empty( $template ) && $original_template !== $template ) { 133 | return $template; 134 | } 135 | 136 | // Determine which id to use. 137 | $id = is_tag() ? get_queried_object()->term_id : $post->ID; 138 | 139 | // Only load a template if it exists. 140 | if ( $page_template = papi_get_entry_type_template( $id ) ) { 141 | if ( $template = locate_template( $page_template ) ) { 142 | /** 143 | * Change which template that is used by Papi. 144 | * 145 | * @param string $template 146 | * 147 | * @return string 148 | */ 149 | return apply_filters( 'papi/template_include', $template ); 150 | } 151 | } 152 | 153 | return $original_template; 154 | } 155 | add_filter( 'template_include', 'papi_template_include' ); 156 | -------------------------------------------------------------------------------- /src/lib/core/url.php: -------------------------------------------------------------------------------- 1 | get_id(); 31 | } 32 | 33 | $value = apply_filters( 'papi/settings/show_page_type_' . $post_type, $page_type ); 34 | 35 | if ( $value === $page_type ) { 36 | return true; 37 | } 38 | 39 | if ( ! is_bool( $value ) ) { 40 | return false; 41 | } 42 | 43 | return $value; 44 | } 45 | 46 | /** 47 | * Get standard page description for the given post type. 48 | * 49 | * @param string $post_type 50 | * 51 | * @return string 52 | */ 53 | function papi_filter_settings_standard_page_type_description( $post_type ) { 54 | $name = papi_get_post_type_label( $post_type, 'singular_name', 'Page' ); 55 | 56 | // New filter, with `type` in the filter tag. 57 | $tag = 'papi/settings/standard_page_type_description_' . $post_type; 58 | $out = apply_filters( $tag, sprintf( __( '%s with WordPress standard fields', 'papi' ), $name ) ); 59 | 60 | // Old filter, that didn't have `type` in the filter tag. 61 | // Should work until Papi 4.0.0. 62 | $tag = 'papi/settings/standard_page_description_' . $post_type; 63 | $out = apply_filters( $tag, $out ); 64 | 65 | return $out; 66 | } 67 | 68 | /** 69 | * Get standard page name for the given post type. 70 | * 71 | * @param string $post_type 72 | * 73 | * @return string 74 | */ 75 | function papi_filter_settings_standard_page_type_name( $post_type ) { 76 | $name = papi_get_post_type_label( $post_type, 'singular_name', 'Page' ); 77 | 78 | // New filter, with `type` in the filter tag. 79 | $tag = 'papi/settings/standard_page_type_name_' . $post_type; 80 | $out = apply_filters( $tag, sprintf( __( 'Standard %s', 'papi' ), $name ) ); 81 | 82 | // Old filter, that didn't have `type` in the filter tag. 83 | // Should work until Papi 4.0.0. 84 | $tag = 'papi/settings/standard_page_name_' . $post_type; 85 | $out = apply_filters( $tag, $out ); 86 | 87 | return $out; 88 | } 89 | 90 | /** 91 | * Show standard page type on the given post type. 92 | * 93 | * @param string $post_type 94 | * 95 | * @return bool 96 | */ 97 | function papi_filter_settings_show_standard_page_type( $post_type ) { 98 | return ! apply_filters( 'papi/settings/show_standard_page_type_' . $post_type, false ) === false; 99 | } 100 | 101 | /** 102 | * Show standard page type in filter dropdown on the given post type. 103 | * 104 | * @param string $post_type 105 | * 106 | * @return bool 107 | */ 108 | function papi_filter_settings_show_standard_page_type_in_filter( $post_type ) { 109 | $tag = 'papi/settings/show_standard_page_type_in_filter_' . $post_type; 110 | 111 | return ! apply_filters( $tag, papi_filter_settings_show_standard_page_type( $post_type ) ) === false; 112 | } 113 | 114 | /** 115 | * Get standard page thumbnail for the given post type. 116 | * 117 | * @param string $post_type 118 | * 119 | * @return string 120 | */ 121 | function papi_filter_settings_standard_page_type_thumbnail( $post_type ) { 122 | // New filter, with `type` in the filter tag. 123 | $tag = 'papi/settings/standard_page_type_thumbnail_' . $post_type; 124 | $out = apply_filters( $tag, '' ); 125 | 126 | // Old filter, that didn't have `type` in the filter tag. 127 | // Should work until Papi 4.0.0. 128 | $tag = 'papi/settings/standard_page_thumbnail_' . $post_type; 129 | $out = apply_filters( $tag, $out ); 130 | 131 | return $out; 132 | } 133 | -------------------------------------------------------------------------------- /src/lib/hooks/filters-taxonomy-type.php: -------------------------------------------------------------------------------- 1 | operator, $rule ); 18 | 19 | if ( $result === true || $result === false ) { 20 | return $result; 21 | } 22 | 23 | return false; 24 | } 25 | 26 | /** 27 | * Format the value of the property before it's returned to WordPress admin or the site. 28 | * 29 | * @since 3.1.0 `$meta_type` argument was added. 30 | * 31 | * @param string $type 32 | * @param mixed $value 33 | * @param string $slug 34 | * @param int $id 35 | * @param string $meta_type 36 | * 37 | * @return mixed 38 | */ 39 | function papi_filter_format_value( $type, $value, $slug, $id, $meta_type = 'post' ) { 40 | return apply_filters( 'papi/format_value/' . $type, $value, $slug, $id, $meta_type ); 41 | } 42 | 43 | /** 44 | * This filter is applied after the value is loaded in the database. 45 | * 46 | * @since 3.1.0 `$meta_type` argument was added. 47 | * 48 | * @param string $type 49 | * @param mixed $value 50 | * @param string $slug 51 | * @param int $id 52 | * @param string $meta_type 53 | * 54 | * @return mixed 55 | */ 56 | function papi_filter_load_value( $type, $value, $slug, $id, $meta_type = 'post' ) { 57 | return apply_filters( 'papi/load_value/' . $type, $value, $slug, $id, $meta_type ); 58 | } 59 | 60 | /** 61 | * Get all registered page type directories. 62 | * 63 | * @return array 64 | */ 65 | function papi_filter_settings_directories() { 66 | $directories = apply_filters( 'papi/settings/directories', [] ); 67 | 68 | if ( empty( $directories ) ) { 69 | $directories = get_template_directory() . '/page-types'; 70 | } 71 | 72 | if ( is_string( $directories ) ) { 73 | return [$directories]; 74 | } 75 | 76 | if ( ! is_array( $directories ) ) { 77 | return []; 78 | } 79 | 80 | return array_filter( $directories, function ( $directory ) { 81 | return is_string( $directory ); 82 | } ); 83 | } 84 | 85 | /** 86 | * Get the default sort order that is 1000. 87 | * 88 | * @return int 89 | */ 90 | function papi_filter_settings_sort_order() { 91 | return intval( apply_filters( 'papi/settings/sort_order', 1000 ) ); 92 | } 93 | 94 | /** 95 | * This filter is applied before the value is saved in the database. 96 | * 97 | * @since 3.1.0 `$meta_type` argument was added. 98 | * 99 | * @param string $type 100 | * @param mixed $value 101 | * @param string $slug 102 | * @param int $id 103 | * @param string $meta_type 104 | * 105 | * @return mixed 106 | */ 107 | function papi_filter_update_value( $type, $value, $slug, $id, $meta_type = 'post' ) { 108 | return apply_filters( 'papi/update_value/' . $type, $value, $slug, $id, $meta_type ); 109 | } 110 | -------------------------------------------------------------------------------- /src/lib/types/taxonomy.php: -------------------------------------------------------------------------------- 1 | name; 41 | } 42 | 43 | /** 44 | * Load the entry type id on a taxonomy. 45 | * 46 | * @param string $entry_type_id 47 | * @param string $type 48 | * 49 | * @return string 50 | */ 51 | function papi_load_taxonomy_type_id( $entry_type_id = '', $type = 'term' ) { 52 | if ( $type !== 'term' ) { 53 | return $entry_type_id; 54 | } 55 | 56 | $key = papi_get_page_type_key(); 57 | $term_id = papi_get_term_id(); 58 | $taxonomy = papi_get_taxonomy( $term_id ); 59 | 60 | // Try to load the entry type id from only taxonomy type filter. 61 | if ( empty( $entry_type_id ) ) { 62 | $entry_type_id = papi_filter_settings_only_taxonomy_type( $taxonomy ); 63 | } 64 | 65 | // If we have a term id we can load the entry type id from the term. 66 | if ( empty( $entry_type_id ) && $term_id > 0 ) { 67 | $meta_value = get_term_meta( $term_id, $key, true ); 68 | $entry_type_id = empty( $meta_value ) ? '' : $meta_value; 69 | } 70 | 71 | // Try to load the entry type from all taxonomy types and check 72 | // if only one exists of that post type. 73 | // 74 | // The same as only taxonomy type filter but without the filter. 75 | if ( empty( $entry_type_id ) ) { 76 | $key = sprintf( 'entry_type_id.taxonomy.%s', $taxonomy ); 77 | 78 | if ( papi()->exists( $key ) ) { 79 | return papi()->make( $key ); 80 | } 81 | 82 | $entries = papi_get_all_entry_types( [ 83 | 'args' => $taxonomy, 84 | 'mode' => 'include', 85 | 'types' => ['taxonomy'] 86 | ] ); 87 | 88 | if ( is_array( $entries ) ) { 89 | usort( $entries, function ( $a, $b ) { 90 | return strcmp( $a->name, $b->name ); 91 | } ); 92 | } 93 | 94 | $entries = papi_sort_order( array_reverse( $entries ) ); 95 | 96 | if ( count( $entries ) === 1 ) { 97 | $entry_type_id = $entries[0]->get_id(); 98 | 99 | papi()->bind( $key, $entry_type_id ); 100 | } 101 | } 102 | 103 | return $entry_type_id; 104 | } 105 | 106 | add_filter( 'papi/entry_type_id', 'papi_load_taxonomy_type_id', 10, 2 ); 107 | 108 | /** 109 | * Get all taxonomies Papi should work with. 110 | * 111 | * @return array 112 | */ 113 | function papi_get_taxonomies() { 114 | $taxonomies = []; 115 | $entry_types = papi_get_all_entry_types( [ 116 | 'types' => 'taxonomy' 117 | ] ); 118 | 119 | foreach ( $entry_types as $entry_type ) { 120 | $taxonomies = array_merge( $taxonomies, papi_to_array( $entry_type->taxonomy ) ); 121 | } 122 | 123 | return array_filter( array_unique( $taxonomies ) ); 124 | } 125 | 126 | /** 127 | * Set taxonomy type to a term. 128 | * 129 | * @param mixed $term_id 130 | * @param string $taxonomy_type 131 | * 132 | * @return bool 133 | */ 134 | function papi_set_taxonomy_type_id( $term_id, $taxonomy_type ) { 135 | if ( papi_entry_type_exists( $taxonomy_type ) ) { 136 | return update_term_meta( papi_get_term_id( $term_id ), papi_get_page_type_key(), $taxonomy_type ); 137 | } 138 | 139 | return false; 140 | } 141 | 142 | /** 143 | * Echo the taxonomy type name. 144 | * 145 | * @param int $term_id 146 | * 147 | * @return string 148 | */ 149 | function the_papi_taxonomy_type_name( $term_id = 0 ) { 150 | echo esc_html( papi_get_taxonomy_type_name( $term_id ) ); 151 | } 152 | -------------------------------------------------------------------------------- /src/properties/class-papi-property-bool.php: -------------------------------------------------------------------------------- 1 | get_value(); 47 | 48 | papi_render_html_tag( 'input', [ 49 | 'type' => 'hidden', 50 | 'name' => esc_attr( $this->html_name() ), 51 | 'value' => false 52 | ] ); 53 | 54 | papi_render_html_tag( 'input', [ 55 | 'checked' => ! empty( $value ), 56 | 'id' => esc_attr( $this->html_id() ), 57 | 'name' => esc_attr( $this->html_name() ), 58 | 'type' => 'checkbox' 59 | ] ); 60 | } 61 | 62 | /** 63 | * Change value after it's loaded from the database. 64 | * 65 | * @param mixed $value 66 | * @param string $slug 67 | * @param int $post_id 68 | * 69 | * @return mixed 70 | */ 71 | public function load_value( $value, $slug, $post_id ) { 72 | return is_string( $value ) && $value === '1' || $value; 73 | } 74 | 75 | /** 76 | * Prepare property value. 77 | * 78 | * @param mixed $value 79 | * 80 | * @return mixed 81 | */ 82 | protected function prepare_value( $value ) { 83 | if ( is_string( $value ) && ( $value === 'true' || $value === 'on' ) || $value === true ) { 84 | return true; 85 | } 86 | 87 | return null; 88 | } 89 | 90 | /** 91 | * Fix the database value on update. 92 | * 93 | * @param mixed $value 94 | * @param string $slug 95 | * @param int $post_id 96 | * 97 | * @return array 98 | */ 99 | public function update_value( $value, $slug, $post_id ) { 100 | return $this->prepare_value( $value ); 101 | } 102 | } 103 | -------------------------------------------------------------------------------- /src/properties/class-papi-property-checkbox.php: -------------------------------------------------------------------------------- 1 | default_value; 40 | } 41 | 42 | return array_map( 'papi_cast_string_value', $value ); 43 | } 44 | 45 | /** 46 | * Get default settings. 47 | * 48 | * @return array 49 | */ 50 | public function get_default_settings() { 51 | return [ 52 | 'items' => [], 53 | 'selected' => [] 54 | ]; 55 | } 56 | 57 | /** 58 | * Render property html. 59 | */ 60 | public function html() { 61 | $settings = $this->get_settings(); 62 | $value = papi_cast_string_value( $this->get_value() ); 63 | 64 | // Override selected setting with 65 | // database value if not empty. 66 | if ( ! papi_is_empty( $value ) ) { 67 | $settings->selected = $value; 68 | } 69 | 70 | $settings->selected = papi_to_array( $settings->selected ); 71 | 72 | echo '
'; 73 | 74 | foreach ( $settings->items as $key => $value ) { 75 | $key = is_numeric( $key ) ? $value : $key; 76 | 77 | papi_render_html_tag( 'p', [ 78 | papi_html_tag( 'label', [ 79 | 'class' => 'light', 80 | 'for' => esc_attr( $this->html_id( $key ) ), 81 | 82 | papi_html_tag( 'input', [ 83 | 'id' => esc_attr( $this->html_id( $key ) ), 84 | 'name' => esc_attr( $this->html_name() ) . '[]', 85 | 'type' => 'hidden' 86 | ] ), 87 | 88 | papi_html_tag( 'input', [ 89 | 'checked' => in_array( $value, $settings->selected, true ), 90 | 'id' => esc_attr( $this->html_id( $key ) ), 91 | 'name' => esc_attr( $this->html_name() ) . '[]', 92 | 'type' => 'checkbox', 93 | 'value' => esc_attr( $value ) 94 | ] ), 95 | 96 | esc_html( papi_convert_to_string( $key ) ) 97 | ] ) 98 | ] ); 99 | } 100 | 101 | echo '
'; 102 | } 103 | } 104 | -------------------------------------------------------------------------------- /src/properties/class-papi-property-color.php: -------------------------------------------------------------------------------- 1 | false, 17 | 'clear' => false, 18 | 'default_color' => false, 19 | 'hide' => false, 20 | 'mode' => 'hsv', 21 | 'palettes' => true, 22 | 'slider' => 'horizontal', 23 | 'show_input' => false, 24 | 'type' => 'full', 25 | 'width' => 255 26 | ]; 27 | } 28 | 29 | /** 30 | * Render property html. 31 | */ 32 | public function html() { 33 | $settings = $this->get_settings(); 34 | $value = $this->get_value(); 35 | 36 | papi_render_html_tag( 'div', [ 37 | 'class' => 'papi-property-color-picker', 38 | 39 | papi_html_tag( 'input', [ 40 | 'data-settings' => $settings, 41 | 'id' => $this->html_id(), 42 | 'name' => $this->html_name(), 43 | 'type' => $settings->show_input === true ? 'text' : 'hidden', 44 | 'value' => $value, 45 | ] ) 46 | 47 | ] ); 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /src/properties/class-papi-property-datetime.php: -------------------------------------------------------------------------------- 1 | 'YYYY-MM-DD hh:mm:ss', 16 | 'first_day' => 0, 17 | 'show_seconds' => false, 18 | 'show_time' => true, 19 | 'show_time_first' => false, 20 | 'show_week_number' => false, 21 | 'use_24_hours' => get_locale() === 'sv_SE' 22 | ]; 23 | } 24 | 25 | /** 26 | * Render property html. 27 | */ 28 | public function html() { 29 | $settings = $this->get_settings(); 30 | $value = $this->get_value(); 31 | 32 | $settings_json = [ 33 | 'i18n' => [ 34 | 'previousMonth' => __( 'Previous Month', 'papi' ), 35 | 'nextMonth' => __( 'Next Month', 'papi' ), 36 | 'midnight' => __( 'Midnight', 'papi' ), 37 | 'months' => [ 38 | __( 'January', 'papi' ), 39 | __( 'February', 'papi' ), 40 | __( 'March', 'papi' ), 41 | __( 'April', 'papi' ), 42 | __( 'May', 'papi' ), 43 | __( 'June', 'papi' ), 44 | __( 'July', 'papi' ), 45 | __( 'August', 'papi' ), 46 | __( 'September', 'papi' ), 47 | __( 'October', 'papi' ), 48 | __( 'November', 'papi' ), 49 | __( 'December', 'papi' ) 50 | ], 51 | 'noon' => __( 'Noon', 'papi' ), 52 | 'weekdays' => [ 53 | __( 'Sunday', 'papi' ), 54 | __( 'Monday', 'papi' ), 55 | __( 'Tuesday', 'papi' ), 56 | __( 'Wednesday', 'papi' ), 57 | __( 'Thursday', 'papi' ), 58 | __( 'Friday', 'papi' ), 59 | __( 'Saturday', 'papi' ) 60 | ], 61 | 'weekdaysShort' => [ 62 | __( 'Sun', 'papi' ), 63 | __( 'Mon', 'papi' ), 64 | __( 'Tue', 'papi' ), 65 | __( 'Wed', 'papi' ), 66 | __( 'Thu', 'papi' ), 67 | __( 'Fri', 'papi' ), 68 | __( 'Sat', 'papi' ) 69 | ] 70 | ] 71 | ]; 72 | 73 | // Remove i18n setting if it exists. 74 | if ( isset( $settings->i18n ) ) { 75 | unset( $settings->i18n ); 76 | } 77 | 78 | // Remove default time format if show time is false. 79 | if ( isset( $settings->show_time ) && ! $settings->show_time && isset( $settings->format ) ) { 80 | $settings->format = trim( str_replace( 'hh:mm:ss', '', $settings->format ) ); 81 | } 82 | 83 | // Convert all sneak case key to camel case. 84 | foreach ( (array) $settings as $key => $val ) { 85 | if ( ! is_string( $key ) ) { 86 | continue; 87 | } 88 | 89 | if ( $key = papi_camel_case( $key ) ) { 90 | $settings_json[$key] = $val; 91 | } 92 | } 93 | 94 | // Papi has `use24Hours` as key and Pikaday has `use24hour`. 95 | // This code will fix it. 96 | if ( isset( $settings_json['use24Hours'] ) ) { 97 | $settings_json['use24hour'] = $settings_json['use24Hours']; 98 | unset( $settings_json['use24Hours'] ); 99 | } 100 | 101 | papi_render_html_tag( 'input', [ 102 | 'class' => 'papi-property-datetime', 103 | 'data-settings' => (object) $settings_json, 104 | 'id' => $this->html_id(), 105 | 'name' => $this->html_name(), 106 | 'type' => 'text', 107 | 'value' => $value 108 | ] ); 109 | } 110 | } 111 | -------------------------------------------------------------------------------- /src/properties/class-papi-property-divider.php: -------------------------------------------------------------------------------- 1 | get_options(); 20 | $title = ''; 21 | $text = ''; 22 | 23 | if ( ! papi_is_empty( $options->title ) ) { 24 | $title = sprintf( '

%s

', esc_html( $options->title ) ); 25 | } 26 | 27 | if ( ! papi_is_empty( $options->description ) ) { 28 | $text = sprintf( '

%s

', $options->description ); 29 | } 30 | 31 | papi_render_html_tag( 'div', [ 32 | 'class' => 'papi-property-divider', 33 | 'data-papi-rule' => esc_attr( $this->html_name() ), 34 | sprintf( '%s%s', $title, $text ) 35 | ] ); 36 | } 37 | 38 | /** 39 | * Render the final html that is displayed in the table. 40 | */ 41 | protected function render_row_html() { 42 | if ( $this->get_option( 'raw' ) ) { 43 | parent::render_row_html(); 44 | } else { 45 | ?> 46 | 47 | 48 | render_property_html(); ?> 49 | 50 | 51 | get_setting( 'multiple' ) ) { 27 | return $this->is_string_items() ? $value : papi_cast_string_value( $value ); 28 | } 29 | 30 | $value = is_array( $value ) ? $value : []; 31 | 32 | if ( ! $this->is_string_items() ) { 33 | $value = array_map( 'papi_cast_string_value', $value ); 34 | } 35 | 36 | return $value; 37 | } 38 | 39 | /** 40 | * Determine if items are strings or not. 41 | * 42 | * @return bool 43 | */ 44 | public function is_string_items() { 45 | $items = $this->get_items(); 46 | 47 | if ( empty( $items ) ) { 48 | return false; 49 | } 50 | 51 | $items = array_values( $items ); 52 | 53 | return is_string( $items[0] ); 54 | } 55 | 56 | /** 57 | * Get default settings. 58 | * 59 | * @return array 60 | */ 61 | public function get_default_settings() { 62 | return [ 63 | 'allow_clear' => true, 64 | 'placeholder' => null, 65 | 'items' => [], 66 | 'multiple' => false, 67 | 'selected' => [], 68 | 'select2' => true 69 | ]; 70 | } 71 | 72 | /** 73 | * Get dropdown items. 74 | * 75 | * @return array 76 | */ 77 | protected function get_items() { 78 | return papi_to_array( $this->get_setting( 'items', [] ) ); 79 | } 80 | 81 | /** 82 | * Render property html. 83 | */ 84 | public function html() { 85 | // Setup variables needed. 86 | $settings = $this->get_settings(); 87 | $value = $this->get_value(); 88 | $options_html = []; 89 | 90 | // Properties that extends dropdown property 91 | // maybe don't have this setting. 92 | if ( ! isset( $settings->selected ) ) { 93 | $settings->selected = []; 94 | } 95 | 96 | // Properties that extends dropdown property 97 | // maybe don't have this setting. 98 | if ( ! isset( $settings->multiple ) ) { 99 | $settings->multiple = false; 100 | } 101 | 102 | // Override selected setting with 103 | // database value if not empty. 104 | if ( ! papi_is_empty( $value ) ) { 105 | $settings->selected = $value; 106 | } 107 | 108 | $placeholder = ! is_null( $settings->placeholder ) ? $settings->placeholder : ''; 109 | $placeholder = papi_is_empty( $placeholder ) ? ' ' : $placeholder; 110 | 111 | // Add placeholder if any. 112 | if ( ! is_null( $settings->placeholder ) ) { 113 | $options_html[] = sprintf( '', esc_attr( $this->get_option( 'default', '' ) ), esc_html( $placeholder ) ); 114 | } 115 | 116 | $classes = 'papi-fullwidth'; 117 | 118 | if ( $settings->select2 ) { 119 | $classes .= ' papi-component-select2'; 120 | } 121 | 122 | // Create option html tags for all items. 123 | foreach ( $this->get_items() as $key => $value ) { 124 | $key = is_numeric( $key ) ? $value : $key; 125 | 126 | if ( papi_is_empty( $key ) ) { 127 | continue; 128 | } 129 | 130 | // When a multiple we need to check with 131 | // `in_array` since the value is a array. 132 | if ( $settings->multiple ) { 133 | $selected = in_array( $value, $settings->selected, true ); 134 | } else { 135 | $selected = papi_convert_to_string( $value ) === papi_convert_to_string( $settings->selected ); 136 | } 137 | 138 | $options_html[] = papi_html_tag( 'option', [ 139 | 'data-edit-url' => false, 140 | 'data-new-url' => false, 141 | 'selected' => $selected ? 'selected' : null, 142 | 'value' => $value, 143 | esc_html( papi_convert_to_string( $key ) ) 144 | ] ); 145 | } 146 | 147 | // When selectize is empty this will be used. 148 | papi_render_html_tag( 'input', [ 149 | 'name' => $this->html_name() . ( $settings->multiple ? '[]' : '' ), 150 | 'type' => 'hidden' 151 | ] ); 152 | 153 | papi_render_html_tag( 'select', [ 154 | 'class' => $classes, 155 | 'data-allow-clear' => $settings->allow_clear, 156 | 'data-placeholder' => $placeholder, 157 | 'data-width' => '100%', 158 | 'id' => $this->html_id() . ( $settings->multiple ? '[]' : '' ), 159 | 'multiple' => $settings->multiple ? 'multiple' : null, 160 | 'name' => $this->html_name() . ( $settings->multiple ? '[]' : '' ), 161 | $options_html 162 | ] ); 163 | } 164 | 165 | /** 166 | * Change value after it's loaded from the database. 167 | * 168 | * @param mixed $value 169 | * @param string $slug 170 | * @param int $post_id 171 | * 172 | * @return mixed 173 | */ 174 | public function load_value( $value, $slug, $post_id ) { 175 | $value = maybe_unserialize( $value ); 176 | 177 | return papi_maybe_json_decode( $value, $this->get_setting( 'multiple' ) ); 178 | } 179 | 180 | /** 181 | * Update value before it's saved to the database. 182 | * 183 | * @param mixed $value 184 | * @param string $slug 185 | * @param int $post_id 186 | * 187 | * @return mixed 188 | */ 189 | public function update_value( $value, $slug, $post_id ) { 190 | if ( ! ( $value = $this->prepare_value( $value ) ) ) { 191 | return; 192 | } 193 | 194 | return papi_maybe_json_encode( $value ); 195 | } 196 | } 197 | -------------------------------------------------------------------------------- /src/properties/class-papi-property-editor.php: -------------------------------------------------------------------------------- 1 | [], 40 | 'mce_buttons_2' => [], 41 | 'mce_buttons_3' => [], 42 | 'mce_buttons_4' => [], 43 | 'media_buttons' => true, 44 | 'teeny' => false, 45 | 'drag_drop_upload' => true 46 | ]; 47 | } 48 | 49 | /** 50 | * Filter TinyMCE buttons (Visual tab). 51 | * 52 | * @param array $buttons 53 | * 54 | * @return array 55 | */ 56 | public function mce_buttons( array $buttons = [] ) { 57 | if ( $new = papi_to_array( $this->get_setting( current_filter(), [] ) ) ) { 58 | return $new; 59 | } 60 | 61 | return $buttons; 62 | } 63 | 64 | /** 65 | * Render property html. 66 | */ 67 | public function html() { 68 | $value = $this->get_value(); 69 | $value = html_entity_decode( $value ); 70 | $id = str_replace( 71 | '[', 72 | '', 73 | str_replace( ']', '', $this->html_name() ) 74 | ) . '-' . uniqid(); 75 | 76 | // Add `mce_buttons` filters. 77 | $this->add_mce_buttons(); 78 | 79 | wp_editor( $value, $id, [ 80 | 'textarea_name' => esc_attr( $this->html_name() ), 81 | 'media_buttons' => $this->get_setting( 'media_buttons', true ), 82 | 'teeny' => $this->get_setting( 'teeny', false ), 83 | 'drag_drop_upload' => $this->get_setting( 'drag_drop_upload', true ), 84 | ] ); 85 | 86 | // Remove `mce_buttons` filters. 87 | $this->reove_mce_buttons(); 88 | 89 | if ( defined( 'DOING_AJAX' ) && DOING_AJAX ) { 90 | add_filter( 'mce_external_plugins', '__return_empty_array' ); 91 | } 92 | } 93 | 94 | /** 95 | * Remove filters that filter TinyMCE buttons. 96 | */ 97 | protected function reove_mce_buttons() { 98 | for ( $i = 0; $i < 4; $i++ ) { 99 | $num = $i === 0 ? '' : '_' . ( $i + 1 ); 100 | remove_filter( 'mce_buttons' . $num, [$this, 'mce_buttons'] ); 101 | } 102 | } 103 | } 104 | -------------------------------------------------------------------------------- /src/properties/class-papi-property-email.php: -------------------------------------------------------------------------------- 1 | true 30 | ]; 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/properties/class-papi-property-group.php: -------------------------------------------------------------------------------- 1 | get_parent_property() instanceof Papi_Property_Repeater ) { 27 | $this->cache = false; 28 | } 29 | 30 | $value = parent::format_value( $value, $slug, $post_id ); 31 | 32 | return array_shift( $value ); 33 | } 34 | 35 | /** 36 | * Get default settings. 37 | * 38 | * @return array 39 | */ 40 | public function get_default_settings() { 41 | return [ 42 | 'items' => [] 43 | ]; 44 | } 45 | 46 | /** 47 | * Get settings properties. 48 | * 49 | * @return array 50 | */ 51 | protected function get_settings_properties() { 52 | $settings = $this->get_settings(); 53 | 54 | if ( is_null( $settings ) ) { 55 | return []; 56 | } 57 | 58 | return array_filter( array_map( 'papi_property', papi_to_array( $settings->items ) ), 'papi_is_property' ); 59 | } 60 | 61 | /** 62 | * Render property html. 63 | */ 64 | public function html() { 65 | $properties = $this->get_settings_properties(); 66 | $properties = $this->prepare_properties( $properties ); 67 | 68 | // Fix so group is not render over the title and description. 69 | if ( $this->get_option( 'layout' ) === 'vertical' ) { 70 | echo '
'; 71 | } 72 | 73 | echo '
'; 74 | papi_render_properties( $properties ); 75 | echo '
'; 76 | } 77 | 78 | /** 79 | * Prepare properties and set right slug and value. 80 | * 81 | * @param array $properties 82 | * 83 | * @return array 84 | */ 85 | protected function prepare_properties( $properties ) { 86 | $result = []; 87 | $value = $this->get_value(); 88 | $value = is_array( $value ) ? $value : []; 89 | 90 | foreach ( $properties as $property ) { 91 | $render_property = clone $property->get_options(); 92 | $value_slug = $property->get_slug( true ); 93 | 94 | if ( array_key_exists( $value_slug, $value ) ) { 95 | $render_property->value = $value[$value_slug]; 96 | } else { 97 | $render_property->value = null; 98 | } 99 | 100 | $render_property->slug = $this->html_name( $property ); 101 | 102 | $result[] = $render_property; 103 | } 104 | 105 | return $result; 106 | } 107 | 108 | /** 109 | * Update value before it's saved to the database. 110 | * 111 | * @param mixed $values 112 | * @param string $slug 113 | * @param int $post_id 114 | * 115 | * @return array 116 | */ 117 | public function update_value( $values, $slug, $post_id ) { 118 | if ( ! isset( $values[0] ) && ! empty( $values ) ) { 119 | $values = [$values]; 120 | } 121 | 122 | return parent::update_value( $values, $slug, $post_id ); 123 | } 124 | } 125 | -------------------------------------------------------------------------------- /src/properties/class-papi-property-hidden.php: -------------------------------------------------------------------------------- 1 | '', 23 | 'save' => false 24 | ]; 25 | } 26 | 27 | /** 28 | * Render property html. 29 | */ 30 | public function html() { 31 | $settings = $this->get_settings(); 32 | $html = papi_maybe_get_callable_value( $settings->html ); 33 | 34 | if ( $settings->save ) { 35 | $value = $this->get_value(); 36 | 37 | if ( ! empty( $value ) && is_string( $value ) ) { 38 | $html = $value; 39 | } 40 | 41 | papi_render_html_tag( 'input', [ 42 | 'name' => esc_attr( $this->html_name() ), 43 | 'type' => 'hidden', 44 | 'value' => $html 45 | ] ); 46 | } 47 | 48 | papi_render_html_tag( 'div', [ 49 | 'data-papi-rule' => esc_attr( $this->html_name() ), 50 | 'class' => 'property-html', 51 | $html 52 | ] ); 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /src/properties/class-papi-property-image.php: -------------------------------------------------------------------------------- 1 | __( 'Add image', 'papi' ), 23 | 'no_file' => __( 'No image selected', 'papi' ) 24 | ]; 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/properties/class-papi-property-number.php: -------------------------------------------------------------------------------- 1 | get_value(); 22 | 23 | return is_float( $value ) ? 'float' : $this->convert_type; 24 | } 25 | 26 | /** 27 | * Format the value of the property before it's returned 28 | * to WordPress admin or the site. 29 | * 30 | * @param mixed $value 31 | * @param string $slug 32 | * @param int $post_id 33 | * 34 | * @return float|int 35 | */ 36 | public function format_value( $value, $slug, $post_id ) { 37 | $value = is_string( $value ) && is_numeric( $value ) ? $value + 0 : $value; 38 | 39 | if ( is_float( $value ) ) { 40 | return floatval( $value ); 41 | } else { 42 | return intval( $value ); 43 | } 44 | } 45 | 46 | /** 47 | * Get default settings. 48 | * 49 | * @return array 50 | */ 51 | public function get_default_settings() { 52 | return [ 53 | 'max' => '', 54 | 'min' => '', 55 | 'step' => 'any', 56 | 'type' => 'number' 57 | ]; 58 | } 59 | 60 | /** 61 | * Get value from the database. 62 | * 63 | * @return float|int 64 | */ 65 | public function get_value() { 66 | return $this->format_value( 67 | parent::get_value(), 68 | $this->get_slug(), 69 | papi_get_post_id() 70 | ); 71 | } 72 | 73 | /** 74 | * Render property html. 75 | */ 76 | public function html() { 77 | $settings = $this->get_settings(); 78 | $value = $this->get_value(); 79 | 80 | // If range type is used change the default values if empty. 81 | if ( $settings->type === 'range' ) { 82 | $settings->max = papi_is_empty( $settings->max ) 83 | ? 100 : $settings->max; 84 | $settings->min = papi_is_empty( $settings->min ) 85 | ? 0 : $settings->min; 86 | $settings->step = papi_is_empty( $settings->step ) 87 | ? 1 : $settings->step; 88 | } 89 | 90 | if ( $settings->min !== 0 && $value < $settings->min ) { 91 | $value = $settings->min; 92 | } 93 | 94 | papi_render_html_tag( 'input', [ 95 | 'id' => $this->html_id(), 96 | 'max' => $settings->max, 97 | 'min' => $settings->min, 98 | 'name' => esc_attr( $this->html_name() ), 99 | 'step' => $settings->step, 100 | 'type' => esc_attr( $settings->type ), 101 | 'value' => $value 102 | ] ); 103 | } 104 | } 105 | -------------------------------------------------------------------------------- /src/properties/class-papi-property-radio.php: -------------------------------------------------------------------------------- 1 | [], 31 | 'selected' => '' 32 | ]; 33 | } 34 | 35 | /** 36 | * Render property html. 37 | */ 38 | public function html() { 39 | $settings = $this->get_settings(); 40 | $value = papi_cast_string_value( $this->get_value() ); 41 | 42 | // Override selected setting with 43 | // database value if not null. 44 | if ( ! papi_is_empty( $value ) ) { 45 | $settings->selected = $value; 46 | } 47 | 48 | echo '
'; 49 | 50 | foreach ( $settings->items as $key => $value ) { 51 | $key = is_numeric( $key ) ? $value : $key; 52 | 53 | papi_render_html_tag( 'p', [ 54 | papi_html_tag( 'label', [ 55 | 'class' => 'light', 56 | 'for' => esc_attr( $this->html_id( $key ) ), 57 | 58 | papi_html_tag( 'input', [ 59 | 'id' => esc_attr( $this->html_id( $key ) ), 60 | 'name' => esc_attr( $this->html_name() ), 61 | 'type' => 'radio', 62 | 'checked' => $value === $settings->selected, 63 | 'value' => $value 64 | ] ), 65 | 66 | esc_html( papi_convert_to_string( $key ) ) 67 | ] ) 68 | ] ); 69 | } 70 | 71 | echo '
'; 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /src/properties/class-papi-property-reference.php: -------------------------------------------------------------------------------- 1 | [], 17 | 'page_type' => [] 18 | ]; 19 | } 20 | 21 | /** 22 | * Render property html. 23 | */ 24 | public function html() { 25 | $post_id = papi_get_post_id(); 26 | $settings = $this->get_settings(); 27 | 28 | // Create query array for every page type. 29 | $page_types = array_map( function ( $page_type ) { 30 | return [ 31 | 'key' => papi_get_page_type_key(), 32 | 'value' => $page_type, 33 | 'compare' => 'LIKE' 34 | ]; 35 | }, papi_to_array( $settings->page_type ) ); 36 | 37 | // Add relation. 38 | $page_types['relation'] = 'OR'; 39 | 40 | // Prepare arguments for WP_Query. 41 | $args = [ 42 | 'post_type' => 'any', 43 | 'no_found_rows' => true, 44 | 'update_post_meta_cache' => false, 45 | 'update_post_term_cache' => false, 46 | 'meta_query' => $page_types 47 | ]; 48 | 49 | $posts = ( new WP_Query( $args ) )->posts; 50 | 51 | $values = []; 52 | 53 | foreach ( papi_to_array( $settings->slug ) as $slug ) { 54 | foreach ( $posts as $post ) { 55 | $val = papi_get_field( $post->ID, $slug ); 56 | 57 | $val = array_filter( papi_to_array( $val ), function ( $item ) use ( $post_id ) { 58 | return is_object( $item ) && $item->ID === $post_id; 59 | } ); 60 | 61 | if ( empty( $val ) ) { 62 | continue; 63 | } 64 | 65 | $page_type = papi_get_entry_type_by_meta_id( $post->ID ); 66 | 67 | if ( empty( $page_type ) ) { 68 | continue; 69 | } 70 | 71 | // Create the array 72 | if ( ! isset( $values[$post->post_type] ) ) { 73 | $values[$post->post_type] = []; 74 | } 75 | 76 | if ( ! isset( $values[$post->post_type][$page_type->name] ) ) { 77 | $values[$post->post_type][$page_type->name] = []; 78 | } 79 | 80 | // Add the post 81 | if ( ! isset( $values[$post->post_type][$page_type->name][$post->ID] ) && ! empty( $val ) ) { 82 | $values[$post->post_type][$page_type->name][$post->ID] = $post; 83 | } 84 | } 85 | } 86 | 87 | ?> 88 |
    89 | 90 |

    91 | 92 |

    93 | $val ): 96 | $post_type = get_post_type_object( $title ); 97 | ?> 98 |
  • 99 |

    labels->name ); ?>

    100 |

    101 |
  • 102 |
  • 103 |
    104 |
      105 | $posts ): ?> 106 |
    • 107 |

      108 |

      109 |
    • 110 |
    • 111 |
      112 | 114 | post_title ); ?> 115 | 117 |
      118 |
      119 |
    • 120 | 121 |
    122 |
    123 |
    124 |
  • 125 | 126 |
127 | settings->render ) { 27 | return $value; 28 | } 29 | 30 | ob_start(); 31 | if ( is_active_sidebar( $value ) ) { 32 | dynamic_sidebar( $value ); 33 | } 34 | 35 | return ob_get_clean(); 36 | } 37 | 38 | /** 39 | * Get default settings. 40 | * 41 | * @return array 42 | */ 43 | public function get_default_settings() { 44 | return [ 45 | 'allow_clear' => true, 46 | 'placeholder' => '', 47 | 'render' => true, 48 | 'select2' => true 49 | ]; 50 | } 51 | 52 | /** 53 | * Get registered sidebars as dropdown items. 54 | * 55 | * @return array 56 | */ 57 | public function get_items() { 58 | global $wp_registered_sidebars; 59 | $items = []; 60 | 61 | foreach ( $wp_registered_sidebars as $item ) { 62 | $items[$item['name']] = $item['id']; 63 | } 64 | 65 | ksort( $items ); 66 | 67 | return $items; 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /src/properties/class-papi-property-string.php: -------------------------------------------------------------------------------- 1 | get_setting( 'allow_html' ) && $this->input_type === 'text' ) { 27 | $value = papi_maybe_json_decode( maybe_unserialize( $value ) ); 28 | 29 | if ( ! is_string( $value ) ) { 30 | $value = ''; 31 | } 32 | 33 | $value = wp_strip_all_tags( $value ); 34 | } 35 | 36 | return $value; 37 | } 38 | 39 | /** 40 | * Get default settings. 41 | * 42 | * @return array 43 | */ 44 | public function get_default_settings() { 45 | return [ 46 | 'allow_html' => false, 47 | 'placeholder' => '' 48 | ]; 49 | } 50 | 51 | /** 52 | * Get value from the database. 53 | * 54 | * @return string 55 | */ 56 | public function get_value() { 57 | $value = $this->format_value( 58 | parent::get_value(), 59 | $this->get_slug(), 60 | papi_get_post_id() 61 | ); 62 | 63 | return $this->get_setting( 'allow_html' ) ? $value : esc_html( $value ); 64 | } 65 | 66 | /** 67 | * Render property html. 68 | */ 69 | public function html() { 70 | papi_render_html_tag( 'input', [ 71 | 'id' => esc_attr( $this->html_id() ), 72 | 'name' => esc_attr( $this->html_name() ), 73 | 'type' => esc_attr( $this->input_type ), 74 | 'value' => $this->get_value(), 75 | 'placeholder' => esc_attr( $this->get_setting( 'placeholder' ) ) 76 | ] ); 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /src/properties/class-papi-property-table.php: -------------------------------------------------------------------------------- 1 | '; 22 | $allow_html = $this->get_setting( 'allow_html' ); 23 | 24 | if ( $child ) { 25 | $html .= ''; 26 | 27 | foreach ( $arr[0] as $key => $value ) { 28 | if ( $allow_html ) { 29 | $key = html_entity_decode( $key ); 30 | } 31 | 32 | $html .= sprintf( '', $key ); 33 | } 34 | 35 | $html .= ''; 36 | } 37 | 38 | foreach ( $arr as $key => $value ) { 39 | $html .= ''; 40 | 41 | foreach ( $value as $key2 => $value2 ) { 42 | if ( is_array( $value2 ) ) { 43 | $value2 = $this->build_table( $value2, true ); 44 | } 45 | 46 | $value2 = papi_convert_to_string( $value2 ); 47 | 48 | if ( $allow_html ) { 49 | $value2 = html_entity_decode( $value2 ); 50 | } 51 | 52 | $html .= sprintf( '', $value2 ); 53 | } 54 | 55 | $html .= ''; 56 | } 57 | 58 | return $html . '
%s
%s
'; 59 | } 60 | 61 | /** 62 | * Get default settings. 63 | * 64 | * @return array 65 | */ 66 | public function get_default_settings() { 67 | return [ 68 | 'allow_html' => false, 69 | 'items' => [], 70 | ]; 71 | } 72 | 73 | /** 74 | * Render property html. 75 | */ 76 | public function html() { 77 | $value = $this->get_value(); 78 | $data = $this->get_setting( 'items' ); 79 | 80 | if ( ! is_array( $data ) ) { 81 | return; 82 | } 83 | 84 | // Convert key/value array to [key, value] value. 85 | foreach ( $data as $key => $value ) { 86 | if ( is_array( $value ) ) { 87 | continue; 88 | } 89 | 90 | $data[$key] = [$key, $value]; 91 | } 92 | 93 | echo $this->build_table( $data ); // wpcs: xss ok 94 | } 95 | } 96 | -------------------------------------------------------------------------------- /src/properties/class-papi-property-text.php: -------------------------------------------------------------------------------- 1 | get_setting( 'allow_html' ) ) { 20 | $value = maybe_unserialize( $value ); 21 | 22 | if ( ! is_string( $value ) ) { 23 | $value = ''; 24 | } 25 | 26 | $value = wp_strip_all_tags( $value ); 27 | 28 | if ( ! papi_is_admin() ) { 29 | $value = $this->get_setting( 'nl2br' ) ? nl2br( $value ) : $value; 30 | } 31 | } 32 | 33 | return $value; 34 | } 35 | 36 | /** 37 | * Get default settings. 38 | * 39 | * @return array 40 | */ 41 | public function get_default_settings() { 42 | return [ 43 | 'allow_html' => false, 44 | 'nl2br' => true 45 | ]; 46 | } 47 | 48 | /** 49 | * Get value from the database. 50 | * 51 | * @return string 52 | */ 53 | public function get_value() { 54 | $value = $this->format_value( 55 | parent::get_value(), 56 | $this->get_slug(), 57 | papi_get_post_id() 58 | ); 59 | 60 | return $this->get_setting( 'allow_html' ) ? $value : esc_html( $value ); 61 | } 62 | 63 | /** 64 | * Render property html. 65 | */ 66 | public function html() { 67 | papi_render_html_tag( 'textarea', [ 68 | 'class' => 'papi-property-text', 69 | 'id' => esc_attr( $this->html_id() ), 70 | 'name' => esc_attr( $this->html_name() ), 71 | $this->get_value() 72 | ] ); 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /src/properties/class-papi-property-url.php: -------------------------------------------------------------------------------- 1 | false 16 | ]; 17 | } 18 | 19 | /** 20 | * Render property html. 21 | */ 22 | public function html() { 23 | $settings = $this->get_settings(); 24 | 25 | papi_render_html_tag( 'input', [ 26 | 'class' => $settings->mediauploader ? 'papi-url-media-input' : null, 27 | 'id' => esc_attr( $this->html_id() ), 28 | 'name' => esc_attr( $this->html_name() ), 29 | 'type' => 'url', 30 | 'value' => $this->get_value() 31 | ] ); 32 | 33 | if ( $settings->mediauploader ) { 34 | echo ' '; 35 | 36 | papi_render_html_tag( 'input', [ 37 | 'class' => 'button papi-url-media-button', 38 | 'data-papi-action' => 'mediauploader', 39 | 'id' => esc_attr( $this->html_id() ), 40 | 'name' => esc_attr( $this->html_name() . '_button' ), 41 | 'type' => 'button', 42 | 'value' => esc_attr__( 'Select file', 'papi' ) 43 | ] ); 44 | } 45 | } 46 | 47 | /** 48 | * Change value after it's loaded from the database. 49 | * 50 | * @param mixed $value 51 | * @param string $slug 52 | * @param int $post_id 53 | * 54 | * @return mixed 55 | */ 56 | public function load_value( $value, $slug, $post_id ) { 57 | if ( filter_var( $value, FILTER_VALIDATE_URL ) ) { 58 | return $value; 59 | } 60 | } 61 | 62 | /** 63 | * Update value before it's saved to the database. 64 | * 65 | * @param mixed $value 66 | * @param string $slug 67 | * @param int $post_id 68 | * 69 | * @return mixed 70 | */ 71 | public function update_value( $value, $slug, $post_id ) { 72 | return $this->load_value( $value, $slug, $post_id ); 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /src/properties/class-papi-property-user.php: -------------------------------------------------------------------------------- 1 | ID ) ) { 27 | $value = $value->ID; 28 | } 29 | 30 | if ( is_numeric( $value ) ) { 31 | return $value === 0 ? null : new WP_User( $value ); 32 | } 33 | 34 | return $value; 35 | } 36 | 37 | /** 38 | * Get default settings. 39 | * 40 | * @return array 41 | */ 42 | public function get_default_settings() { 43 | return [ 44 | 'allow_clear' => true, 45 | 'capabilities' => [], 46 | 'placeholder' => '', 47 | 'select2' => true 48 | ]; 49 | } 50 | 51 | /** 52 | * Get value from database. 53 | * 54 | * @return int 55 | */ 56 | public function get_value() { 57 | $user = parent::get_value(); 58 | 59 | if ( is_object( $user ) && isset( $user->ID ) ) { 60 | return $user->ID; 61 | } 62 | 63 | return 0; 64 | } 65 | 66 | /** 67 | * Get users as dropdown items. 68 | * 69 | * @return array 70 | */ 71 | public function get_items() { 72 | $capabilities = papi_to_array( $this->get_setting( 'capabilities' ) ); 73 | $users = get_users(); 74 | $items = []; 75 | 76 | foreach ( $users as $user ) { 77 | $allcaps = $user->allcaps; 78 | 79 | if ( count( array_diff( $capabilities, array_keys( $allcaps ) ) ) === 0 ) { 80 | $items[$user->display_name] = $user->ID; 81 | } 82 | } 83 | 84 | ksort( $items ); 85 | 86 | return $items; 87 | } 88 | 89 | /** 90 | * Change value after it's loaded from the database. 91 | * 92 | * @param mixed $value 93 | * @param string $slug 94 | * @param int $post_id 95 | * 96 | * @return int 97 | */ 98 | public function load_value( $value, $slug, $post_id ) { 99 | return (int) $value; 100 | } 101 | 102 | /** 103 | * Update value before it's saved to the database. 104 | * 105 | * @param mixed $value 106 | * @param string $slug 107 | * @param int $post_id 108 | * 109 | * @return int 110 | */ 111 | public function update_value( $value, $slug, $post_id ) { 112 | if ( $value instanceof WP_User ) { 113 | $value = $value->ID; 114 | } 115 | 116 | return (int) $value; 117 | } 118 | } 119 | -------------------------------------------------------------------------------- /src/properties/class-papi-property.php: -------------------------------------------------------------------------------- 1 | get_option( 'value' ); 16 | 17 | if ( papi_is_empty( $value ) ) { 18 | $type = papi_get_meta_type(); 19 | $slug = $this->get_slug( true ); 20 | $value = papi_get_field( $slug, null, $type ); 21 | $post_status = get_post_status( $this->get_post_id() ); 22 | 23 | if ( papi_is_empty( $value ) && ( $post_status === false || $post_status === 'auto-draft' ) ) { 24 | $value = $this->get_option( 'default' ); 25 | } 26 | } 27 | 28 | if ( papi_is_empty( $value ) ) { 29 | return $this->default_value; 30 | } 31 | 32 | if ( $this->convert_type === 'string' ) { 33 | $value = papi_convert_to_string( $value ); 34 | } 35 | 36 | return papi_santize_data( $value ); 37 | } 38 | 39 | /** 40 | * Render property html. 41 | */ 42 | public function html() { 43 | } 44 | 45 | /** 46 | * Determine if the property can be rendered or not. 47 | * 48 | * @return bool 49 | */ 50 | public function can_render() { 51 | // Check if current user can view the property. 52 | if ( ! $this->current_user_can() ) { 53 | return false; 54 | } 55 | 56 | // A disabled property should not be rendered. 57 | if ( $this->disabled() ) { 58 | return false; 59 | } 60 | 61 | // Check language option, so we don't render properties on a different language. 62 | if ( $lang = $this->get_option( 'lang' ) ) { 63 | return in_array( papi_get_lang(), papi_to_array( $lang ), true ); 64 | } 65 | 66 | // If no valid lang query string exists we have to override the display property. 67 | return $this->get_option( 'lang' ) === false; 68 | } 69 | 70 | /** 71 | * Render the property. 72 | */ 73 | public function render() { 74 | // Bail if we can't render the property. 75 | if ( ! $this->can_render() ) { 76 | return; 77 | } 78 | 79 | // Override display with rules check. 80 | if ( $this->display() ) { 81 | $this->display = $this->render_is_allowed_by_rules(); 82 | } 83 | 84 | // Render property. 85 | $this->render_row_html(); 86 | } 87 | 88 | /** 89 | * Render AJAX request. 90 | */ 91 | public function render_ajax_request() { 92 | papi_render_property( $this ); 93 | } 94 | 95 | /** 96 | * Render the property description. 97 | */ 98 | protected function render_description_html() { 99 | if ( papi_is_empty( $this->get_option( 'description' ) ) ) { 100 | return; 101 | } 102 | 103 | papi_render_html_tag( 'p', [ 104 | papi_nl2br( $this->get_option( 'description' ) ) 105 | ] ); 106 | } 107 | 108 | /** 109 | * Output hidden input field that cointains which property is used. 110 | */ 111 | protected function render_hidden_html() { 112 | $slug = $this->get_option( 'slug' ); 113 | 114 | if ( substr( $slug, - 1 ) === ']' ) { 115 | $slug = substr( $slug, 0, - 1 ); 116 | $slug = papi_get_property_type_key( $slug ); 117 | $slug .= ']'; 118 | } else { 119 | $slug = papi_get_property_type_key( $slug ); 120 | } 121 | 122 | $slug = papify( $slug ); 123 | $options = $this->get_options(); 124 | $property_json = base64_encode( papi_maybe_json_encode( $options ) ); 125 | 126 | papi_render_html_tag( 'input', [ 127 | 'data-property' => strtolower( $this->get_option( 'type' ) ), 128 | 'name' => $slug, 129 | 'type' => 'hidden', 130 | 'value' => $property_json 131 | ] ); 132 | } 133 | 134 | /** 135 | * Render label for the property. 136 | */ 137 | protected function render_label_html() { 138 | $title = $this->get_option( 'title' ); 139 | 140 | papi_render_html_tag( 'label', [ 141 | 'for' => $this->html_id(), 142 | 'title' => trim( $title . ' ' . papi_property_require_text( $this->get_options() ) ), 143 | $title, 144 | papi_property_required_html( $this ) 145 | ] ); 146 | } 147 | 148 | /** 149 | * Render property html. 150 | */ 151 | protected function render_property_html() { 152 | papi_render_html_tag( 'div', [ 153 | 'class' => 'papi-before-html ' . $this->get_option( 'before_class' ), 154 | 'data-property' => $this->get_option( 'type' ), 155 | papi_maybe_get_callable_value( $this->get_option( 'before_html' ) ) 156 | ] ); 157 | 158 | $this->html(); 159 | 160 | papi_render_html_tag( 'div', [ 161 | 'class' => 'papi-after-html ' . $this->get_option( 'after_class' ), 162 | 'data-property' => $this->get_option( 'type' ), 163 | papi_maybe_get_callable_value( $this->get_option( 'after_html' ) ) 164 | ] ); 165 | } 166 | 167 | /** 168 | * Render the final html that is displayed in the table. 169 | */ 170 | protected function render_row_html() { 171 | $display_class = $this->display() ? '' : ' papi-hide'; 172 | $rules_class = papi_is_empty( $this->get_rules() ) ? '' : ' papi-rules-exists'; 173 | $css_class = trim( $display_class . $rules_class ); 174 | $css_class .= sprintf( ' papi-layout-%s', $this->get_option( 'layout' ) ); 175 | 176 | if ( $this->get_option( 'raw' ) ) { 177 | echo sprintf( '
', esc_attr( $css_class ) ); 178 | $this->render_property_html(); 179 | $this->render_hidden_html(); 180 | $this->render_rules_json(); 181 | echo '
'; 182 | } else { 183 | ?> 184 | 185 | get_option( 'sidebar' ) && $this->get_option( 'layout' ) === 'horizontal' ): ?> 186 | 187 | render_label_html(); 189 | $this->render_description_html(); 190 | ?> 191 | 192 | 193 | get_option( 'sidebar' ) && $this->get_option( 'layout' ) === 'horizontal' ? '' : 'colspan="2"'; ?>> 194 | get_option( 'layout' ) === 'vertical' && $this->get_option( 'sidebar' ) ) { 197 | $this->render_label_html(); 198 | $this->render_description_html(); 199 | } 200 | 201 | $this->render_property_html(); 202 | $this->render_hidden_html(); 203 | $this->render_rules_json(); 204 | ?> 205 | 206 | 207 | get_rules(); 216 | 217 | if ( empty( $rules ) ) { 218 | return; 219 | } 220 | 221 | $rules = $this->conditional->prepare_rules( $rules, $this ); 222 | 223 | papi_render_html_tag( 'script', [ 224 | 'data-papi-rule-source-slug' => $this->html_name(), 225 | 'data-papi-rules' => 'true', 226 | 'type' => 'application/json', 227 | papi_maybe_json_encode( $rules ) 228 | ] ); 229 | } 230 | } 231 | -------------------------------------------------------------------------------- /src/query/class-papi-query.php: -------------------------------------------------------------------------------- 1 | '' 19 | ]; 20 | 21 | /** 22 | * The query type. 23 | * 24 | * @var string 25 | */ 26 | protected $type; 27 | 28 | /** 29 | * The query instance. 30 | * 31 | * @var WP_Term_Query|WP_Query 32 | */ 33 | protected $query; 34 | 35 | /** 36 | * Query constructor. 37 | * 38 | * @param array $args 39 | * @param string $type 40 | */ 41 | public function __construct( array $args = [], $type = 'post' ) { 42 | $this->type = $type === 'page' ? 'post' : $type; 43 | $this->query = $this->get_query_class(); 44 | $this->args = $args; 45 | } 46 | 47 | /** 48 | * Dynamically access query properties. 49 | * 50 | * @param string $key 51 | * 52 | * @return mixed 53 | */ 54 | public function __get( $key ) { 55 | switch ( $key ) { 56 | case 'posts': 57 | case 'terms': 58 | return $this->get_result(); 59 | default: 60 | break; 61 | } 62 | } 63 | 64 | /** 65 | * Get first item of result. 66 | * 67 | * @return array 68 | */ 69 | public function first() { 70 | $result = $this->get_result(); 71 | 72 | return array_shift( $result ); 73 | } 74 | 75 | /** 76 | * Parse query arguments. 77 | * 78 | * @param array $args 79 | */ 80 | public function parse_args( array $args ) { 81 | $args = array_merge( $this->default_args, $args ); 82 | 83 | // Since a page type has defined post types we should use them. 84 | // With a fallback on `post_type` args or `any` value. 85 | if ( $this->type === 'post' ) { 86 | $args = $this->parse_post_args( $args ); 87 | } else if ( $this->type === 'term' ) { 88 | $args = $this->parse_term_args( $args ); 89 | } 90 | 91 | $this->args = $args; 92 | } 93 | 94 | /** 95 | * Parse post query arguments. 96 | * 97 | * @param array $args 98 | * 99 | * @return array 100 | */ 101 | protected function parse_post_args( array $args ) { 102 | if ( isset( $args['page_type'] ) ) { 103 | $args['entry_type'] = $args['page_type']; 104 | 105 | unset( $args['page_type'] ); 106 | } 107 | 108 | $entry_type = papi_get_entry_type_by_id( $args['entry_type'] ); 109 | 110 | if ( $entry_type instanceof Papi_Page_Type ) { 111 | $args['post_type'] = papi_to_array( $entry_type->post_type ); 112 | } else { 113 | $args['post_type'] = isset( $args['post_type'] ) ? $args['post_type'] : ''; 114 | } 115 | 116 | return $args; 117 | } 118 | 119 | /** 120 | * Parse term query arguments. 121 | * 122 | * @param array $args 123 | * 124 | * @return array 125 | */ 126 | protected function parse_term_args( array $args ) { 127 | if ( isset( $args['taxonomy_type'] ) ) { 128 | $args['entry_type'] = $args['taxonomy_type']; 129 | 130 | unset( $args['taxonomy_type'] ); 131 | } 132 | 133 | $entry_type = papi_get_entry_type_by_id( $args['entry_type'] ); 134 | 135 | if ( $entry_type instanceof Papi_Taxonomy_Type ) { 136 | $args['taxonomy'] = papi_to_array( $entry_type->taxonomy ); 137 | } else { 138 | $args['taxonomy'] = isset( $args['taxonomy'] ) ? $args['taxonomy'] : ''; 139 | } 140 | 141 | return $args; 142 | } 143 | 144 | /** 145 | * Get query object for right query type. 146 | * 147 | * @return WP_Query|WP_Term_Query 148 | */ 149 | public function get_query_class() { 150 | switch ( $this->type ) { 151 | case 'post': 152 | case 'page': 153 | return new WP_Query; 154 | case 'term': 155 | // `WP_Term_Query` was added in WordPress 4.6. 156 | if ( class_exists( 'WP_Term_Query' ) ) { 157 | return new WP_Term_Query; 158 | } 159 | 160 | break; 161 | default: 162 | break; 163 | } 164 | } 165 | 166 | /** 167 | * Get real query arguments without Papi Query specific arguments. 168 | * 169 | * @return array 170 | */ 171 | public function get_query_args() { 172 | $args = $this->args; 173 | 174 | if ( empty( $args['meta_query'] ) ) { 175 | // Add new meta key/value if `meta_key` or `meta_value` is empty. 176 | if ( empty( $args['meta_key'] ) || empty( $args['meta_value'] ) ) { 177 | $args['meta_key'] = papi_get_page_type_key(); 178 | $args['meta_value'] = $args['entry_type']; 179 | } else if ( papi_entry_type_exists( $args['entry_type'] ) ) { 180 | $item = [ 181 | 'key' => $args['meta_key'], 182 | 'value' => $args['meta_value'] 183 | ]; 184 | 185 | // Add `meta_compare` if set. 186 | if ( isset( $args['meta_compare'] ) ) { 187 | $item['compare'] = $args['meta_compare']; 188 | 189 | unset( $args['meta_compare'] ); 190 | } 191 | 192 | // Add new meta query item. 193 | $args['meta_query'][] = $item; 194 | 195 | // Add Papi entry/page type meta query. 196 | $args['meta_query'][] = [ 197 | 'key' => papi_get_page_type_key(), 198 | 'value' => $args['entry_type'] 199 | ]; 200 | 201 | // Add meta query relation when two query items. 202 | if ( isset( $args['relation'] ) ) { 203 | $args['meta_query']['relation'] = $args['relation']; 204 | } else { 205 | $args['meta_query']['relation'] = 'AND'; 206 | } 207 | 208 | unset( $args['meta_key'] ); 209 | unset( $args['meta_value'] ); 210 | } 211 | } else if ( papi_entry_type_exists( $args['entry_type'] ) ) { 212 | // Add Papi entry/page type meta query. 213 | $args['meta_query'][] = [ 214 | 'key' => papi_get_page_type_key(), 215 | 'value' => $args['entry_type'] 216 | ]; 217 | 218 | // Add meta query relation if not set. 219 | if ( ! isset( $args['meta_query']['relation'] ) ) { 220 | $args['meta_query']['relation'] = 'AND'; 221 | } 222 | } 223 | 224 | // Since the real query classes don't support 225 | // custom arguments the should be deleted. 226 | foreach ( array_keys( $this->default_args ) as $key ) { 227 | if ( isset( $args[$key] ) ) { 228 | unset( $args[$key] ); 229 | } 230 | } 231 | 232 | return $args; 233 | } 234 | 235 | /** 236 | * Get result. 237 | * 238 | * Works for all query types. 239 | * 240 | * @return array 241 | */ 242 | public function get_result() { 243 | if ( ! method_exists( $this->query, 'query' ) ) { 244 | return []; 245 | } 246 | 247 | $this->parse_args( $this->args ); 248 | 249 | return $this->query->query( $this->get_query_args() ); 250 | } 251 | 252 | /** 253 | * Get last item of result. 254 | * 255 | * @return array 256 | */ 257 | public function last() { 258 | $result = $this->get_result(); 259 | 260 | return array_pop( $result ); 261 | } 262 | } 263 | -------------------------------------------------------------------------------- /src/rest-api/class-papi-rest-api-post.php: -------------------------------------------------------------------------------- 1 | ID ) ) ) { 41 | return $post; 42 | } 43 | 44 | // Register all properties fields with register meta. 45 | foreach ( $page_type->get_properties() as $property ) { 46 | $property->register(); 47 | } 48 | 49 | // Add filter to prepare the response for a post type. 50 | add_filter( 'rest_prepare_' . $post->post_type, [$this, 'prepare_response'] ); 51 | 52 | return $post; 53 | } 54 | 55 | /** 56 | * Prepare response. 57 | * 58 | * @param WP_REST_Response $response 59 | * 60 | * @return WP_REST_Response 61 | */ 62 | public function prepare_response( $response ) { 63 | if ( ! isset( $response->data['meta'] ) ) { 64 | return $response; 65 | } 66 | 67 | foreach ( $response->data['meta'] as $key => $value ) { 68 | $response->data['meta'][$key] = papi_get_field( $key, $value, 'post' ); 69 | } 70 | 71 | return $response; 72 | } 73 | 74 | /** 75 | * Setup REST API fields. 76 | */ 77 | public function setup_fields() { 78 | if ( ! function_exists( 'register_rest_field' ) ) { 79 | return; 80 | } 81 | 82 | $post_types = papi_get_post_types(); 83 | 84 | foreach ( $post_types as $post_type ) { 85 | register_rest_field( $post_type, 'page_type', [ 86 | 'get_callback' => [$this, 'get_page_type'] 87 | ] ); 88 | } 89 | } 90 | } 91 | 92 | new Papi_REST_API_Post; 93 | -------------------------------------------------------------------------------- /src/rest-api/class-papi-rest-api-settings.php: -------------------------------------------------------------------------------- 1 | entries = papi_get_all_entry_types( [ 27 | 'types' => 'option' 28 | ] ); 29 | 30 | foreach ( $this->entries as $entry ) { 31 | foreach ( $entry->get_properties() as $property ) { 32 | $property->register( 'option' ); 33 | } 34 | } 35 | } 36 | 37 | /** 38 | * Setup request after callbacks filter so it's only runs on settings endpoint. 39 | * 40 | * When the filter is added the `rest_pre_get_setting` will be removed since it's 41 | * not used to anything good. 42 | * 43 | * @param mixed $value 44 | * 45 | * @return mixed 46 | */ 47 | public function pre_get_setting( $value ) { 48 | if ( ! has_filter( 'rest_request_after_callbacks', [$this, 'prepare_response'] ) ) { 49 | add_filter( 'rest_request_after_callbacks', [$this, 'prepare_response'] ); 50 | remove_filter( 'rest_pre_get_setting', [$this, 'pre_get_setting'] ); 51 | } 52 | 53 | return $value; 54 | } 55 | 56 | /** 57 | * Get setting value for a property. 58 | * 59 | * @param string $key 60 | * @param string $value 61 | * 62 | * @return mixed 63 | */ 64 | public function get_setting( $key, $value ) { 65 | $property = null; 66 | 67 | foreach ( (array) $this->entries as $entry ) { 68 | if ( $property = $entry->get_property( $key ) ) { 69 | break; 70 | } 71 | } 72 | 73 | if ( is_null( $property ) ) { 74 | return $value; 75 | } 76 | 77 | $value = papi_get_option( $key ); 78 | 79 | return $property->rest_prepare_value( $value ); 80 | } 81 | 82 | /** 83 | * Prepare settings response. 84 | * 85 | * @param WP_HTTP_Response $response 86 | * 87 | * @return array 88 | */ 89 | public function prepare_response( $response ) { 90 | $response = (array) $response; 91 | 92 | foreach ( $response as $key => $value ) { 93 | $setting = $this->get_setting( $key, $value ); 94 | 95 | if ( $setting !== $value ) { 96 | $response[$key] = $setting; 97 | } 98 | } 99 | 100 | return $response; 101 | } 102 | } 103 | 104 | new Papi_REST_API_Settings; 105 | -------------------------------------------------------------------------------- /src/rest-api/class-papi-rest-api.php: -------------------------------------------------------------------------------- 1 | load_files(); 10 | $this->setup_actions(); 11 | } 12 | 13 | /** 14 | * Load admin files that are not loaded by the autoload. 15 | */ 16 | protected function load_files() { 17 | require_once __DIR__ . '/class-papi-rest-api-post.php'; 18 | require_once __DIR__ . '/class-papi-rest-api-settings.php'; 19 | } 20 | 21 | /** 22 | * Register REST API routes. 23 | */ 24 | public function register_routes() { 25 | } 26 | 27 | /** 28 | * REST API init callback. 29 | */ 30 | public function rest_api_init() { 31 | papi_get_all_entry_types(); 32 | } 33 | 34 | /** 35 | * Setup actions. 36 | */ 37 | protected function setup_actions() { 38 | add_action( 'rest_api_init', [$this, 'rest_api_init'] ); 39 | add_action( 'rest_api_init', [$this, 'register_routes'] ); 40 | } 41 | } 42 | 43 | new Papi_REST_API; 44 | -------------------------------------------------------------------------------- /src/stores/class-papi-option-store.php: -------------------------------------------------------------------------------- 1 | id = 0; 23 | } 24 | 25 | /** 26 | * Load property from page type. 27 | * 28 | * @param string $slug 29 | * @param string $child_slug 30 | * 31 | * @return null|object 32 | */ 33 | public function get_property( $slug, $child_slug = '' ) { 34 | $entry_type_id = papi_get_qs( 'page' ); 35 | 36 | if ( empty( $entry_type_id ) ) { 37 | $property = null; 38 | $entry_types = papi_get_all_entry_types( [ 39 | 'types' => 'option' 40 | ] ); 41 | 42 | foreach ( $entry_types as $entry_type ) { 43 | if ( $property = $entry_type->get_property( $slug, $child_slug ) ) { 44 | break; 45 | } 46 | } 47 | 48 | if ( is_null( $property ) ) { 49 | return; 50 | } 51 | 52 | return $property; 53 | } 54 | 55 | $entry_type = papi_get_entry_type_by_id( $entry_type_id ); 56 | 57 | if ( $entry_type instanceof Papi_Option_Type === false ) { 58 | return; 59 | } 60 | 61 | if ( $property = $entry_type->get_property( $slug, $child_slug ) ) { 62 | return $this->prepare_property( $property ); 63 | } 64 | } 65 | 66 | /** 67 | * Check if it's a valid store. 68 | * 69 | * @return bool 70 | */ 71 | public function valid() { 72 | return $this->id === 0; 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /src/stores/class-papi-post-store.php: -------------------------------------------------------------------------------- 1 | id = papi_get_post_id( $id ); 29 | $this->post = get_post( $this->id ); 30 | $id = papi_get_page_type_id( $this->id ); 31 | $this->type_class = papi_get_entry_type_by_id( $id ); 32 | } 33 | 34 | /** 35 | * Get the permalink for the page. 36 | * 37 | * @return string 38 | */ 39 | public function get_permalink() { 40 | return get_permalink( $this->id ); 41 | } 42 | 43 | /** 44 | * Get the WordPress post object. 45 | * 46 | * @return WP_Post 47 | */ 48 | public function get_post() { 49 | return $this->post; 50 | } 51 | 52 | /** 53 | * Get the post status of a page. 54 | * 55 | * @return string 56 | */ 57 | public function get_status() { 58 | return get_post_status( $this->id ); 59 | } 60 | 61 | /** 62 | * Load property from page type. 63 | * 64 | * @param string $slug 65 | * @param string $child_slug 66 | * 67 | * @return null|Papi_Core_Property 68 | */ 69 | public function get_property( $slug, $child_slug = '' ) { 70 | $page_type_id = papi_get_page_type_id( $this->id ); 71 | $page_type = papi_get_entry_type_by_id( $page_type_id ); 72 | 73 | if ( $page_type instanceof Papi_Page_Type === false ) { 74 | return; 75 | } 76 | 77 | if ( $property = $page_type->get_property( $slug, $child_slug ) ) { 78 | return $this->prepare_property( $property ); 79 | } 80 | } 81 | 82 | /** 83 | * Prepare load value. 84 | * 85 | * @param Papi_Core_Property $property 86 | * @param mixed $value 87 | * 88 | * @return mixed 89 | */ 90 | protected function prepare_load_value( Papi_Core_Property $property, $value ) { 91 | if ( $property->overwrite ) { 92 | // Clear post cache to solve issue with cached post objects 93 | // when selecting post field. 94 | clean_post_cache( $this->id ); 95 | 96 | $slug = $property->get_slug( true ); 97 | $context = papi_is_admin() ? 'edit' : 'display'; 98 | $value = get_post_field( $slug, $this->id, $context ); 99 | } 100 | 101 | return $value; 102 | } 103 | 104 | /** 105 | * Check if the page has the post object and that it's not null. 106 | * 107 | * @return bool 108 | */ 109 | public function valid() { 110 | return ! is_null( $this->post ); 111 | } 112 | } 113 | -------------------------------------------------------------------------------- /src/stores/class-papi-term-store.php: -------------------------------------------------------------------------------- 1 | id = papi_get_term_id( $id ); 29 | $this->term = get_term( $this->id, '' ); 30 | $id = papi_get_taxonomy_type_id( $this->id ); 31 | $this->type_class = papi_get_entry_type_by_id( $id ); 32 | } 33 | 34 | /** 35 | * Get the permalink for the term. 36 | * 37 | * @return string 38 | */ 39 | public function get_permalink() { 40 | return get_term_link( $this->id ); 41 | } 42 | 43 | /** 44 | * Get the WordPress term object. 45 | * 46 | * @return WP_Term 47 | */ 48 | public function get_term() { 49 | return $this->term; 50 | } 51 | 52 | /** 53 | * Check if the term is a valid term object. 54 | * 55 | * @return bool 56 | */ 57 | public function valid() { 58 | return $this->term instanceof WP_Term; 59 | } 60 | 61 | /** 62 | * Load property from page type. 63 | * 64 | * @param string $slug 65 | * @param string $child_slug 66 | * 67 | * @return null|Papi_Core_Property 68 | */ 69 | public function get_property( $slug, $child_slug = '' ) { 70 | $taxonomy_type_id = papi_get_taxonomy_type_id( $this->id, 'term' ); 71 | $taxonomy_type = papi_get_entry_type_by_id( $taxonomy_type_id ); 72 | 73 | if ( $taxonomy_type instanceof Papi_Taxonomy_Type === false ) { 74 | return; 75 | } 76 | 77 | if ( $property = $taxonomy_type->get_property( $slug, $child_slug ) ) { 78 | return $this->prepare_property( $property ); 79 | } 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /src/types/class-papi-attachment-type.php: -------------------------------------------------------------------------------- 1 | post_type[0]; 31 | } 32 | 33 | /** 34 | * Setup filters. 35 | */ 36 | protected function setup_filters() { 37 | // Don't add any filters on post.php page. 38 | if ( ! isset( $_GET['post'] ) ) { 39 | add_filter( 'attachment_fields_to_edit', [$this, 'edit_attachment'], 10, 2 ); 40 | add_filter( 'attachment_fields_to_save', [$this, 'save_attachment'], 10 ); 41 | } 42 | } 43 | 44 | /** 45 | * Add attachment fields. 46 | * 47 | * @param array $form_fields 48 | * @param WP_Post $post 49 | * 50 | * @return array 51 | */ 52 | public function edit_attachment( $form_fields, $post ) { 53 | foreach ( $this->get_boxes() as $box ) { 54 | if ( ! empty( $box->title ) ) { 55 | $form_fields['papi-media-title-' . uniqid()] = [ 56 | 'label' => '', 57 | 'input' => 'html', 58 | 'html' => '

' . $box->title . '

' 59 | ]; 60 | } 61 | 62 | foreach ( $box->properties as $prop ) { 63 | // Raw output is required. 64 | $prop->raw = true; 65 | 66 | // Set post id to the property. 67 | $prop->set_post_id( $post->ID ); 68 | 69 | // Add property to form fields. 70 | $form_fields[$prop->get_slug()] = [ 71 | 'label' => $prop->title, 72 | 'input' => 'html', 73 | 'helps' => $prop->description, 74 | 'html' => papi_maybe_get_callable_value( 75 | 'papi_render_property', 76 | $prop 77 | ) 78 | ]; 79 | } 80 | } 81 | 82 | // Add nonce field. 83 | $form_fields['papi_meta_nonce'] = [ 84 | 'label' => '', 85 | 'input' => 'html', 86 | 'html' => sprintf( 87 | '', 88 | wp_create_nonce( 'papi_save_data' ) 89 | ) 90 | ]; 91 | 92 | return $form_fields; 93 | } 94 | 95 | /** 96 | * Save attachment post data. 97 | * 98 | * @param array $post 99 | * 100 | * @return array 101 | */ 102 | public function save_attachment( $post ) { 103 | update_post_meta( $post['ID'], papi_get_page_type_key(), $this->get_id() ); 104 | 105 | $handler = new Papi_Admin_Meta_Handler(); 106 | $handler->save_meta_boxes( $post['ID'], $post ); 107 | 108 | return $post; 109 | } 110 | 111 | /** 112 | * Check if the entry type is a singleton. 113 | * 114 | * @return bool 115 | */ 116 | public function singleton() { 117 | $key = sprintf( 'entry_type_id.post_type.%s', $this->get_post_type() ); 118 | 119 | if ( papi()->exists( $key ) ) { 120 | return true; 121 | } 122 | 123 | papi()->singleton( $key, $this->get_id() ); 124 | 125 | return false; 126 | } 127 | } 128 | -------------------------------------------------------------------------------- /src/types/class-papi-front-page-type.php: -------------------------------------------------------------------------------- 1 | 43 |
44 |

name ); ?>

45 | description ) ); // wpcs: xss ok ?> 46 |
47 |
48 | 49 | 50 |
51 |
52 |
53 | boxes as $box ) { 55 | do_meta_boxes( $box->id, 'normal', null ); 56 | } 57 | ?> 58 | 59 |
60 |
61 |
62 |
63 | taxonomy, true ); 56 | } 57 | 58 | /** 59 | * Should the Taxonomy Type be displayed in WordPress admin or not? 60 | * 61 | * @param string $taxonomy 62 | * 63 | * @return bool 64 | */ 65 | public function display( $taxonomy ) { 66 | return true; 67 | } 68 | 69 | /** 70 | * Get labels that should be changed 71 | * when using `fill_labels` option. 72 | * 73 | * @return array 74 | */ 75 | public function get_labels() { 76 | if ( ! $this->fill_labels ) { 77 | return $this->labels; 78 | } 79 | 80 | return array_merge( $this->labels, [ 81 | 'add_new_item' => sprintf( '%s %s', __( 'Add New', 'papi' ), $this->name ), 82 | 'edit_item' => sprintf( '%s %s', __( 'Edit', 'papi' ), $this->name ), 83 | 'view_item' => sprintf( '%s %s', __( 'View', 'papi' ), $this->name ) 84 | ] ); 85 | } 86 | 87 | /** 88 | * Setup actions. 89 | */ 90 | protected function setup_actions() { 91 | foreach ( papi_to_array( $this->taxonomy ) as $taxonomy ) { 92 | if ( is_string( $taxonomy ) && taxonomy_exists( $taxonomy ) ) { 93 | add_action( $taxonomy . '_edit_form', [$this, 'edit_form'] ); 94 | } 95 | } 96 | } 97 | 98 | /** 99 | * Render term edit form. 100 | */ 101 | public function edit_form() { 102 | ?> 103 |
104 | 105 |
106 |
107 |
108 | boxes as $box ) { 110 | do_meta_boxes( 111 | $box->id, 112 | 'normal', 113 | null 114 | ); 115 | } 116 | ?> 117 |
118 |
119 | taxonomy = papi_to_array( $this->taxonomy ); 128 | } 129 | } 130 | --------------------------------------------------------------------------------