[
  {
    "path": ".babelrc",
    "content": "{\n  \"presets\": [\"@jitesoft/main\"]\n}\n"
  },
  {
    "path": ".editorconfig",
    "content": "root = true\n\n[*]\ncharset = utf-8\nend_of_line = lf\nindent_style = space\nindent_size = 2\ninsert_final_newline = true\ntrim_trailing_whitespace = true\n\n[*.php]\nindent_style = tab\nindent_size = 4\n\n[*.md]\ntrim_trailing_whitespace = false\n\n[Makefile]\nindent_style = tab\nindent_size = 8\n\n[*.{js,jsx,ts,tsx,vue}]\nquote_type = single\n"
  },
  {
    "path": ".gitattributes",
    "content": "* text=auto\n\n/bin export-ignore\n/src/assets export-ignore\n/tests export-ignore\n.coveralls.yml export-ignore\n.gitattributes export-ignore\n.gitignore export-ignore\n.github export-ignore\n.scrutinizer.yml export-ignore\n.travis.yml export-ignore\nCONTRIBUTING.md export-ignore\nMakefile export-ignore\npackage.json export-ignore\nphpcs.xml export-ignore\nphpunit.xml.dist export-ignore\ntmp export-ignore\nVagrantfile export-ignore\nvendor export-ignore\nbehat.yml export-ignore\nwebpack.config.js export-ignore\n"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/bug_report.md",
    "content": "---\nname: Bug report\nabout: Create a report to help us improve\n\n---\n\n### What I expected\n\nA clear and concise description of what you expected to happen.\n\n### What happened instead\n\nA clear and concise description of what the bug is.\n\n### Screenshots\nIf applicable, add screenshots to help explain your problem.\n\n### Steps to reproduce\n\n1. Go to '...'\n2. Click on '....'\n3. Scroll down to '....'\n4. See error\n\n### What versions of softwares are you using?\n\n- Browser: [e.g. chrome, safari]\n- Papi: [e.g. 3.2.0]\n- WordPress: [e.g. 4.9.8]\n\n### Additional context\nAdd any other context about the problem here.\n"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/feature_request.md",
    "content": "---\nname: Feature request\nabout: Suggest an idea for this project\n\n---\n\n**Is your feature request related to a problem? Please describe.**\nA clear and concise description of what the problem is. Ex. I'm always frustrated when [...]\n\n**Describe the solution you'd like**\nA clear and concise description of what you want to happen.\n\n**Describe alternatives you've considered**\nA clear and concise description of any alternative solutions or features you've considered.\n\n**Additional context**\nAdd any other context or screenshots about the feature request here.\n"
  },
  {
    "path": ".github/issue_template.md",
    "content": "<!--\nThink you found a bug?\n======================\nUse the template below to log a new bug.\n\nHave a feature request?\n=======================\nRemove the template below and add your feature request.\n-->\n\n### What I expected\n\n### What happened instead\n\n### Steps to reproduce\n\n### What versions of softwares are you using?\n\n- Browser:\n- Papi:\n- WordPress:\n"
  },
  {
    "path": ".github/pull_request_template.md",
    "content": "### Description\n\nPlease explain the changes you made here.\n\n### Checklist\n\n- [ ] Bug fix?\n- [ ] New feature?\n- [ ] Deprecations?\n- [ ] Created tests, if possible\n"
  },
  {
    "path": ".github/workflows/php.yml",
    "content": "name: PHP Unit Tests\n\non:\n  push:\n    branches: [ master ]\n  pull_request:\n    branches: [ master ]\n\njobs:\n  build:\n    strategy:\n      matrix:\n        php-versions: ['7.4']\n    runs-on: ubuntu-latest\n\n    steps:\n    - uses: actions/checkout@v2\n\n    - name: Setup PHP with PECL extension\n      uses: shivammathur/setup-php@v2\n      with:\n        php-version: ${{ matrix.php-versions }}\n\n    - name: Start MySQL\n      run: sudo systemctl start mysql.service\n\n    - name: Validate composer.json and composer.lock\n      run: composer validate\n\n    - name: Cache Composer packages\n      id: composer-cache\n      uses: actions/cache@v2\n      with:\n        path: vendor\n        key: ${{ runner.os }}-php-${{ hashFiles('**/composer.lock') }}\n        restore-keys: |\n          ${{ runner.os }}-php-\n\n    - name: Install dependencies\n      if: steps.composer-cache.outputs.cache-hit != 'true'\n      run: composer install --prefer-dist --no-progress --no-suggest\n\n    - name: Set up test environement\n      run: vendor/frozzare/wp-test-suite/bin/install-wp-tests.sh wordpress_test root root localhost latest\n\n    - name: Run tests\n      run: vendor/bin/phpunit\n\n"
  },
  {
    "path": ".gitignore",
    "content": ".idea\nnode_modules\ntest.php\ntmp/\nbuild/\ncoverage/\nvendor\ncomposer.lock\ncomponents.js\nstyle.css\nmain.js\n"
  },
  {
    "path": "CHANGELOG.md",
    "content": "# Papi Changelog\n\n## [3.3.0]\n\n## Added\n\n* Added `papi/pre_template_include` filter to modify which template is loaded before Papi looks for a template.\n* Added `papi_get_fields` and `papi_get_term_fields` to receive all slugs and values from a Page/Taxonomy type.\n\n## [3.2.0](https://github.com/wp-papi/papi/releases/tag/v3.2.0) - 2017-09-07\n\nThe stable release of Papi 3.2.0, a lot has been added, changed, fixed and some functions has been removed (mostly core stuff). Papi 3.2.0 introduce a new core data api that is used everywhere in the plugin and by advanced properties. Upgrading from Papi 3.1.x will work and the new core data api will not create any problems.\n\nUnfortunately this 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 now new versions) since me and my colleagues use Papi internally and will be continuing doing it.\n\n## Added\n\n* Added: `body_classes` will be added to the frontend aswell and not just the admin.\n* Added: `register_meta` support for REST API.\n* Added: Edit post reference in a iframe instead of leaving the current page.\n* Added: `edit_url` setting to post property to display iframe mode.\n* Added: Page type switcher.\n* Added: `papi/get_boxes` filter so you can add boxes to a entry type that don't exists in the class.\n* Added: New ajax action for shortcodes `/papi-ajax/?action=get_shortcode&shortcode=[test]`\n* Added:  New method on `Papi_Entry_Type` to fetch all properties `get_properties`, it's useful if you would like to build something custom.\n* Added: `Papi_Query`\n* Added: Multiple select support for dropdown property.\n* Added: `site_id` option added to both box and property.\n* Added: `papi/get_property` filter added so you can modify properties when they are fetched inside Papi.\n* Added: Labels to flexible property so you can see which row is using which layout.\n* Added: `fields => ids` setting on file, image, gallery, post, term and relationship property.\n* Added: `meta_key => custom_meta_key` setting on file, image, gallery, post, term and relationship property, so you can match meta value instead of post id.\n* Added: Support for `any` post type value for a page type.\n* Added: CLI to rename meta keys for posts and terms, see [#208](https://github.com/wp-papi/papi/issues/208).\n* Added: `lang` option now supports arrays and not just strings.\n* Added: Filter to override conditional rule allowed value `papi/conditional/rule_allowed`\n* Added: Filter to modify lang query string value `papi/lang`\n* Added: Filter to modify template extension `papi/template_extension`\n\n## Changed\n\n* Changed: PHP 5.6 will be required instead of 5.5.9.\n* Changed: WordPress 4.4 will be required instead of 4.0.\n* Changed: String numeric and bool values are strings and numeric and bool values are not string values in dropdown anymore.\n* Changed: You can now use `papi_is_page_type` to check if current post is a page type with (was only used internal before).\n* Changed: `papi()->make( 'Sample_Page_Type' )` is replaced by id `papi()->make( 'simple-page-type' )` to make it more consistent.\n\n## Deprecated\n\n* Deprecated: `papi_get_page`, is deprecated and you should use `papi_get_meta_store( $id )` instead since it support both post, term and option stores and `papi_get_page` returns the value of `papi_get_meta_store` so no need for two functions that does the same thing.\n\n## Fixed\n\n* Fixed: `papi_get_entry_type_css_class` will work with taxonomies.\n* Fixed: Better peformance when core loads entry types.\n* Fixed: Group properties works with conditional rules now.\n* Fixed: Dropdown property can now handle numeric values, see [#225](https://github.com/wp-papi/papi/issues/225).\n* Fixed: Respect show standard type filter in list columns, see [#235](https://github.com/wp-papi/papi/issues/235).\n* Fixed: Complex properties should now be rendered in REST API, see [#232](https://github.com/wp-papi/papi/issues/232).\n* Fixed: Post ID will be respected all the way and it will not guess, see [#227](https://github.com/wp-papi/papi/pull/227).\n\n## Removed\n\n* Removed: HHVM testing is removed, PHP7 is more awesome.\n* Removed: `papi_delete_property_meta_value` is replaced with `papi_data_delete`, mostly use by core and advanced properties.\n* Removed: `papi_get_property_meta_value` is replaced with `papi_data_get`, mostly use by core and advanced properties.\n* Removed: `papi_update_property_meta_value` is replaced with `papi_data_update`, mostly use by core and advanced properties.\n\n## [3.1.19](https://github.com/wp-papi/papi/releases/tag/v3.1.19) - 2017-02-14\n\n* Fixed: Sanitize box id key [#215](https://github.com/wp-papi/papi/issues/215).\n* Fixed: Undefined property on file object used in file property [#217](https://github.com/wp-papi/papi/issues/217).\n* Fixed: AJAX endpoint for plain permalink structure [#218](https://github.com/wp-papi/papi/issues/218).\n* Fixed: `FORCE_SSL_ADMIN` and AJAX endpoint [#213](https://github.com/wp-papi/papi/issues/213).\n* Fixed: oEmbed cache is broken if content is empty [#210](https://github.com/wp-papi/papi/issues/210).\n* Fixed: Respect user admin language in WordPress 4.7 [#209](https://github.com/wp-papi/papi/issues/209).\n* Fixed: Sort order on properties.\n* Fixed: Autosave didn't work as it should.\n* Fixed: Repeater rows was affected when removing flexible rows when repeater was a child property to the flexible.\n\n## [3.1.18](https://github.com/wp-papi/papi/releases/tag/v3.1.18) - 2016-12-16\n\n* Fixed: Link property target didn't work correct in new WordPress [#207](https://github.com/wp-papi/papi/issues/207).\n\n## [3.1.17](https://github.com/wp-papi/papi/releases/tag/v3.1.17) - 2016-11-01\n\n* Fixed: Link property didn't save values correct.\n* Fixed: Relationship custom data can now use string id and not just int ids.\n\n## [3.1.16](https://github.com/wp-papi/papi/releases/tag/v3.1.16) - 2016-10-26\n\n* Fixed: Auto draft didn't save fields.\n* Fixed: Issues with color picker property that didn't work [#179](https://github.com/wp-papi/papi/issues/179).\n* Fixed: Editor tabs didn't work when flexible and repeater use `vertical` layout [#201](https://github.com/wp-papi/papi/issues/201).\n* Fixed: Remove duplicated database queries.\n* Fixed: `z-index` issue with flexible `add` button when placed under a editor [#198](https://github.com/wp-papi/papi/pull/198). Props [nlemoine](https://github.com/nlemoine).\n* Fixed: `nl2br` issue with text property [#199](https://github.com/wp-papi/papi/issues/199).\n\n## [3.1.15](https://github.com/wp-papi/papi/releases/tag/v3.1.15) - 2016-10-06\n\n* Fixed: Expression issues with TinyMCE in flexibles and repeaters [#196](https://github.com/wp-papi/papi/issues/196).\n* Fixed: Dot files should not be loaded if exists in types directories [#193](https://github.com/wp-papi/papi/issues/193).\n* Fixed: Cache issue when a repeater is a child property to a flexible.\n\n## [3.1.14](https://github.com/wp-papi/papi/releases/tag/v3.1.14) - 2016-09-23\n\n* Fixed: Browser issue with `selected` attribute.\n* Fixed: Drag and drop issue with repeater.\n\n## [3.1.13](https://github.com/wp-papi/papi/releases/tag/v3.1.13) - 2016-09-12\n\n* Fixed: Drag and drop issue with link property in flexible/repeater.\n* Fixed: Bug with post add new link when user is on another post type [#190](https://github.com/wp-papi/papi/pull/190).\n* Fixed: Optimize `papi_is_empty` check for arrays [#191](https://github.com/wp-papi/papi/pull/191).\n\n## [3.1.12](https://github.com/wp-papi/papi/releases/tag/v3.1.12) - 2016-09-05\n\n* Fixed: Cache issue with improved cache for flexible.\n\n## [3.1.11](https://github.com/wp-papi/papi/releases/tag/v3.1.11) - 2016-09-05\n\n* Fixed: Cache only raw database value instead of formatted value, this solves problems with shortcodes in the editor that shouldn't be cached.\n* Fixed: Improve css for flexible and repeater.\n\n## [3.1.10](https://github.com/wp-papi/papi/releases/tag/v3.1.10) - 2016-08-30\n\n* Fixed: Multiple render issue when using `papi_get_field` instead a property array.\n* Fixed: Add new page did remove custom query strings that shouldn't be removed.\n\n## [3.1.9](https://github.com/wp-papi/papi/releases/tag/v3.1.9) - 2016-08-16\n\n* Fixed: Small css fixes for repeater in flexible [#187](https://github.com/wp-papi/papi/pull/187).\n* Fixed: Remove repeater rows in flexible.\n* Fixed: Object cache not updated when adding row in repeater in flexible [#185](https://github.com/wp-papi/papi/pull/185).\n* Fixed: CSS bug: Remove is hidden for repeater in flexible [#184](https://github.com/wp-papi/papi/pull/184).\n\n## [3.1.8](https://github.com/wp-papi/papi/releases/tag/v3.1.8) - 2016-08-10\n\n* Fixed: Some properties did try to save the property key.\n\n## [3.1.7](https://github.com/wp-papi/papi/releases/tag/v3.1.7) - 2016-08-10\n\n* Fixed: Save issue with key/value arrays.\n* FIxed: Relationship setting `only_once` didn't remove items on page load [#183](https://github.com/wp-papi/papi/pull/183).\n\n## [3.1.6](https://github.com/wp-papi/papi/releases/tag/v3.1.6) - 2016-08-08\n\n* Fixed: Issue with `papi_html_tag` that caused html output issue with some properties.\n\n## [3.1.5](https://github.com/wp-papi/papi/releases/tag/v3.1.5) - 2016-08-08\n\n* Fixed: Slug with `_property` suffix didn't work.\n* Fixed: Remove empty values on post save for properties.\n\n## [3.1.4](https://github.com/wp-papi/papi/releases/tag/v3.1.4) - 2016-08-01\n\n* Fixed: PHP type error in group property.\n* Fixed: Wrong meta type was send to ajax when option page was in a post type menu.\n\n## [3.1.3](https://github.com/wp-papi/papi/releases/tag/v3.1.3) - 2016-07-27\n\n* Fixed: Cache issue with key that has `papi_` prefix.\n\n## [3.1.2](https://github.com/wp-papi/papi/releases/tag/v3.1.2) - 2016-07-27\n\n* Added: `papi_filter_settings_only_taxonomy_type` that acts the same way as `papi_filter_settings_only_page_type`\n* Fixed: UTF-8 Encoding issue with JSON encoded strings [#177](https://github.com/wp-papi/papi/pull/177).\n* Fixed: Equal hight class names on add new page view.\n\n## [3.1.1](https://github.com/wp-papi/papi/releases/tag/v3.1.1) - 2016-07-04\n\n* Fixed: Path to files was rewritten with to lowercase [#176](https://github.com/wp-papi/papi/issues/176)\n\n## [3.1.0](https://github.com/wp-papi/papi/releases/tag/v3.1.0) - 2016-06-27\n\n### Added\n\n* Added: `show_screen_options` to all types meta method that can turn off screen options tab.\n* Added: `show_help_tabs` to all types meta method that can turn off help tab.\n* Added: `help` method to all types which can be used to add help tabs.\n* Added: `help_sidebar` method to all types which can be used to add help tabs sidebar content.\n* Added: Support for taxonomy types with support for term meta.\n* Added: `wp papi term` command like post command.\n* Added: `mce_buttons` settings to editor property.\n* Added: `media_buttons`, `teeny` and `drag_drop_upload` settings to editor property.\n* Added: Vertical boxes support [#148](https://github.com/wp-papi/papi/issues/159).\n* Added: Support for saving properties on revision posts and restoring revision data.\n* Added: `display` option to box options in order to control if the box should be displayed or not.\n* Added: Support for repeaters inside repeaters.\n* Added: Support for repeaters inside flexibles.\n* Added: Support for render html in `Publish box` with `publish_box` method.\n* Added: `papi_get_entry_type_css_class` to get the css class that is added to body for a entry type.\n* Added: Support for autosaving fields.\n* Added: Support for group inside flexibles.\n* Added: Support for group inside repeaters.\n* Added: `body_classes` method to all types which can be used to add custom body classes.\n* Added: `show_permalink` to page type so permalink div can be hidden on a page type.\n* Added: `show_page_template` to hide page template dropdown by default on a page type.\n* Added: `show_page_attributes` to show page attributes box by default on a page type.\n* Added: `false` value to `slug` key to generate a unique slug that don't should be saved in the database but displayed in the admin.\n\n### Changed\n\n* Updated: Cross icon for file property with new color [#158](https://github.com/wp-papi/papi/pull/158).\n* Updated: `$id` param for `papi_get_slugs` is optional.\n* Updated: Group property is stored as a repeater but only with one row and not a standalone properties.\n\n### Removed\n\n* Removed: Papi tool page.\n* Removed: `papi_translate_keys`\n\n## [3.0.9](https://github.com/wp-papi/papi/releases/tag/v3.0.9) - 2016-05-25\n\n* Fixed: Checkbox property did not save unchecked values.\n\n## [3.0.8](https://github.com/wp-papi/papi/releases/tag/v3.0.8) - 2016-05-17\n\n* Fixed: Cache issue where admin and theme did get the same cached data, will now be saved as two caches.\n\n## [3.0.7](https://github.com/wp-papi/papi/releases/tag/v3.0.7) - 2016-05-09\n\n* Fixed: Locale should be restored after `papi_slugify` is used [#169](https://github.com/wp-papi/papi/issues/169).\n* Fixed: Show standard page type filter is used when only one page type exists.\n\n## [3.0.6](https://github.com/wp-papi/papi/releases/tag/v3.0.6) - 2016-04-14\n\n* Fixed: Attachment types used on a page type didn't load right, so the site performance was bad.\n* Fixed: Object cache issue with properties that overwrites a existing post field. Cached data was loaded in WordPress admin.\n\n## [3.0.5](https://github.com/wp-papi/papi/releases/tag/v3.0.5) - 2016-04-01\n\n* Fixed: Datetime did not work in repeater [#166](https://github.com/wp-papi/papi/issues/166).\n\n## [3.0.4](https://github.com/wp-papi/papi/releases/tag/v3.0.4) - 2016-03-23\n\n* Fixed: Require for link property [#165](https://github.com/wp-papi/papi/issues/165).\n* Fixed: Check so file exists before calling `file_get_contents` when reading page type files.\n\n## [3.0.3](https://github.com/wp-papi/papi/releases/tag/v3.0.3) - 2016-03-19\n\n* Fixed: Box options didn't work when no properties exists in the box.\n* Fixed: Some properties that was stored in options table did return null from `papi_get_option`, mostly flexible and repeater.\n* Fixed: Object cache issue with options fields that did get post id instead of zero that options should have.\n* Fixed: Check for registered directories before they are used.\n\n## [3.0.2](https://github.com/wp-papi/papi/releases/tag/v3.0.2) - 2016-03-08\n\n### Fixed\n\n* Fixed: PHP Notice/Object issue for relationship when using custom relationship data.\n* Fixed: Double fields issue for any field when using `papi_get_field` in hooks that fires earlier then `admin_init` [#153](https://github.com/wp-papi/papi/issues/153).\n\n## [3.0.1](https://github.com/wp-papi/papi/releases/tag/v3.0.1) - 2016-02-15\n\n### Fixed\n\n* Fixed: Render issue with tabs that existed in template files [#148](https://github.com/wp-papi/papi/issues/148).\n* Fixed: Edit link property did appear in the default editor.\n* Fixed: Required did not output the red wildcard [#149](https://github.com/wp-papi/papi/issues/149).\n* Fixed: Required did not output the red wildcard [#149](https://github.com/wp-papi/papi/issues/149).\n\n## [3.0.0](https://github.com/wp-papi/papi/releases/tag/v3.0.0) - 2016-02-02\n\nPapi 3.0.0 is a big release since a big piece of the core code has been refactored to improve how page type works. With 3.0.0 release we introduce `Entry Type` which is a base class that both page type and option type use. Both `box` and `tab` logic has been rewritten with new core classes and the admin classes has been divided into several smaller classes. Some internal functions has been removed or rewritten with backward compatibility.\n\nWe moved some logic from page type class to entry type class to be able the separate page type and option type class. This will make it easier to add new types to core or create plugin that has custom types.\n\n### Added\n\n* Added: Extended support for `meta`, `box` and `remove` methods [#114](https://github.com/wp-papi/papi/issues/114).\n* Added: WP CLI Support [#111](https://github.com/wp-papi/papi/issues/111).\n* Added: Group property [#112](https://github.com/wp-papi/papi/issues/112).\n* Added: `papi/before_init` action that is fired before Papi loads textdomain, classes, functions and setups the container.\n* Added: `papi/init` action that is fired after Papi loads textdomain, classes, functions and setups the container.\n* Added: `papi/loaded` action that will be the new `papi/include`. The old action is deprecated but will still work, it's fired before `papi/loaded` and will be removed in a feature version of Papi.\n* Added: `placeholder` setting to string property.\n* Added: Description to option type meta data.\n* Added: `papi/settings/column_hide_{$post_type}` for hiding type column.\n* Added: `papi/template_include` to provide support for third party templating engines.\n* Added: Layout mode to post property. It can devide into multiple select or a single select with labels (as before).\n* Added: Layout mode to term property. It can devide into multiple select or a single select with labels (as before).\n* Added: Second bool param to `papi_get_slugs` that will return only slugs if true.\n* Added: A way to handle classes with the same name in multiple directories [#107](https://github.com/wp-papi/papi/issues/107)\n* Added: When WordPress refresh nonces, Papi nonces should be refreshed too.\n\n### Changed\n\n* Flexible property will only save one layout per row. The layout key is changed from `_layout` to `_flexible_layout` since `_layout` can be a real slug. It has backward compatibility for the old layout key. This may cause problem with existing slugs that are named `layout`. To fix default value issue you need to manually add the layout value for the effected row. The slug will be something like this: `sections_0_flexible_layout`, where `sections` is your flexible slug, `0` is the row and `_flexible_layout` is the new layout key. The value should be a slug of the layout title, the same value as the old `_layout` rows that exists on each property.\n\n### Fixed\n\n* Fixed: Save post issue when using property template file and overwrited the slug [#129](https://github.com/wp-papi/papi/issues/129).\n* Fixed: Select2 clear issue [#132](https://github.com/wp-papi/papi/issues/132).\n* Fixed: Property type values like  `test-form-1` should match `Papi_Property_Test_Form_1`\n* Fixed: When using `overwrite` it should read data from the post instead of post meta [#145](https://github.com/wp-papi/papi/issues/145)\n* Fixed: Same prefix on folders should not replace all, only the current one.\n\n### Removed\n\n* Removed: `remove` method is removed.\n* Removed: No more array properties, all properties must use `papi_property` or `$this->property` to work. This because the converting of array properties is bad and some keys can't be used for other things.\n* Removed deprecated function `current_page`, was deprecated in 2.0.0.\n* Removed deprecated function `papi_field`, was deprecated in 2.0.0.\n* Removed deprecated function `papi_fields`, was deprecated in 2.0.0.\n* Removed deprecated function `papi_get_page_type_meta_value`, was deprecated in 2.0.0.\n* Removed deprecated meta methods, `page_type` and `option_type`, `meta` method should be used instead.\n\n### Upgraded\n\n* Upgraded: Moment.js from 2.10.6 to 2.11.0\n\n### Thanks\n\nThanks to all contributors and all who have tested Papi during the development.\n\nYou can find the old changelog for `2.x` [here](https://github.com/wp-papi/papi/blob/2.x/CHANGELOG.md).\n"
  },
  {
    "path": "CODE_OF_CONDUCT.md",
    "content": "# Contributor Covenant Code of Conduct\n\n## Our Pledge\n\nIn the interest of fostering an open and welcoming environment, we as\ncontributors and maintainers pledge to making participation in our project and\nour community a harassment-free experience for everyone, regardless of age, body\nsize, disability, ethnicity, gender identity and expression, level of experience,\nnationality, personal appearance, race, religion, or sexual identity and\norientation.\n\n## Our Standards\n\nExamples of behavior that contributes to creating a positive environment\ninclude:\n\n* Using welcoming and inclusive language\n* Being respectful of differing viewpoints and experiences\n* Gracefully accepting constructive criticism\n* Focusing on what is best for the community\n* Showing empathy towards other community members\n\nExamples of unacceptable behavior by participants include:\n\n* The use of sexualized language or imagery and unwelcome sexual attention or\nadvances\n* Trolling, insulting/derogatory comments, and personal or political attacks\n* Public or private harassment\n* Publishing others' private information, such as a physical or electronic\n  address, without explicit permission\n* Other conduct which could reasonably be considered inappropriate in a\n  professional setting\n\n## Our Responsibilities\n\nProject maintainers are responsible for clarifying the standards of acceptable\nbehavior and are expected to take appropriate and fair corrective action in\nresponse to any instances of unacceptable behavior.\n\nProject maintainers have the right and responsibility to remove, edit, or\nreject comments, commits, code, wiki edits, issues, and other contributions\nthat are not aligned to this Code of Conduct, or to ban temporarily or\npermanently any contributor for other behaviors that they deem inappropriate,\nthreatening, offensive, or harmful.\n\n## Scope\n\nThis Code of Conduct applies both within project spaces and in public spaces\nwhen an individual is representing the project or its community. Examples of\nrepresenting a project or community include using an official project e-mail\naddress, posting via an official social media account, or acting as an appointed\nrepresentative at an online or offline event. Representation of a project may be\nfurther defined and clarified by project maintainers.\n\n## Enforcement\n\nInstances of abusive, harassing, or otherwise unacceptable behavior may be\nreported by contacting the project team at [INSERT EMAIL ADDRESS]. All\ncomplaints will be reviewed and investigated and will result in a response that\nis deemed necessary and appropriate to the circumstances. The project team is\nobligated to maintain confidentiality with regard to the reporter of an incident.\nFurther details of specific enforcement policies may be posted separately.\n\nProject maintainers who do not follow or enforce the Code of Conduct in good\nfaith may face temporary or permanent repercussions as determined by other\nmembers of the project's leadership.\n\n## Attribution\n\nThis Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4,\navailable at [http://contributor-covenant.org/version/1/4][version]\n\n[homepage]: http://contributor-covenant.org\n[version]: http://contributor-covenant.org/version/1/4/\n"
  },
  {
    "path": "CONTRIBUTING.md",
    "content": "## Contributing to Papi\n\nEveryone is welcome to contribute with patches, bug-fixes and new features.\n\n## Ideas\n\nIf you have a idea the easiest way is to create a [issue](https://github.com/wp-papi/papi/issues).\n\n## Bugs\n\nBefore you submit a bug issue you should be able to run the tests that the project has. Then you know if the tests works or not.\n\nWhen you submit a bug issue you should write which browser you are using since Papi contains front-end code and not only back-end code.\n\nIf a test fails you should tell which one so it's easier to know what the bug is about.\n\nTry to be as detailed as possible in your bug issue so we can help you better with the issue.\n\n**Please** write how to reproduce the bug.\n\n## Which branch?\n\nThe `master` branch is unsafe but it should be used when you adding new features. When it's a bug fix that effects the current version you should do your pull request against the last stable branch. The stable branch will have the format `MAJOR.X`.\n\n## Pull requests\n\nGood pull requests with patches, improvements or new features is always welcome. They should remain focused in scope and avoid containing unrelated commits.\n\n**Please ask first** embarking on any significant pull request (e.g. implementing features, refactoring code, porting to a different language), otherwise you risk spending a lot of time working on something that the project developers might not want to merge into the project.\n\n**Please follow** the projects code style. The projects PHP code should be following the code style that is based on [WordPress code standard](https://make.wordpress.org/core/handbook/coding-standards/php/) and always use brackets.\n\n* Fork [wp-papi/papi](https://github.com/wp-papi/papi) on Github and add the upstream remote.\n\n```\ngit clone https://github.com/<your-username>/papi.git\ncd papi\ngit remote add upstream https://github.com/wp-papi/papi.git\n```\n\nThis is useful if you cloned your repo a while ago and now wants to update it.\n\n```\ngit checkout master\ngit pull upstream master\n```\n\n* Create a new branch:\n\n```\ngit checkout -b <topic-branch-name>\n```\n\n* Make sure to update, or add to the tests when appropriate.\n* Commit your changes to your fork.\n* Locally merge (or rebase) the upstream development branch into your topic branch:\n\n```\ngit pull [--rebase] upstream master\n```\n\n* Push to your branch:\n\n```\ngit push origin <topic-branch-name>\n```\n\n* [Open a Pull Request](https://help.github.com/articles/using-pull-requests/) with a clear title and description against the `master` branch or against the stable branch if it's a bug fix that effects the current version.\n\n**Note:**\nIf you are making several changes at once please divide them into multiple pull requests.\n\n## License\n\nBy contributing your code, you agree to license your contribution under the [MIT license](https://github.com/wp-papi/papi/blob/master/LICENSE).\n"
  },
  {
    "path": "LICENSE",
    "content": "The MIT License (MIT)\n\nCopyright (c) 2013-2019 Fredrik Forsmo\n\nPermission is hereby granted, free of charge, to any person obtaining a copy of\nthis software and associated documentation files (the \"Software\"), to deal in\nthe Software without restriction, including without limitation the rights to\nuse, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of\nthe Software, and to permit persons to whom the Software is furnished to do so,\nsubject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS\nFOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR\nCOPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER\nIN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN\nCONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n"
  },
  {
    "path": "Makefile",
    "content": "all: css js\n\ncss:\n\tnpm run css\n\ndeps:\n\tcomposer install\n\tnpm install -g yarn wp-pot-cli\n\tyarn\n\njs:\n\tnpm run js\n\nlint:\n\tmake lint:js\n\tmake lint:php\n\nlint\\:js:\n\tnpm run lint\n\nlint\\:php:\n\tvendor/bin/phpcs -s --extensions=php --standard=phpcs.xml src/\n\npot:\n\twp-pot --src 'src/**/*.php' --dest-file languages/papi.pot --package papi\n\nwatch:\n\tnpm run watch\n\nwatch\\:css:\n\tnpm run css\n\nwatch\\:js:\n\tnpm run js\n"
  },
  {
    "path": "README.md",
    "content": "# Papi\n\n![Papi](https://cloud.githubusercontent.com/assets/14610/9073902/16a6d906-3b05-11e5-9287-5644a96e9a82.png)\n\n[![Latest Version](https://img.shields.io/github/release/wp-papi/papi.svg?style=flat)](https://github.com/wp-papi/papi/releases)\n[![License](https://img.shields.io/packagist/l/wp-papi/papi.svg)](https://packagist.org/packages/wp-papi/papi)\n[![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)\n[![No Maintenance Intended](http://unmaintained.tech/badge.svg)](http://unmaintained.tech/)\n\n> `master` is unsafe. `3.x` is the stable branch.\n\nPapi 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.\n\nSo 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!\n\n[Visit Papi’s project page](https://wp-papi.github.io/)\n\n## A message from the author\n\nv3.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.\n\n## Installation\n\nIf you're using Composer to manage WordPress, add Papi to your project's dependencies. Run:\n\n```sh\ncomposer require wp-papi/papi\n```\n\nOr manually add it to your `composer.json`:\n\n```json\n\"require\": {\n  \"php\": \"^^7.4\",\n  \"wordpress\": \"^4.6\",\n  \"wp-papi/papi\": \"^3.2\"\n}\n```\n\n## Build CSS and JavaScript\n\nInstall dependencies:\n\n```\nmake deps\n```\n\nBuild CSS:\n\n```\nmake css\n```\n\nBuild JavaScript:\n\n```\nmake js\n```\n\n## Testing\n\nVisit the [readme](tests/README.md) file for testing.\n\n## Coding style\n\nYou 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:\n\n```\nmake lint:php\n```\n\n## Contributing\n\nVisit the [contributing](CONTRIBUTING.md) file.\n\n## Security\n\nIf 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.\n\n## License\n\nMIT © [Fredrik Forsmo](https://github.com/frozzare)\n"
  },
  {
    "path": "composer.json",
    "content": "{\n  \"name\": \"wp-papi/papi\",\n  \"type\": \"wordpress-plugin\",\n  \"license\": \"MIT\",\n  \"description\": \"WordPress Page Type API with custom fields\",\n  \"homepage\": \"https://wp-papi.github.io\",\n  \"authors\": [{\n    \"name\": \"Fredrik Forsmo\",\n    \"email\": \"fredrik.forsmo@gmail.com\",\n    \"homepage\": \"https://github.com/frozzare\"\n  }],\n  \"keywords\": [\n    \"wordpress\",\n    \"custom fields\",\n    \"page type\",\n    \"page\",\n    \"taxonomy\",\n    \"options\"\n  ],\n  \"require\": {\n    \"php\": \"^7.4\"\n  },\n  \"require-dev\": {\n    \"frozzare/wp-test-suite\": \"^1.0\",\n    \"wp-coding-standards/wpcs\": \"^0.13\"\n  },\n  \"scripts\": {\n    \"post-install-cmd\": \"if [ -f vendor/bin/phpcs ]; then \\\"vendor/bin/phpcs\\\" --config-set installed_paths vendor/wp-coding-standards/wpcs; fi\",\n    \"post-update-cmd\": \"if [ -f vendor/bin/phpcs ]; then \\\"vendor/bin/phpcs\\\" --config-set installed_paths vendor/wp-coding-standards/wpcs; fi\"\n  },\n  \"minimum-stability\": \"dev\",\n  \"prefer-stable\": true,\n  \"extra\": {\n    \"branch-alias\": {\n      \"dev-master\": \"3.3.x-dev\"\n    }\n  }\n}\n"
  },
  {
    "path": "docker-compose.yml",
    "content": "version: '3'\n\nservices:\n  mysql:\n    image: mariadb\n    environment:\n      - MYSQL_ROOT_PASSWORD=root\n  phpunit:\n    build:\n      context: ./tests\n    volumes:\n      - .:/var/www/html\n    depends_on:\n      - mysql\n    entrypoint: bash -c \"composer install && vendor/frozzare/wp-test-suite/bin/install-wp-tests.sh wordpress_test root root mysql latest && vendor/bin/phpunit\"\n"
  },
  {
    "path": "languages/papi-fr_FR.po",
    "content": "msgid \"\"\nmsgstr \"\"\n\"Project-Id-Version: Papi\\n\"\n\"POT-Creation-Date: 2015-10-13 22:23+0100\\n\"\n\"PO-Revision-Date: 2015-10-13 22:23+0100\\n\"\n\"Last-Translator: Fredrik Forsmo <fredrik.forsmo@gmail.com>\\n\"\n\"Language-Team: \\n\"\n\"Language: fr_FR\\n\"\n\"MIME-Version: 1.0\\n\"\n\"Content-Type: text/plain; charset=UTF-8\\n\"\n\"Content-Transfer-Encoding: 8bit\\n\"\n\"X-Generator: Poedit 1.7.3\\n\"\n\"X-Poedit-Basepath: .\\n\"\n\"Plural-Forms: nplurals=2; plural=(n > 1);\\n\"\n\"X-Poedit-SourceCharset: UTF-8\\n\"\n\"X-Poedit-KeywordsList: __;_e;_n:1,2;_x:1,2c;_ex:1,2c;_nx:4c,1,2;esc_attr__;esc_attr_e;esc_attr_x:1,2c;esc_html__;esc_html_e;esc_html_x:1,2c;_n_noop:1,2;_nx_noop:3c,1,2;__ngettext_noop:1,2\\n\"\n\"X-Poedit-SearchPath-0: ../src\\n\"\n\n#: ../src/admin/class-papi-admin-menu.php:109\n#: ../src/admin/class-papi-admin-menu.php:158\n#: ../src/admin/class-papi-admin-menu.php:159\n#: ../src/types/class-papi-page-type-meta.php:153\nmsgid \"Add New\"\nmsgstr \"Ajouter nouveau\"\n\n#: ../src/admin/class-papi-admin.php:45 ../src/admin/class-papi-admin.php:54\n#: ../src/papi-loader.php:55 ../src/papi-loader.php:64\nmsgid \"Cheatin&#8217; huh?\"\nmsgstr \"Alors, on triche ?\"\n\n#: ../src/admin/class-papi-admin.php:114\n#: ../src/properties/class-papi-property-flexible.php:721\n#: ../src/properties/class-papi-property-flexible.php:744\n#: ../src/properties/class-papi-property-repeater.php:703\n#: ../src/properties/class-papi-property-repeater.php:725\nmsgid \"Remove\"\nmsgstr \"Supprimer\"\n\n#: ../src/admin/class-papi-admin.php:115\nmsgid \"This fields are required:\"\nmsgstr \"Ces champs sont obligatoires :\"\n\n#: ../src/admin/class-papi-admin.php:305\nmsgid \"All types\"\nmsgstr \"\"\n\n#: ../src/admin/views/add-new-page.php:8\n#, php-format\nmsgid \"Add New %s\"\nmsgstr \"\"\n\n#: ../src/admin/views/management-page-type.php:18\nmsgid \"Tab Title\"\nmsgstr \"Title de l’onglet\"\n\n#: ../src/admin/views/management-page-type.php:19\n#: ../src/admin/views/management-page-type.php:25\nmsgid \"Sort Order\"\nmsgstr \"Ordre de tri\"\n\n#: ../src/admin/views/management-page-type.php:20\n#: ../src/admin/views/management-page-type.php:44\n#: ../src/admin/views/management-page-type.php:104\nmsgid \"Properties\"\nmsgstr \"Propriétés\"\n\n#: ../src/admin/views/management-page-type.php:22\n#: ../src/properties/class-papi-property-link.php:139\n#: ../src/properties/class-papi-property-link.php:208\nmsgid \"Title\"\nmsgstr \"Titre\"\n\n#: ../src/admin/views/management-page-type.php:23 ../src/lib/filters.php:136\nmsgid \"Type\"\nmsgstr \"Type\"\n\n#: ../src/admin/views/management-page-type.php:24\nmsgid \"Slug\"\nmsgstr \"Alias\"\n\n#: ../src/admin/views/management-page-type.php:80\nmsgid \"Cannot find the page type\"\nmsgstr \"Le type de page n’a pas été trouvé\"\n\n#: ../src/admin/views/management-page-type.php:87\nmsgid \"Overview of page type\"\nmsgstr \"Vue d’ensemble du type de page\"\n\n#: ../src/admin/views/management-page-type.php:98\nmsgid \"No meta boxes exists.\"\nmsgstr \"Aucune metaboxes existante.\"\n\n#: ../src/admin/views/management-page-type.php:107\nmsgid \"Tabs\"\nmsgstr \"Onglets\"\n\n#: ../src/admin/views/management-start.php:7\nmsgid \"Page Types\"\nmsgstr \"Types de page\"\n\n#: ../src/admin/views/management-start.php:12\nmsgid \"Name\"\nmsgstr \"Nom\"\n\n#: ../src/admin/views/management-start.php:15\nmsgid \"Page Type ID\"\nmsgstr \"Type de page ID\"\n\n#: ../src/admin/views/management-start.php:18\nmsgid \"Post types\"\nmsgstr \"\"\n\n#: ../src/admin/views/management-start.php:21\nmsgid \"Template\"\nmsgstr \"Gabarit\"\n\n#: ../src/admin/views/management-start.php:24\nmsgid \"Page Count\"\nmsgstr \"Nombre de pages\"\n\n#: ../src/admin/views/management-start.php:55\nmsgid \"Page Type has no template file\"\nmsgstr \"Le type de page n’a pas de fichier de gabarit\"\n\n#: ../src/admin/views/management-start.php:69\nmsgid \"Template file does not exist\"\nmsgstr \"Le fichier de gabarit n’existe pas\"\n\n#: ../src/admin/views/partials/add-new-item.php:14\n#: ../src/properties/class-papi-property-relationship.php:123\nmsgid \"Select\"\nmsgstr \"Sélectionner\"\n\n#: ../src/lib/filters.php:181\n#, php-format\nmsgid \"%s with WordPress standard fields\"\nmsgstr \"%s avec les champs standard WordPress\"\n\n#: ../src/lib/filters.php:197\n#, php-format\nmsgid \"Standard %s\"\nmsgstr \"Standard %s\"\n\n#: ../src/lib/property.php:353\nmsgid \"(required field)\"\nmsgstr \"(champ obligatoire)\"\n\n#: ../src/papi-loader.php:185\nmsgid \"\"\n\"WordPress 4.0 and higher required to run Papi! The plugin has now disabled \"\n\"itself.\"\nmsgstr \"\"\n\"WordPress 4.0 et supérieur est requis pour exécuter Papi ! Le plugin s’est \"\n\"maintenant désactivé.\"\n\n#: ../src/properties/class-papi-property-datetime.php:32\nmsgid \"Previous Month\"\nmsgstr \"\"\n\n#: ../src/properties/class-papi-property-datetime.php:33\nmsgid \"Next Month\"\nmsgstr \"\"\n\n#: ../src/properties/class-papi-property-datetime.php:34\nmsgid \"Midnight\"\nmsgstr \"\"\n\n#: ../src/properties/class-papi-property-datetime.php:36\nmsgid \"January\"\nmsgstr \"\"\n\n#: ../src/properties/class-papi-property-datetime.php:37\nmsgid \"February\"\nmsgstr \"\"\n\n#: ../src/properties/class-papi-property-datetime.php:38\nmsgid \"March\"\nmsgstr \"\"\n\n#: ../src/properties/class-papi-property-datetime.php:39\nmsgid \"April\"\nmsgstr \"\"\n\n#: ../src/properties/class-papi-property-datetime.php:40\nmsgid \"May\"\nmsgstr \"\"\n\n#: ../src/properties/class-papi-property-datetime.php:41\nmsgid \"June\"\nmsgstr \"\"\n\n#: ../src/properties/class-papi-property-datetime.php:42\nmsgid \"July\"\nmsgstr \"\"\n\n#: ../src/properties/class-papi-property-datetime.php:43\nmsgid \"August\"\nmsgstr \"\"\n\n#: ../src/properties/class-papi-property-datetime.php:44\nmsgid \"September\"\nmsgstr \"\"\n\n#: ../src/properties/class-papi-property-datetime.php:45\nmsgid \"October\"\nmsgstr \"\"\n\n#: ../src/properties/class-papi-property-datetime.php:46\nmsgid \"November\"\nmsgstr \"\"\n\n#: ../src/properties/class-papi-property-datetime.php:47\nmsgid \"December\"\nmsgstr \"\"\n\n#: ../src/properties/class-papi-property-datetime.php:49\nmsgid \"Noon\"\nmsgstr \"\"\n\n#: ../src/properties/class-papi-property-datetime.php:51\nmsgid \"Sunday\"\nmsgstr \"\"\n\n#: ../src/properties/class-papi-property-datetime.php:52\nmsgid \"Monday\"\nmsgstr \"\"\n\n#: ../src/properties/class-papi-property-datetime.php:53\nmsgid \"Tuesday\"\nmsgstr \"\"\n\n#: ../src/properties/class-papi-property-datetime.php:54\nmsgid \"Wednesday\"\nmsgstr \"\"\n\n#: ../src/properties/class-papi-property-datetime.php:55\nmsgid \"Thursday\"\nmsgstr \"\"\n\n#: ../src/properties/class-papi-property-datetime.php:56\nmsgid \"Friday\"\nmsgstr \"\"\n\n#: ../src/properties/class-papi-property-datetime.php:57\nmsgid \"Saturday\"\nmsgstr \"\"\n\n#: ../src/properties/class-papi-property-datetime.php:60\nmsgid \"Sun\"\nmsgstr \"\"\n\n#: ../src/properties/class-papi-property-datetime.php:61\nmsgid \"Mon\"\nmsgstr \"\"\n\n#: ../src/properties/class-papi-property-datetime.php:62\nmsgid \"Tue\"\nmsgstr \"\"\n\n#: ../src/properties/class-papi-property-datetime.php:63\nmsgid \"Wed\"\nmsgstr \"\"\n\n#: ../src/properties/class-papi-property-datetime.php:64\nmsgid \"Thu\"\nmsgstr \"\"\n\n#: ../src/properties/class-papi-property-datetime.php:65\nmsgid \"Fri\"\nmsgstr \"\"\n\n#: ../src/properties/class-papi-property-datetime.php:66\nmsgid \"Sat\"\nmsgstr \"\"\n\n#: ../src/properties/class-papi-property-file.php:104\nmsgid \"Add file\"\nmsgstr \"\"\n\n#: ../src/properties/class-papi-property-file.php:105\nmsgid \"No file selected\"\nmsgstr \"\"\n\n#: ../src/properties/class-papi-property-flexible.php:655\n#: ../src/properties/class-papi-property-repeater.php:636\nmsgid \"Add new row\"\nmsgstr \"Ajouter une nouvelle ligne\"\n\n#: ../src/properties/class-papi-property-image.php:22\nmsgid \"Add image\"\nmsgstr \"Ajouter image\"\n\n#: ../src/properties/class-papi-property-image.php:23\nmsgid \"No image selected\"\nmsgstr \"Aucune image sélectionnée\"\n\n#: ../src/properties/class-papi-property-link.php:129\n#: ../src/properties/class-papi-property-link.php:198\nmsgid \"URL\"\nmsgstr \"\"\n\n#: ../src/properties/class-papi-property-link.php:148\n#: ../src/properties/class-papi-property-link.php:217\nmsgid \"Target\"\nmsgstr \"\"\n\n#: ../src/properties/class-papi-property-link.php:151\nmsgid \"New window\"\nmsgstr \"\"\n\n#: ../src/properties/class-papi-property-link.php:151\nmsgid \"Same window\"\nmsgstr \"\"\n\n#: ../src/properties/class-papi-property-link.php:161\nmsgid \"No link selected\"\nmsgstr \"\"\n\n#: ../src/properties/class-papi-property-link.php:162\nmsgid \"Add link\"\nmsgstr \"\"\n\n#: ../src/properties/class-papi-property-link.php:165\nmsgid \"Edit link\"\nmsgstr \"\"\n\n#: ../src/properties/class-papi-property-link.php:166\nmsgid \"Remove link\"\nmsgstr \"\"\n\n#: ../src/properties/class-papi-property-reference.php:91\nmsgid \"No references exists\"\nmsgstr \"Aucune référence existe\"\n\n#: ../src/properties/class-papi-property-relationship.php:93\nmsgid \"Post\"\nmsgstr \"\"\n\n#: ../src/properties/class-papi-property-relationship.php:125\nmsgid \"Name (alphabetically)\"\nmsgstr \"Nom (alphabétiquement)\"\n\n#: ../src/properties/class-papi-property-relationship.php:133\nmsgid \"Post created date (ascending)\"\nmsgstr \"Article par date de création (croissant)\"\n\n#: ../src/properties/class-papi-property-relationship.php:137\nmsgid \"Post created date (descending)\"\nmsgstr \"Article par date de création (décroissant)\"\n\n#: ../src/properties/class-papi-property-relationship.php:141\nmsgid \"Post id (ascending)\"\nmsgstr \"ID article (croissant)\"\n\n#: ../src/properties/class-papi-property-relationship.php:146\nmsgid \"Post id (descending)\"\nmsgstr \"ID article (décroissant)\"\n\n#: ../src/properties/class-papi-property-relationship.php:151\nmsgid \"Post order value (ascending)\"\nmsgstr \"Article par champ ordre (croissant)\"\n\n#: ../src/properties/class-papi-property-relationship.php:155\nmsgid \"Post order value (descending)\"\nmsgstr \"Article par champ ordre (décroissant)\"\n\n#: ../src/properties/class-papi-property-relationship.php:159\nmsgid \"Post modified date (ascending)\"\nmsgstr \"Article par date de modification (croissant)\"\n\n#: ../src/properties/class-papi-property-relationship.php:164\nmsgid \"Post modified date (descending)\"\nmsgstr \"Article par date de modification (décroissant)\"\n\n#: ../src/properties/class-papi-property-relationship.php:254\nmsgid \"Search\"\nmsgstr \"Recherche\"\n\n#: ../src/properties/class-papi-property-relationship.php:259\nmsgid \"Sort by\"\nmsgstr \"Trier par\"\n\n#: ../src/properties/class-papi-property-term.php:65\nmsgid \"No taxonomy defined for term property\"\nmsgstr \"\"\n\n#: ../src/properties/class-papi-property-url.php:42\nmsgid \"Select file\"\nmsgstr \"Sélectionnez un fichier\"\n\n#: ../src/types/class-papi-page-type-meta.php:158\nmsgid \"Edit\"\nmsgstr \"Modifier\"\n\n#: ../src/types/class-papi-page-type-meta.php:163\nmsgid \"View\"\nmsgstr \"Voir\"\n\n#~ msgid \"Show all page types\"\n#~ msgstr \"Voir tous les types de pages\"\n\n#~ msgid \"Add new page type\"\n#~ msgstr \"Ajouter une nouvelle page\"\n\n#~ msgid \"Search page types\"\n#~ msgstr \"Recherche dans les types de page\"\n\n#~ msgid \"Back to list\"\n#~ msgstr \"Retour à la liste\"\n\n#~ msgid \"Page Type\"\n#~ msgstr \"Type de page\"\n"
  },
  {
    "path": "languages/papi-sv_SE.po",
    "content": "msgid \"\"\nmsgstr \"\"\n\"Project-Id-Version: Papi\\n\"\n\"Report-Msgid-Bugs-To: \\n\"\n\"POT-Creation-Date: \\n\"\n\"PO-Revision-Date: 2017-03-20 11:51+0100\\n\"\n\"Last-Translator: Rasmus Bengtsson <rasmus@isotop.se>\\n\"\n\"Language-Team: \\n\"\n\"Language: sv_SE\\n\"\n\"MIME-Version: 1.0\\n\"\n\"Content-Type: text/plain; charset=UTF-8\\n\"\n\"Content-Transfer-Encoding: 8bit\\n\"\n\"X-Generator: Poedit 1.8.12\\n\"\n\"X-Poedit-Basepath: .\\n\"\n\"Plural-Forms: nplurals=2; plural=(n != 1);\\n\"\n\"X-Poedit-SourceCharset: UTF-8\\n\"\n\"X-Poedit-KeywordsList: __;_e;_n:1,2;_x:1,2c;_ex:1,2c;_nx:4c,1,2;esc_attr__;\"\n\"esc_attr_e;esc_attr_x:1,2c;esc_html__;esc_html_e;esc_html_x:1,2c;\"\n\"_n_noop:1,2;_nx_noop:3c,1,2;__ngettext_noop:1,2\\n\"\n\"X-Poedit-SearchPath-0: ../src\\n\"\n\n#: ../src/papi-loader.php:75, ../src/papi-loader.php:84,\n#: ../src/admin/class-papi-admin.php:38, ../src/admin/class-papi-admin.php:47\nmsgid \"Cheatin&#8217; huh?\"\nmsgstr \"Fuskar du?\"\n\n#: ../src/papi-loader.php:244\nmsgid \"\"\n\"WordPress 4.4 and higher required to run Papi! The plugin has now disabled \"\n\"itself.\"\nmsgstr \"\"\n\"WordPress 4.4 eller högre krävs för att köra Papi! Tillägget har nu \"\n\"inaktiverat sig.\"\n\n#: ../src/admin/class-papi-admin-assets.php:51\nmsgid \"Close\"\nmsgstr \"Stäng\"\n\n#: ../src/admin/class-papi-admin-assets.php:52,\n#: ../src/admin/class-papi-admin-page-type-switcher.php:39,\n#: ../src/types/class-papi-page-type.php:204,\n#: ../src/types/class-papi-taxonomy-type.php:67\nmsgid \"Edit\"\nmsgstr \"Redigera\"\n\n#: ../src/admin/class-papi-admin-assets.php:53,\n#: ../src/properties/class-papi-property-flexible.php:824,\n#: ../src/properties/class-papi-property-flexible.php:847,\n#: ../src/properties/class-papi-property-repeater.php:790,\n#: ../src/properties/class-papi-property-repeater.php:812\nmsgid \"Remove\"\nmsgstr \"Ta bort\"\n\n#: ../src/admin/class-papi-admin-assets.php:54\nmsgid \"This fields are required:\"\nmsgstr \"Dessa fält är obligatoriska:\"\n\n#: ../src/admin/class-papi-admin-columns.php:68\nmsgid \"Type\"\nmsgstr \"Typ\"\n\n#: ../src/admin/class-papi-admin-columns.php:175\nmsgid \"All types\"\nmsgstr \"Alla typer\"\n\n#: ../src/admin/class-papi-admin-entry-taxonomy.php:50\nmsgid \"%s type\"\nmsgstr \"%styp\"\n\n#: ../src/admin/class-papi-admin-menu.php:125,\n#: ../src/admin/class-papi-admin-menu.php:172,\n#: ../src/admin/class-papi-admin-menu.php:173,\n#: ../src/types/class-papi-page-type.php:203,\n#: ../src/types/class-papi-taxonomy-type.php:66\nmsgid \"Add New\"\nmsgstr \"Skapa ny\"\n\n#: ../src/admin/class-papi-admin-page-type-switcher.php:35\nmsgid \"Page Type:\"\nmsgstr \"Sidtyp:\"\n\n#: ../src/admin/class-papi-admin-page-type-switcher.php:57\nmsgid \"OK\"\nmsgstr \"OK\"\n\n#: ../src/admin/class-papi-admin-page-type-switcher.php:58\nmsgid \"Cancel\"\nmsgstr \"Avbryt\"\n\n#: ../src/admin/class-papi-admin.php:185\nmsgid \"View Papi Documentation\"\nmsgstr \"Läs Papi dokumentationen\"\n\n#: ../src/admin/class-papi-admin.php:185\nmsgid \"Docs\"\nmsgstr \"Dokumentation\"\n\n#: ../src/properties/class-papi-property-datetime.php:32\nmsgid \"Previous Month\"\nmsgstr \"Föregående månad\"\n\n#: ../src/properties/class-papi-property-datetime.php:33\nmsgid \"Next Month\"\nmsgstr \"Nästa månad\"\n\n#: ../src/properties/class-papi-property-datetime.php:34\nmsgid \"Midnight\"\nmsgstr \"Midnatt\"\n\n#: ../src/properties/class-papi-property-datetime.php:36\nmsgid \"January\"\nmsgstr \"Januari\"\n\n#: ../src/properties/class-papi-property-datetime.php:37\nmsgid \"February\"\nmsgstr \"Februari\"\n\n#: ../src/properties/class-papi-property-datetime.php:38\nmsgid \"March\"\nmsgstr \"Mars\"\n\n#: ../src/properties/class-papi-property-datetime.php:39\nmsgid \"April\"\nmsgstr \"April\"\n\n#: ../src/properties/class-papi-property-datetime.php:40\nmsgid \"May\"\nmsgstr \"Maj\"\n\n#: ../src/properties/class-papi-property-datetime.php:41\nmsgid \"June\"\nmsgstr \"Juni\"\n\n#: ../src/properties/class-papi-property-datetime.php:42\nmsgid \"July\"\nmsgstr \"Juli\"\n\n#: ../src/properties/class-papi-property-datetime.php:43\nmsgid \"August\"\nmsgstr \"Augusti\"\n\n#: ../src/properties/class-papi-property-datetime.php:44\nmsgid \"September\"\nmsgstr \"September\"\n\n#: ../src/properties/class-papi-property-datetime.php:45\nmsgid \"October\"\nmsgstr \"Oktober\"\n\n#: ../src/properties/class-papi-property-datetime.php:46\nmsgid \"November\"\nmsgstr \"November\"\n\n#: ../src/properties/class-papi-property-datetime.php:47\nmsgid \"December\"\nmsgstr \"December\"\n\n#: ../src/properties/class-papi-property-datetime.php:49\nmsgid \"Noon\"\nmsgstr \"Eftermiddag\"\n\n#: ../src/properties/class-papi-property-datetime.php:51\nmsgid \"Sunday\"\nmsgstr \"Söndag\"\n\n#: ../src/properties/class-papi-property-datetime.php:52\nmsgid \"Monday\"\nmsgstr \"Måndag\"\n\n#: ../src/properties/class-papi-property-datetime.php:53\nmsgid \"Tuesday\"\nmsgstr \"Tisdag\"\n\n#: ../src/properties/class-papi-property-datetime.php:54\nmsgid \"Wednesday\"\nmsgstr \"Onsdag\"\n\n#: ../src/properties/class-papi-property-datetime.php:55\nmsgid \"Thursday\"\nmsgstr \"Torsdag\"\n\n#: ../src/properties/class-papi-property-datetime.php:56\nmsgid \"Friday\"\nmsgstr \"Fredag\"\n\n#: ../src/properties/class-papi-property-datetime.php:57\nmsgid \"Saturday\"\nmsgstr \"Lördag\"\n\n#: ../src/properties/class-papi-property-datetime.php:60\nmsgid \"Sun\"\nmsgstr \"Sön\"\n\n#: ../src/properties/class-papi-property-datetime.php:61\nmsgid \"Mon\"\nmsgstr \"Mån\"\n\n#: ../src/properties/class-papi-property-datetime.php:62\nmsgid \"Tue\"\nmsgstr \"Tis\"\n\n#: ../src/properties/class-papi-property-datetime.php:63\nmsgid \"Wed\"\nmsgstr \"Ons\"\n\n#: ../src/properties/class-papi-property-datetime.php:64\nmsgid \"Thu\"\nmsgstr \"Tor\"\n\n#: ../src/properties/class-papi-property-datetime.php:65\nmsgid \"Fri\"\nmsgstr \"Fre\"\n\n#: ../src/properties/class-papi-property-datetime.php:66\nmsgid \"Sat\"\nmsgstr \"Lör\"\n\n#: ../src/properties/class-papi-property-file.php:117\nmsgid \"Add file\"\nmsgstr \"Lägg till fil\"\n\n#: ../src/properties/class-papi-property-file.php:118\nmsgid \"No file selected\"\nmsgstr \"Ingen fil vald\"\n\n#: ../src/properties/class-papi-property-image.php:22\nmsgid \"Add image\"\nmsgstr \"Lägg till bild\"\n\n#: ../src/properties/class-papi-property-image.php:23\nmsgid \"No image selected\"\nmsgstr \"Ingen bild vald\"\n\n#: ../src/properties/class-papi-property-link.php:127,\n#: ../src/properties/class-papi-property-link.php:240\nmsgid \"URL\"\nmsgstr \"URL\"\n\n#: ../src/properties/class-papi-property-link.php:135,\n#: ../src/properties/class-papi-property-link.php:250\nmsgid \"Title\"\nmsgstr \"Rubrik\"\n\n#: ../src/properties/class-papi-property-link.php:142,\n#: ../src/properties/class-papi-property-link.php:259\nmsgid \"Target\"\nmsgstr \"Mål\"\n\n#: ../src/properties/class-papi-property-link.php:144,\n#: ../src/properties/class-papi-property-link.php:263\nmsgid \"New window\"\nmsgstr \"Nytt fönster\"\n\n#: ../src/properties/class-papi-property-link.php:144,\n#: ../src/properties/class-papi-property-link.php:263\nmsgid \"Same window\"\nmsgstr \"Samma fönster\"\n\n#: ../src/properties/class-papi-property-link.php:154\nmsgid \"No link selected\"\nmsgstr \"Ingen länk vald\"\n\n#: ../src/properties/class-papi-property-link.php:155\nmsgid \"Add link\"\nmsgstr \"Lägg till länk\"\n\n#: ../src/properties/class-papi-property-link.php:158\nmsgid \"Edit link\"\nmsgstr \"Ändra länk\"\n\n#: ../src/properties/class-papi-property-link.php:159\nmsgid \"Remove link\"\nmsgstr \"Ta bort länk\"\n\n#: ../src/properties/class-papi-property-post.php:70\nmsgid \"Select Post Type\"\nmsgstr \"Välj post typ\"\n\n#: ../src/properties/class-papi-property-post.php:71\nmsgid \"Select %s\"\nmsgstr \"Välj %s\"\n\n#: ../src/properties/class-papi-property-reference.php:91\nmsgid \"No references exists\"\nmsgstr \"Inga referenser finns\"\n\n#: ../src/properties/class-papi-property-relationship.php:145\nmsgid \"Post\"\nmsgstr \"Inlägg\"\n\n#: ../src/properties/class-papi-property-relationship.php:334\nmsgid \"Search\"\nmsgstr \"Sök\"\n\n#: ../src/properties/class-papi-property-relationship.php:339\nmsgid \"Sort by\"\nmsgstr \"Sortera efter\"\n\n#: ../src/properties/class-papi-property-repeater.php:157\nmsgid \"Add new row\"\nmsgstr \"Lägg till ny rad\"\n\n#: ../src/properties/class-papi-property-term.php:69\nmsgid \"Select Taxonomy\"\nmsgstr \"Välj taxonomi\"\n\n#: ../src/properties/class-papi-property-term.php:70\nmsgid \"Select %s term\"\nmsgstr \"Välj %s term\"\n\n#: ../src/properties/class-papi-property-url.php:42\nmsgid \"Select file\"\nmsgstr \"Välj fil\"\n\n#: ../src/types/class-papi-page-type.php:205,\n#: ../src/types/class-papi-taxonomy-type.php:68\nmsgid \"View\"\nmsgstr \"Vy\"\n\n#: ../src/admin/views/add-new-page.php:8\nmsgid \"Add New %s\"\nmsgstr \"Lägg till ny %s\"\n\n#: ../src/lib/core/property.php:327\nmsgid \"(required field)\"\nmsgstr \"(obligatoriskt fält)\"\n\n#: ../src/lib/hooks/filters-page-type.php:58\nmsgid \"%s with WordPress standard fields\"\nmsgstr \"%s med Wordpress standardfält\"\n\n#: ../src/lib/hooks/filters-page-type.php:80,\n#: ../src/lib/hooks/filters-taxonomy-type.php:31\nmsgid \"Standard %s\"\nmsgstr \"Standard %s\"\n\n#: ../src/admin/views/partials/add-new-item.php:9\nmsgid \"Thumbnail missing\"\nmsgstr \"Bild saknas\"\n\n#: ../src/admin/views/partials/add-new-item.php:22\nmsgid \"Select\"\nmsgstr \"Välj\"\n\n#~ msgid \"\"\n#~ \"WordPress 4.0 and higher required to run Papi! The plugin has now \"\n#~ \"disabled itself.\"\n#~ msgstr \"\"\n#~ \"Wordpress 4.0 och högre krävs för att köra Papi! Tillägget har nu \"\n#~ \"inaktiverat sig själv.\"\n\n#~ msgid \"Name (alphabetically)\"\n#~ msgstr \"Enligt namn (alfabetiskt)\"\n\n#~ msgid \"Post created date (ascending)\"\n#~ msgstr \"Enligt publiceringsdatum (stigande)\"\n\n#~ msgid \"Post created date (descending)\"\n#~ msgstr \"Enligt publiceringsdatum (fallande)\"\n\n#~ msgid \"Post id (ascending)\"\n#~ msgstr \"Enligt sidans id (stigande)\"\n\n#~ msgid \"Post id (descending)\"\n#~ msgstr \"Enligt sidans id (fallande)\"\n\n#~ msgid \"Post order value (ascending)\"\n#~ msgstr \"Post ordervärde (stigande)\"\n\n#~ msgid \"Post order value (descending)\"\n#~ msgstr \"Post ordervärde (fallande)\"\n\n#~ msgid \"Post modified date (ascending)\"\n#~ msgstr \"Enligt ändringsdatum (stigande)\"\n\n#~ msgid \"Post modified date (descending)\"\n#~ msgstr \"Enligt ändringsdatum (fallande)\"\n\n#~ msgid \"Tab Title\"\n#~ msgstr \"Flikens rubrik\"\n\n#~ msgid \"Sort Order\"\n#~ msgstr \"Sorteringsordning\"\n\n#~ msgid \"Properties\"\n#~ msgstr \"Egenskaper\"\n\n#~ msgid \"Slug\"\n#~ msgstr \"Adress\"\n\n#~ msgid \"Cannot find the page type\"\n#~ msgstr \"Kan inte hitta sidtypen\"\n\n#~ msgid \"Overview of page type\"\n#~ msgstr \"Översikt över sidtyper\"\n\n#~ msgid \"No meta boxes exists.\"\n#~ msgstr \"Inga meta boxar existerar.\"\n\n#~ msgid \"Tabs\"\n#~ msgstr \"Tabbar\"\n\n#~ msgid \"Page Types\"\n#~ msgstr \"Sidtyper\"\n\n#~ msgid \"Name\"\n#~ msgstr \"Namn\"\n\n#~ msgid \"Page Type ID\"\n#~ msgstr \"Sidtyp ID\"\n\n#~ msgid \"Post types\"\n#~ msgstr \"Post typer\"\n\n#~ msgid \"Template\"\n#~ msgstr \"Mall\"\n\n#~ msgid \"Page Count\"\n#~ msgstr \"Antal sidor\"\n\n#~ msgid \"Page Type has no template file\"\n#~ msgstr \"Sidtypen har ingen mallfil\"\n\n#~ msgid \"Template file does not exist\"\n#~ msgstr \"Mallfilen finns inte\"\n"
  },
  {
    "path": "languages/papi.pot",
    "content": "# Copyright (C) 2017 papi\n# This file is distributed under the same license as the papi package.\nmsgid \"\"\nmsgstr \"\"\n\"Project-Id-Version: papi\\n\"\n\"MIME-Version: 1.0\\n\"\n\"Content-Type: text/plain; charset=UTF-8\\n\"\n\"Content-Transfer-Encoding: 8bit\\n\"\n\"X-Poedit-Basepath: ..\\n\"\n\"X-Poedit-KeywordsList: __;_e;_ex:1,2c;_n:1,2;_n_noop:1,2;_nx:1,2,4c;_nx_noop:1,2,3c;_x:1,2c;esc_attr__;esc_attr_e;esc_attr_x:1,2c;esc_html__;esc_html_e;esc_html_x:1,2c\\n\"\n\"X-Poedit-SearchPath-0: .\\n\"\n\"X-Poedit-SearchPathExcluded-0: *.js\\n\"\n\"X-Poedit-SourceCharset: UTF-8\\n\"\n\"Plural-Forms: nplurals=2; plural=(n != 1);\\n\"\n\n#: ../src/papi-loader.php:75, ../src/papi-loader.php:84, ../src/admin/class-papi-admin.php:38, ../src/admin/class-papi-admin.php:47\nmsgid \"Cheatin&#8217; huh?\"\nmsgstr \"\"\n\n#: ../src/papi-loader.php:244\nmsgid \"WordPress 4.4 and higher required to run Papi! The plugin has now disabled itself.\"\nmsgstr \"\"\n\n#: ../src/admin/class-papi-admin-assets.php:51\nmsgid \"Close\"\nmsgstr \"\"\n\n#: ../src/admin/class-papi-admin-assets.php:52, ../src/admin/class-papi-admin-page-type-switcher.php:39, ../src/types/class-papi-page-type.php:204, ../src/types/class-papi-taxonomy-type.php:67\nmsgid \"Edit\"\nmsgstr \"\"\n\n#: ../src/admin/class-papi-admin-assets.php:53, ../src/properties/class-papi-property-flexible.php:824, ../src/properties/class-papi-property-flexible.php:847, ../src/properties/class-papi-property-repeater.php:790, ../src/properties/class-papi-property-repeater.php:812\nmsgid \"Remove\"\nmsgstr \"\"\n\n#: ../src/admin/class-papi-admin-assets.php:54\nmsgid \"This fields are required:\"\nmsgstr \"\"\n\n#: ../src/admin/class-papi-admin-columns.php:68\nmsgid \"Type\"\nmsgstr \"\"\n\n#: ../src/admin/class-papi-admin-columns.php:175\nmsgid \"All types\"\nmsgstr \"\"\n\n#: ../src/admin/class-papi-admin-entry-taxonomy.php:50\nmsgid \"%s type\"\nmsgstr \"\"\n\n#: ../src/admin/class-papi-admin-menu.php:125, ../src/admin/class-papi-admin-menu.php:172, ../src/admin/class-papi-admin-menu.php:173, ../src/types/class-papi-page-type.php:203, ../src/types/class-papi-taxonomy-type.php:66\nmsgid \"Add New\"\nmsgstr \"\"\n\n#: ../src/admin/class-papi-admin-page-type-switcher.php:35\nmsgid \"Page Type:\"\nmsgstr \"\"\n\n#: ../src/admin/class-papi-admin-page-type-switcher.php:57\nmsgid \"OK\"\nmsgstr \"\"\n\n#: ../src/admin/class-papi-admin-page-type-switcher.php:58\nmsgid \"Cancel\"\nmsgstr \"\"\n\n#: ../src/admin/class-papi-admin.php:185\nmsgid \"View Papi Documentation\"\nmsgstr \"\"\n\n#: ../src/admin/class-papi-admin.php:185\nmsgid \"Docs\"\nmsgstr \"\"\n\n#: ../src/properties/class-papi-property-datetime.php:32\nmsgid \"Previous Month\"\nmsgstr \"\"\n\n#: ../src/properties/class-papi-property-datetime.php:33\nmsgid \"Next Month\"\nmsgstr \"\"\n\n#: ../src/properties/class-papi-property-datetime.php:34\nmsgid \"Midnight\"\nmsgstr \"\"\n\n#: ../src/properties/class-papi-property-datetime.php:36\nmsgid \"January\"\nmsgstr \"\"\n\n#: ../src/properties/class-papi-property-datetime.php:37\nmsgid \"February\"\nmsgstr \"\"\n\n#: ../src/properties/class-papi-property-datetime.php:38\nmsgid \"March\"\nmsgstr \"\"\n\n#: ../src/properties/class-papi-property-datetime.php:39\nmsgid \"April\"\nmsgstr \"\"\n\n#: ../src/properties/class-papi-property-datetime.php:40\nmsgid \"May\"\nmsgstr \"\"\n\n#: ../src/properties/class-papi-property-datetime.php:41\nmsgid \"June\"\nmsgstr \"\"\n\n#: ../src/properties/class-papi-property-datetime.php:42\nmsgid \"July\"\nmsgstr \"\"\n\n#: ../src/properties/class-papi-property-datetime.php:43\nmsgid \"August\"\nmsgstr \"\"\n\n#: ../src/properties/class-papi-property-datetime.php:44\nmsgid \"September\"\nmsgstr \"\"\n\n#: ../src/properties/class-papi-property-datetime.php:45\nmsgid \"October\"\nmsgstr \"\"\n\n#: ../src/properties/class-papi-property-datetime.php:46\nmsgid \"November\"\nmsgstr \"\"\n\n#: ../src/properties/class-papi-property-datetime.php:47\nmsgid \"December\"\nmsgstr \"\"\n\n#: ../src/properties/class-papi-property-datetime.php:49\nmsgid \"Noon\"\nmsgstr \"\"\n\n#: ../src/properties/class-papi-property-datetime.php:51\nmsgid \"Sunday\"\nmsgstr \"\"\n\n#: ../src/properties/class-papi-property-datetime.php:52\nmsgid \"Monday\"\nmsgstr \"\"\n\n#: ../src/properties/class-papi-property-datetime.php:53\nmsgid \"Tuesday\"\nmsgstr \"\"\n\n#: ../src/properties/class-papi-property-datetime.php:54\nmsgid \"Wednesday\"\nmsgstr \"\"\n\n#: ../src/properties/class-papi-property-datetime.php:55\nmsgid \"Thursday\"\nmsgstr \"\"\n\n#: ../src/properties/class-papi-property-datetime.php:56\nmsgid \"Friday\"\nmsgstr \"\"\n\n#: ../src/properties/class-papi-property-datetime.php:57\nmsgid \"Saturday\"\nmsgstr \"\"\n\n#: ../src/properties/class-papi-property-datetime.php:60\nmsgid \"Sun\"\nmsgstr \"\"\n\n#: ../src/properties/class-papi-property-datetime.php:61\nmsgid \"Mon\"\nmsgstr \"\"\n\n#: ../src/properties/class-papi-property-datetime.php:62\nmsgid \"Tue\"\nmsgstr \"\"\n\n#: ../src/properties/class-papi-property-datetime.php:63\nmsgid \"Wed\"\nmsgstr \"\"\n\n#: ../src/properties/class-papi-property-datetime.php:64\nmsgid \"Thu\"\nmsgstr \"\"\n\n#: ../src/properties/class-papi-property-datetime.php:65\nmsgid \"Fri\"\nmsgstr \"\"\n\n#: ../src/properties/class-papi-property-datetime.php:66\nmsgid \"Sat\"\nmsgstr \"\"\n\n#: ../src/properties/class-papi-property-file.php:117\nmsgid \"Add file\"\nmsgstr \"\"\n\n#: ../src/properties/class-papi-property-file.php:118\nmsgid \"No file selected\"\nmsgstr \"\"\n\n#: ../src/properties/class-papi-property-image.php:22\nmsgid \"Add image\"\nmsgstr \"\"\n\n#: ../src/properties/class-papi-property-image.php:23\nmsgid \"No image selected\"\nmsgstr \"\"\n\n#: ../src/properties/class-papi-property-link.php:127, ../src/properties/class-papi-property-link.php:240\nmsgid \"URL\"\nmsgstr \"\"\n\n#: ../src/properties/class-papi-property-link.php:135, ../src/properties/class-papi-property-link.php:250\nmsgid \"Title\"\nmsgstr \"\"\n\n#: ../src/properties/class-papi-property-link.php:142, ../src/properties/class-papi-property-link.php:259\nmsgid \"Target\"\nmsgstr \"\"\n\n#: ../src/properties/class-papi-property-link.php:144, ../src/properties/class-papi-property-link.php:263\nmsgid \"New window\"\nmsgstr \"\"\n\n#: ../src/properties/class-papi-property-link.php:144, ../src/properties/class-papi-property-link.php:263\nmsgid \"Same window\"\nmsgstr \"\"\n\n#: ../src/properties/class-papi-property-link.php:154\nmsgid \"No link selected\"\nmsgstr \"\"\n\n#: ../src/properties/class-papi-property-link.php:155\nmsgid \"Add link\"\nmsgstr \"\"\n\n#: ../src/properties/class-papi-property-link.php:158\nmsgid \"Edit link\"\nmsgstr \"\"\n\n#: ../src/properties/class-papi-property-link.php:159\nmsgid \"Remove link\"\nmsgstr \"\"\n\n#: ../src/properties/class-papi-property-post.php:70\nmsgid \"Select Post Type\"\nmsgstr \"\"\n\n#: ../src/properties/class-papi-property-post.php:71\nmsgid \"Select %s\"\nmsgstr \"\"\n\n#: ../src/properties/class-papi-property-reference.php:91\nmsgid \"No references exists\"\nmsgstr \"\"\n\n#: ../src/properties/class-papi-property-relationship.php:145\nmsgid \"Post\"\nmsgstr \"\"\n\n#: ../src/properties/class-papi-property-relationship.php:334\nmsgid \"Search\"\nmsgstr \"\"\n\n#: ../src/properties/class-papi-property-relationship.php:339\nmsgid \"Sort by\"\nmsgstr \"\"\n\n#: ../src/properties/class-papi-property-repeater.php:157\nmsgid \"Add new row\"\nmsgstr \"\"\n\n#: ../src/properties/class-papi-property-term.php:69\nmsgid \"Select Taxonomy\"\nmsgstr \"\"\n\n#: ../src/properties/class-papi-property-term.php:70\nmsgid \"Select %s term\"\nmsgstr \"\"\n\n#: ../src/properties/class-papi-property-url.php:42\nmsgid \"Select file\"\nmsgstr \"\"\n\n#: ../src/types/class-papi-page-type.php:205, ../src/types/class-papi-taxonomy-type.php:68\nmsgid \"View\"\nmsgstr \"\"\n\n#: ../src/admin/views/add-new-page.php:8\nmsgid \"Add New %s\"\nmsgstr \"\"\n\n#: ../src/lib/core/property.php:327\nmsgid \"(required field)\"\nmsgstr \"\"\n\n#: ../src/lib/hooks/filters-page-type.php:58\nmsgid \"%s with WordPress standard fields\"\nmsgstr \"\"\n\n#: ../src/lib/hooks/filters-page-type.php:80, ../src/lib/hooks/filters-taxonomy-type.php:31\nmsgid \"Standard %s\"\nmsgstr \"\"\n\n#: ../src/admin/views/partials/add-new-item.php:9\nmsgid \"Thumbnail missing\"\nmsgstr \"\"\n\n#: ../src/admin/views/partials/add-new-item.php:22\nmsgid \"Select\"\nmsgstr \"\"\n"
  },
  {
    "path": "package.json",
    "content": "{\n  \"name\": \"papi\",\n  \"version\": \"3.2.0\",\n  \"repository\": \"wp-papi/papi\",\n  \"description\": \"WordPress Page Type API with custom fields\",\n  \"author\": \"Fredrik Forsmo <fredrik.forsmo@gmail.com>\",\n  \"license\": \"MIT\",\n  \"dependencies\": {\n    \"moment\": \"^2.29\",\n    \"select2\": \"^4.0.5\"\n  },\n  \"devDependencies\": {\n    \"@babel/core\": \"^7.8.7\",\n    \"@jitesoft/babel-preset-main\": \"^2.1.5\",\n    \"autoprefixer-cli\": \"^1.0\",\n    \"babel\": \"^6.5\",\n    \"babel-core\": \"^6.17\",\n    \"babel-eslint\": \"^10.1.0\",\n    \"babel-loader\": \"^8.0.6\",\n    \"babel-preset-es2015\": \"^6.18\",\n    \"concurrently\": \"^5.1.0\",\n    \"core-js\": \"^3.6.4\",\n    \"csso-cli\": \"^3.0.0\",\n    \"sass\": \"^1.17.2\",\n    \"semistandard\": \"^14.2.0\",\n    \"watch\": \"^1.0\",\n    \"webpack\": \"^4.42.0\",\n    \"webpack-cli\": \"^3.3.12\"\n  },\n  \"scripts\": {\n    \"build\": \"NODE_ENV=production npm run js\",\n    \"css\": \"sass src/assets/scss/style.scss dist/css/style.css && autoprefixer-cli -b \\\"last 2 version\\\" dist/css/style.css dist/css/style.css && csso dist/css/style.css dist/css/style.min.css && echo \\\"\\n/*# sourceMappingURL=style.min.css.map */\\\" >> dist/css/style.min.css && mv dist/css/style.css.map dist/css/style.min.css.map && rm dist/css/style.css\",\n    \"js\": \"webpack\",\n    \"lint\": \"semistandard --global papi --fix src/assets/js/*.js src/assets/js/properties/*.js\",\n    \"watch\": \"concurrently \\\"watch:css\\\" \\\"watch:js\\\"\",\n    \"watch:css\": \"watch \\\"npm run css\\\"\",\n    \"watch:js\": \"watch \\\"npm run js\\\"\"\n  },\n  \"semistandard\": {\n    \"parser\": \"babel-eslint\"\n  }\n}\n"
  },
  {
    "path": "papi-loader.php",
    "content": "<?php\n\n/**\n * Plugin Name: Papi\n * Description: Page Type API with custom fields\n * Author: Fredrik Forsmo\n * Author URI: https://frozzare.com\n * Version: 3.3.0-dev\n * Plugin URI: https://wp-papi.github.io\n * Textdomain: papi\n * Domain Path: /languages/\n */\n\n// Load Papi loader.\nrequire_once __DIR__ . '/src/papi-loader.php';\n"
  },
  {
    "path": "phpcs.xml",
    "content": "<?xml version=\"1.0\"?>\n<ruleset name=\"Papi\">\n  <description>Papi rules for PHP_CodeSniffer</description>\n\n  <exclude-pattern>node_modules/*</exclude-pattern>\n  <exclude-pattern>vendor/*</exclude-pattern>\n\n  <rule ref=\"Generic.Arrays.DisallowLongArraySyntax\" />\n\n  <rule ref=\"WordPress-Core\">\n    <exclude name=\"PEAR.Functions.FunctionCallSignature.EmptyLine\" />\n    <exclude name=\"WordPress.Arrays.ArrayKeySpacingRestrictions\" />\n    <exclude name=\"WordPress.Arrays.ArrayDeclarationSpacing.NoSpaceAfterArrayOpener\" />\n    <exclude name=\"WordPress.Arrays.ArrayDeclarationSpacing.NoSpaceBeforeArrayCloser\" />\n    <exclude name=\"WordPress.Arrays.ArrayDeclaration\" />\n    <exclude name=\"WordPress.PHP.YodaConditions\" />\n    <exclude name=\"WordPress.WhiteSpace.ControlStructureSpacing.NoSpaceBetweenStructureColon\" />\n    <exclude name=\"Squiz.Commenting.LongConditionClosingComment.Missing\" />\n    <exclude name=\"WordPress.NamingConventions.ValidHookName.UseUnderscores\" />\n    <exclude name=\"Generic.PHP.DisallowAlternativePHPTags.MaybeASPShortOpenTagFound\" />\n    <exclude name=\"Generic.PHP.DisallowAlternativePHPTags.MaybeASPOpenTagFound\" />\n    <exclude name=\"Squiz.PHP.DisallowMultipleAssignments.FoundInControlStructure\" />\n    <exclude name=\"PSR2\" />\n    <!-- allow papi-loader.php -->\n    <exclude name=\"WordPress.Files.FileName.InvalidClassFileName\" />\n    <!-- allow if ( $value = func() ) -->\n    <exclude name=\"Squiz.PHP.DisallowMultipleAssignments.Found\" />\n    <!-- allow __, esc_html__ and so without translators comment -->\n    <exclude name=\"WordPress.WP.I18n.MissingTranslatorsComment\" />\n    <!-- allow ['key' => $value] -->\n    <exclude name=\"WordPress.Arrays.ArrayDeclarationSpacing.AssociativeKeyFound\" />\n    <exclude name=\"WordPress.Arrays.CommaAfterArrayItem.NoComma\" />\n    <!-- allow opening/closing tag on same line -->\n    <exclude name=\"Squiz.PHP.EmbeddedPhp.ContentAfterOpen\" />\n    <exclude name=\"Squiz.PHP.EmbeddedPhp.ContentBeforeEnd\" />\n  </rule>\n\n  <rule ref=\"WordPress.XSS.EscapeOutput\" />\n  <rule ref=\"WordPress.PHP.StrictComparisons\" />\n  <rule ref=\"WordPress.PHP.StrictInArray\" />\n</ruleset>\n"
  },
  {
    "path": "phpunit.xml.dist",
    "content": "<phpunit backupGlobals=\"false\"\n  backupStaticAttributes=\"false\"\n  beStrictAboutTestsThatDoNotTestAnything=\"true\"\n  beStrictAboutOutputDuringTests=\"true\"\n  bootstrap=\"tests/bootstrap.php\"\n  colors=\"true\"\n  convertErrorsToExceptions=\"true\"\n  convertNoticesToExceptions=\"true\"\n  convertWarningsToExceptions=\"true\"\n  failOnRisky=\"true\"\n  failOnWarning=\"true\"\n  processIsolation=\"false\"\n  stopOnError=\"false\"\n  stopOnFailure=\"false\"\n  verbose=\"true\"\n>\n  <testsuites>\n    <testsuite name=\"Papi Test Suite\">\n      <directory suffix=\".php\">./tests/cases</directory>\n    </testsuite>\n  </testsuites>\n  <filter>\n    <whitelist addUncoveredFilesFromWhitelist=\"true\">\n      <directory suffix=\".php\">./src</directory>\n    </whitelist>\n  </filter>\n  <logging>\n    <log type=\"coverage-clover\" target=\"./tmp/clover.xml\" charset=\"UTF-8\" />\n  </logging>\n</phpunit>\n"
  },
  {
    "path": "src/admin/class-papi-admin-ajax.php",
    "content": "<?php\n\n/**\n * Admin class that handle ajax calls.\n */\nclass Papi_Admin_Ajax {\n\n\t/**\n\t * The action prefix for Papi ajax actions.\n\t *\n\t * @var string\n\t */\n\tprotected $action_prefix = 'papi/ajax/';\n\n\t/**\n\t * The permalink structure.\n\t *\n\t * @var string\n\t */\n\tprotected $structure;\n\n\t/**\n\t * The construct.\n\t */\n\tpublic function __construct() {\n\t\t$this->setup_actions();\n\t\t$this->structure = get_option( 'permalink_structure' );\n\t}\n\n\t/**\n\t * Add ajax endpoint.\n\t */\n\tpublic function add_endpoint() {\n\t\tif ( empty( $this->structure ) ) {\n\t\t\treturn;\n\t\t}\n\n\t\tadd_rewrite_tag( '%action%', '([^/]*)' );\n\t\tadd_rewrite_rule( 'papi-ajax/([^/]*)/?', 'index.php?action=$matches[1]', 'top' );\n\t}\n\n\t/**\n\t * Add ajax url to Papi JavaScript object.\n\t */\n\tpublic function ajax_url() {\n\t\tif ( empty( $this->structure ) ) {\n\t\t\t$url = esc_url( home_url( 'index.php', is_ssl() ? 'https' : 'http' ) );\n\t\t} else {\n\t\t\t$url = esc_url( home_url( '/papi-ajax/', is_ssl() ? 'https' : 'http' ) );\n\t\t}\n\t\t?>\n\t\t<script type=\"text/javascript\">\n\t\t\tvar papi = papi || {};\n\t\t\tpapi.ajaxUrl = '<?php echo esc_html( $url ); ?>';\n\t\t</script>\n\t\t<?php\n\t}\n\n\t/**\n\t * Handle Papi ajax.\n\t */\n\tpublic function handle_papi_ajax() {\n\t\tif ( defined( 'DOING_AJAX' ) && DOING_AJAX ) {\n\t\t\treturn;\n\t\t}\n\n\t\t$ajax_action = '';\n\n\t\tif ( ! empty( $_GET['action'] ) ) {\n\t\t\t$ajax_action = sanitize_text_field( $_GET['action'] );\n\t\t}\n\n\t\tif ( is_user_logged_in() && has_action( $this->action_prefix . $ajax_action ) !== false ) {\n\t\t\tif ( ! defined( 'DOING_AJAX' ) ) {\n\t\t\t\tdefine( 'DOING_AJAX', true );\n\t\t\t}\n\n\t\t\tif ( ! defined( 'DOING_PAPI_AJAX' ) ) {\n\t\t\t\tdefine( 'DOING_PAPI_AJAX', true );\n\t\t\t}\n\n\t\t\tstatus_header( 200 );\n\t\t\tdo_action( $this->action_prefix . $ajax_action );\n\t\t\twp_die();\n\t\t}\n\t}\n\n\t/**\n\t * Get entry type via GET.\n\t *\n\t * GET /papi-ajax/?action=get_entry_type\n\t */\n\tpublic function get_entry_type() {\n\t\t$entry_type_id = papi_get_qs( 'entry_type' );\n\t\t$entry_type    = papi_get_entry_type_by_id( $entry_type_id );\n\n\t\tif ( empty( $entry_type ) ) {\n\t\t\t$this->render_error( 'No entry type found' );\n\t\t\treturn;\n\t\t}\n\n\t\twp_send_json( $entry_type );\n\t}\n\n\t/**\n\t * Get posts via GET.\n\t *\n\t * Posts with empty title will be ignored.\n\t *\n\t * GET /papi-ajax/?action=get_posts\n\t */\n\tpublic function get_posts() {\n\t\t$args   = papi_get_qs( 'query' ) ?: [];\n\t\t$args   = is_array( $args ) ? $args : [];\n\t\t$fields = papi_get_qs( 'fields' ) ?: [];\n\t\t$fields = is_array( $fields ) ? $fields : [];\n\t\t$posts  = ( new WP_Query( array_merge( [\n\t\t\t'posts_per_page'         => -1,\n\t\t\t'post_status'            => 'any',\n\t\t\t'post_type'              => ['post'],\n\t\t\t'no_found_rows'          => true,\n\t\t\t'update_post_meta_cache' => false,\n\t\t\t'update_post_term_cache' => false\n\t\t], $args ) ) )->posts;\n\n\t\t$posts = array_filter( $posts, function ( $post ) {\n\t\t\treturn ! empty( $post->post_title );\n\t\t} );\n\n\t\tusort( $posts, function ( $a, $b ) {\n\t\t\treturn strcmp( strtolower( $a->post_title ), strtolower( $b->post_title ) );\n\t\t} );\n\n\t\tif ( ! empty( $fields ) ) {\n\t\t\tforeach ( $posts as $index => $post ) {\n\t\t\t\t$item = [];\n\n\t\t\t\tforeach ( $fields as $field ) {\n\t\t\t\t\t$item[$field] = $post->$field;\n\t\t\t\t}\n\n\t\t\t\t$posts[$index] = $item;\n\t\t\t}\n\t\t}\n\n\t\twp_send_json( $posts );\n\t}\n\n\t/**\n\t * Get property html via GET.\n\t *\n\t * GET /papi-ajax/?action=get_property\n\t */\n\tpublic function get_property() {\n\t\t$default_options = Papi_Core_Property::factory()->get_options();\n\t\t$keys            = array_keys( get_object_vars( $default_options ) );\n\t\t$options         = papi_get_qs( $keys, true );\n\n\t\tif ( $property = papi_property( $options ) ) {\n\t\t\tob_start();\n\n\t\t\t$property->render_ajax_request();\n\n\t\t\t$html = ob_get_clean();\n\n\t\t\twp_send_json( [\n\t\t\t\t'html' => utf8_encode( $html )\n\t\t\t] );\n\n\t\t\treturn;\n\t\t}\n\n\t\t$this->render_error( 'No property found' );\n\t}\n\n\t/**\n\t * Get properties via POST.\n\t *\n\t * POST /papi-ajax/?action=get_properties\n\t */\n\tpublic function get_properties() {\n\t\tif ( ! papi_get_sanitized_post( 'properties' ) ) {\n\t\t\t$this->render_error( 'No properties found' );\n\n\t\t\treturn;\n\t\t}\n\n\t\t$items = json_decode( stripslashes( $_POST['properties'] ), true );\n\n\t\tif ( empty( $items ) || ! is_array( $items ) ) {\n\t\t\t$this->render_error( 'No properties found' );\n\n\t\t\treturn;\n\t\t}\n\n\t\tforeach ( $items as $key => $item ) {\n\t\t\t$property = papi_property( (array) $item );\n\n\t\t\tif ( ! papi_is_property( $property ) ) {\n\t\t\t\tunset( $items[$key] );\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tob_start();\n\n\t\t\t$property->render_ajax_request();\n\n\t\t\t$items[$key] = trim( ob_get_clean() );\n\t\t}\n\n\t\t$items = array_filter( $items );\n\n\t\tif ( empty( $items ) ) {\n\t\t\t$this->render_error( 'No properties found' );\n\n\t\t\treturn;\n\t\t}\n\n\t\twp_send_json( [\n\t\t\t'html' => $items\n\t\t] );\n\t}\n\n\t/**\n\t * Get rules result via GET.\n\t *\n\t * GET /papi-ajax/?action=get_rules_result\n\t */\n\tpublic function get_rules_result() {\n\t\tif ( ! papi_get_sanitized_post( 'data' ) ) {\n\t\t\t$this->render_error( 'No rule found' );\n\n\t\t\treturn;\n\t\t}\n\n\t\t$data = json_decode( stripslashes( papi_get_sanitized_post( 'data' ) ), true );\n\n\t\tif ( empty( $data ) || ! is_array( $data ) || ! isset( $data['slug'] ) ) {\n\t\t\t$this->render_error( 'No rule found' );\n\n\t\t\treturn;\n\t\t}\n\n\t\t$entry_type = papi_get_entry_type_by_meta_id();\n\n\t\tif ( empty( $entry_type ) ) {\n\t\t\t$entry_type_id = papi_get_qs( 'entry_type' );\n\t\t\t$entry_type    = papi_get_entry_type_by_id( $entry_type_id );\n\t\t}\n\n\t\tif ( $entry_type instanceof Papi_Entry_Type === false ) {\n\t\t\t$this->render_error( 'No rule found' );\n\n\t\t\treturn;\n\t\t}\n\n\t\tif ( preg_match( '/\\[\\]$/', $data['slug'] ) ) {\n\t\t\t$data['slug'] = preg_replace( '/\\[\\]$/', '', $data['slug'] );\n\t\t}\n\n\t\tif ( $property = $entry_type->get_property( $data['slug'] ) ) {\n\t\t\twp_send_json( [\n\t\t\t\t'render' => $property->render_is_allowed_by_rules( $data['rules'] )\n\t\t\t] );\n\n\t\t\treturn;\n\t\t}\n\n\t\t$this->render_error( 'No rule found' );\n\t}\n\n\t/**\n\t * Get shortcode via GET.\n\t *\n\t * GET /papi-ajax/?action=get_shortcode\n\t */\n\tpublic function get_shortcode() {\n\t\t$shortcode = papi_get_qs( 'shortcode' ) ?: '';\n\t\t$shortcode = html_entity_decode( $shortcode );\n\t\t$shortcode = wp_unslash( $shortcode );\n\n\t\twp_send_json( [\n\t\t\t'html' => do_shortcode( $shortcode )\n\t\t] );\n\t}\n\n\t/**\n\t * Get terms via GET.\n\t *\n\t * GET /papi-ajax/?action=get_terms\n\t */\n\tpublic function get_terms() {\n\t\t$query    = papi_get_qs( 'query' ) ?: [];\n\t\t$query    = is_array( $query ) ? $query : [];\n\t\t$taxonomy = papi_get_qs( 'taxonomy' ) ?: '';\n\n\t\t$args = array_merge( [\n\t\t\t'fields' => 'id=>name'\n\t\t], $query );\n\n\t\t$terms = [];\n\t\tif ( taxonomy_exists( $taxonomy ) ) {\n\t\t\t$terms = get_terms( $taxonomy, $args );\n\t\t}\n\n\t\twp_send_json( $terms );\n\t}\n\n\t/**\n\t * Render error message.\n\t *\n\t * @param string $message\n\t */\n\tpublic function render_error( $message ) {\n\t\twp_send_json( [\n\t\t\t'error' => $message\n\t\t] );\n\t}\n\n\t/**\n\t * Setup action hooks.\n\t */\n\tprotected function setup_actions() {\n\t\tadd_action( 'init', [$this, 'add_endpoint'] );\n\t\tadd_action( 'parse_request', [$this, 'handle_papi_ajax'] );\n\t\tadd_action( 'admin_enqueue_scripts', [$this, 'ajax_url'], 10 );\n\n\t\t// Ajax actions.\n\t\tadd_action( $this->action_prefix . 'get_entry_type', [$this, 'get_entry_type'] );\n\t\tadd_action( $this->action_prefix . 'get_property', [$this, 'get_property'] );\n\t\tadd_action( $this->action_prefix . 'get_properties', [$this, 'get_properties'] );\n\t\tadd_action( $this->action_prefix . 'get_rules_result', [$this, 'get_rules_result'] );\n\t\tadd_action( $this->action_prefix . 'get_posts', [$this, 'get_posts'] );\n\t\tadd_action( $this->action_prefix . 'get_terms', [$this, 'get_terms'] );\n\t\tadd_action( $this->action_prefix . 'get_shortcode', [$this, 'get_shortcode'] );\n\t}\n}\n\nnew Papi_Admin_Ajax;\n"
  },
  {
    "path": "src/admin/class-papi-admin-assets.php",
    "content": "<?php\n\n/**\n * Admin class that handle assets.\n */\nfinal class Papi_Admin_Assets {\n\n\t/**\n\t * The constructor.\n\t */\n\tpublic function __construct() {\n\t\tadd_action( 'admin_enqueue_scripts', [$this, 'enqueue_css'] );\n\t\tadd_action( 'admin_enqueue_scripts', [$this, 'enqueue_js'] );\n\t\tadd_action( 'admin_enqueue_scripts', [$this, 'enqueue_locale'] );\n\t}\n\n\t/**\n\t * Enqueue CSS.\n\t */\n\tpublic function enqueue_css() {\n\t\twp_enqueue_media();\n\t\twp_enqueue_style( 'wp-color-picker' );\n\t\twp_enqueue_style( 'papi-main', dirname( PAPI_PLUGIN_URL ) . '/dist/css/style.min.css', false, null );\n\t}\n\n\t/**\n\t * Enqueue JavaScript.\n\t */\n\tpublic function enqueue_js() {\n\t\t// WordPress will override window.papi on plugins page,\n\t\t// so don't include Papi JavaScript on plugins page.\n\t\tif ( strpos( $_SERVER['REQUEST_URI'], 'plugins.php' ) !== false ) {\n\t\t\treturn;\n\t\t}\n\n\t\twp_enqueue_script( 'papi-main', dirname( PAPI_PLUGIN_URL ) . '/dist/js/main.min.js', [\n\t\t\t'json2',\n\t\t\t'jquery',\n\t\t\t'jquery-ui-core',\n\t\t\t'jquery-ui-sortable',\n\t\t\t'jquery-masonry',\n\t\t\t'wp-color-picker'\n\t\t], '', true );\n\t}\n\n\t/**\n\t * Enqueue locale.\n\t */\n\tpublic function enqueue_locale() {\n\t\twp_localize_script( 'papi-main', 'papiL10n', [\n\t\t\t'close'         => __( 'Close', 'default' ),\n\t\t\t'edit'          => __( 'Edit', 'default' ),\n\t\t\t'new'           => __( 'New', 'default' ),\n\t\t\t'remove'        => __( 'Remove', 'default' ),\n\t\t\t'requiredError' => __( 'This fields are required:', 'papi' ),\n\t\t] );\n\t}\n}\n\nif ( papi_is_admin() ) {\n\tnew Papi_Admin_Assets;\n}\n"
  },
  {
    "path": "src/admin/class-papi-admin-columns.php",
    "content": "<?php\n\n/**\n * Admin class that handle table columns.\n */\nfinal class Papi_Admin_Columns {\n\n\t/**\n\t * Current post type.\n\t *\n\t * @var string\n\t */\n\tprotected $post_type;\n\n\t/**\n\t * Current taxonomy.\n\t *\n\t * @var string\n\t */\n\tprotected $taxonomy;\n\n\t/**\n\t * The constructor.\n\t */\n\tpublic function __construct() {\n\t\t$this->setup_globals();\n\t\t$this->setup_actions();\n\t\t$this->setup_filters();\n\t}\n\n\t/**\n\t * Get meta type value.\n\t *\n\t * @return string\n\t */\n\tprotected function get_meta_type_value() {\n\t\treturn empty( $this->taxonomy ) ? $this->post_type : $this->taxonomy;\n\t}\n\n\t/**\n\t * Add custom table header to page or taxonomy type.\n\t *\n\t * @param  array $defaults\n\t *\n\t * @return array\n\t */\n\tpublic function manage_page_type_posts_columns( array $defaults = [] ) {\n\t\tif ( ! in_array( $this->post_type, papi_get_post_types(), true ) && ! in_array( $this->taxonomy, papi_get_taxonomies(), true ) ) {\n\t\t\treturn $defaults;\n\t\t}\n\n\t\t/**\n\t\t * Hide column for post or taxonomy type. Default is false.\n\t\t *\n\t\t * @param string $post_type\n\t\t */\n\t\tif ( apply_filters( 'papi/settings/column_hide_' . $this->get_meta_type_value(), false ) ) {\n\t\t\treturn $defaults;\n\t\t}\n\n\t\t/**\n\t\t * Change column title for entry type column.\n\t\t *\n\t\t * @param string $post_type\n\t\t */\n\t\t$defaults['entry_type'] = apply_filters(\n\t\t\t'papi/settings/column_title_' . $this->get_meta_type_value(),\n\t\t\tesc_html__( 'Type', 'papi' )\n\t\t);\n\n\t\treturn $defaults;\n\t}\n\n\t/**\n\t * Add custom table column to page or taxonomy type.\n\t *\n\t * @param string $column_name\n\t * @param int    $post_id\n\t * @param int    $term_id\n\t */\n\tpublic function manage_page_type_posts_custom_column( $column_name, $post_id, $term_id = null ) {\n\t\tif ( ! in_array( $this->post_type, papi_get_post_types(), true ) && ! in_array( $this->taxonomy, papi_get_taxonomies(), true ) ) {\n\t\t\treturn;\n\t\t}\n\n\t\t/**\n\t\t * Hide column for post type. Default is false.\n\t\t *\n\t\t * @param string $post_type\n\t\t */\n\t\tif ( apply_filters( 'papi/settings/column_hide_' . $this->get_meta_type_value(), false ) ) {\n\t\t\treturn;\n\t\t}\n\n\t\t// Column name most be `entry_type`. On taxomy the column name is `post_id` variable.\n\t\tif ( $column_name !== 'entry_type' && $post_id !== 'entry_type' ) {\n\t\t\treturn;\n\t\t}\n\n\t\t// Get the entry type for the post or term.\n\t\t$entry_type = papi_get_entry_type_by_meta_id(\n\t\t\tis_numeric( $post_id ) ? $post_id : $term_id,\n\t\t\tpapi_get_meta_type()\n\t\t);\n\n\t\t$post = ! empty( $this->post_type ) && empty( $this->taxonomy );\n\t\t$type = $post ? 'page' : 'taxonomy';\n\t\t$arg  = $post ? papi_get_post_type() : papi_get_taxonomy();\n\t\t$show = call_user_func_array( \"papi_filter_settings_show_standard_{$type}_type\", [$arg] );\n\n\t\tif ( ! is_null( $entry_type ) && ( $show === false || get_metadata( papi_get_meta_type( $type ), ( is_numeric( $post_id ) ? $post_id : $term_id ), papi_get_page_type_key(), true ) === $entry_type->get_id() ) ) {\n\t\t\techo esc_html( $entry_type->name );\n\t\t} else {\n\t\t\techo esc_html( call_user_func_array( \"papi_filter_settings_standard_{$type}_type_name\", [$arg] ) );\n\t\t}\n\t}\n\n\t/**\n\t * Add entry type as a sortable column.\n\t *\n\t * @param  array $columns\n\t *\n\t * @return array\n\t */\n\tpublic function manage_page_type_sortable_columns( $columns ) {\n\t\tif ( in_array( $this->post_type, papi_get_post_types(), true ) || in_array( $this->taxonomy, papi_get_taxonomies(), true ) ) {\n\t\t\t$columns['entry_type'] = 'entry_type';\n\t\t}\n\n\t\treturn $columns;\n\t}\n\n\t/**\n\t * Filter posts on load if `page_type` query string is set.\n\t *\n\t * @param  WP_Query $query\n\t *\n\t * @return WP_Query\n\t */\n\tpublic function pre_get_posts( WP_Query $query ) {\n\t\tglobal $pagenow;\n\n\t\tif ( $pagenow === 'edit.php' && ! is_null( papi_get_qs( 'page_type' ) ) ) {\n\t\t\tif ( papi_get_qs( 'page_type' ) === 'papi-standard-page' ) {\n\t\t\t\t$query->set( 'meta_query', [\n\t\t\t\t\t[\n\t\t\t\t\t\t'key'     => papi_get_page_type_key(),\n\t\t\t\t\t\t'compare' => 'NOT EXISTS'\n\t\t\t\t\t]\n\t\t\t\t] );\n\t\t\t} else {\n\t\t\t\t$query->set( 'meta_key', papi_get_page_type_key() );\n\t\t\t\t$query->set( 'meta_value', papi_get_qs( 'page_type' ) );\n\t\t\t}\n\t\t}\n\n\t\t// Order query result by entry type.\n\t\tif ( $query->get( 'orderby' ) === 'entry_type' ) {\n\t\t\t$query->set( 'meta_key', papi_get_page_type_key() );\n\t\t\t$query->set( 'orderby', 'meta_value' );\n\t\t}\n\n\t\treturn $query;\n\t}\n\n\t/**\n\t * Filter page types in post type list.\n\t */\n\tpublic function restrict_page_types() {\n\t\t$post_types = papi_get_post_types();\n\n\t\tif ( in_array( $this->post_type, $post_types, true ) ) {\n\t\t\t$page_types = papi_get_all_page_types( $this->post_type );\n\t\t\t$page_types = array_map( function ( $page_type ) {\n\t\t\t\treturn [\n\t\t\t\t\t'name'  => $page_type->name,\n\t\t\t\t\t'value' => $page_type->get_id()\n\t\t\t\t];\n\t\t\t}, $page_types );\n\n\t\t\t// Add the standard page that isn't a real page type.\n\t\t\tif ( papi_filter_settings_show_standard_page_type_in_filter( $this->post_type ) ) {\n\t\t\t\t$page_types[] = [\n\t\t\t\t\t'name'  => papi_filter_settings_standard_page_type_name( $this->post_type ),\n\t\t\t\t\t'value' => 'papi-standard-page'\n\t\t\t\t];\n\t\t\t}\n\n\t\t\tusort( $page_types, function ( $a, $b ) {\n\t\t\t\treturn strcmp(\n\t\t\t\t\tstrtolower( $a['name'] ),\n\t\t\t\t\tstrtolower( $b['name'] )\n\t\t\t\t);\n\t\t\t} );\n\t\t\t?>\n\t\t\t<select name=\"page_type\" class=\"postform\">\n\t\t\t\t<option value=\"0\" selected><?php esc_html_e( 'All types', 'papi' ); ?></option>\n\t\t\t\t<?php\n\t\t\t\tforeach ( $page_types as $page_type ) {\n\t\t\t\t\tprintf(\n\t\t\t\t\t\t'<option value=\"%s\" %s>%s</option>',\n\t\t\t\t\t\tesc_attr( $page_type['value'] ),\n\t\t\t\t\t\tpapi_get_qs( 'page_type' ) === $page_type['value'] ? ' selected' : '',\n\t\t\t\t\t\tesc_html( $page_type['name'] )\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t\t?>\n\t\t\t</select>\n\t\t\t<?php\n\t\t}\n\t}\n\n\t/**\n\t * Setup actions.\n\t */\n\tprotected function setup_actions() {\n\t\t// Setup post type actions.\n\t\tif ( ! empty( $this->post_type ) && empty( $this->taxonomy ) ) {\n\t\t\tadd_filter( 'pre_get_posts', [$this, 'pre_get_posts'] );\n\t\t\tadd_action( 'restrict_manage_posts', [$this, 'restrict_page_types'] );\n\t\t\tadd_action( sprintf( 'manage_%s_posts_custom_column', $this->post_type ), [$this, 'manage_page_type_posts_custom_column'], 10, 2 );\n\t\t}\n\n\t\t// Setup taxonomy actions.\n\t\tif ( ! empty( $this->taxonomy ) ) {\n\t\t\tadd_action( sprintf( 'manage_%s_custom_column', $this->taxonomy ), [$this, 'manage_page_type_posts_custom_column'], 10, 3 );\n\t\t}\n\t}\n\n\t/**\n\t * Setup filters.\n\t */\n\tprotected function setup_filters() {\n\t\t// Setup post type actions.\n\t\tif ( ! empty( $this->post_type ) && empty( $this->taxonomy ) ) {\n\t\t\tadd_filter( sprintf( 'manage_%s_posts_columns', $this->post_type ), [$this, 'manage_page_type_posts_columns'] );\n\t\t\tadd_filter( sprintf( 'manage_edit-%s_sortable_columns', $this->post_type ), [$this, 'manage_page_type_sortable_columns'] );\n\t\t}\n\n\t\t// Setup taxonomy actions.\n\t\tif ( ! empty( $this->taxonomy ) ) {\n\t\t\tadd_filter( sprintf( 'manage_edit-%s_columns', $this->taxonomy ), [$this, 'manage_page_type_posts_columns'] );\n\t\t\tadd_filter( sprintf( 'manage_edit-%s_sortable_columns', $this->taxonomy ), [$this, 'manage_page_type_sortable_columns'] );\n\t\t}\n\t}\n\n\t/**\n\t * Setup globals.\n\t */\n\tprotected function setup_globals() {\n\t\t$this->post_type = papi_get_post_type();\n\t\t$this->taxonomy  = papi_get_taxonomy();\n\t}\n}\n\nif ( papi_is_admin() ) {\n\tnew Papi_Admin_Columns;\n}\n"
  },
  {
    "path": "src/admin/class-papi-admin-entry-post.php",
    "content": "<?php\n\nclass Papi_Admin_Entry_Post extends Papi_Admin_Entry {\n\n\t/**\n\t * The post type.\n\t *\n\t * @var string\n\t */\n\tprotected $post_type;\n\n\t/**\n\t * The construct.\n\t */\n\tpublic function __construct() {\n\t\t$this->post_type = papi_get_post_type();\n\n\t\t$this->setup_actions();\n\t\t$this->setup_filters();\n\t}\n\n\t/**\n\t * Filters the fields displayed in the post revision diff UI.\n\t *\n\t * @param  array   $return\n\t * @param  WP_Post $compare_from\n\t * @param  WP_Post $compare_to\n\t *\n\t * @return array\n\t */\n\tpublic function get_revision_ui_diff( $return, $compare_from, $compare_to ) {\n\t\t$meta = get_post_meta( $compare_from->ID );\n\t\t$meta = is_array( $meta ) ? $meta : [];\n\n\t\tforeach ( $meta as $key => $value ) {\n\t\t\tif ( $key[0] === '_' && $key !== papi_get_page_type_key() ) {\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\t$content_from = papi_data_get( $compare_from->ID, $key );\n\t\t\t$content_to = papi_data_get( $compare_to->ID, $key );\n\n\t\t\t$diff = wp_text_diff( $content_from, $content_to, [\n\t\t\t\t'show_split_view' => true\n\t\t\t] );\n\n\t\t\tif ( $diff ) {\n\t\t\t\t$return[] = [\n\t\t\t\t\t'id'   => $key,\n\t\t\t\t\t'name' => $key,\n\t\t\t\t\t'diff' => $diff,\n\t\t\t\t];\n\t\t\t}\n\t\t}\n\n\t\treturn $return;\n\t}\n\n\t/**\n\t * Output hidden meta boxes.\n\t */\n\tpublic function hidden_meta_boxes() {\n\t\tglobal $_wp_post_type_features;\n\n\t\tif ( isset( $_wp_post_type_features[$this->post_type]['editor'] ) ) {\n\t\t\treturn;\n\t\t}\n\n\t\tadd_meta_box( 'papi-hidden-editor', 'Papi hidden editor', [$this, 'hidden_meta_box_editor'], $this->post_type );\n\t}\n\n\t/**\n\t * Output hidden WordPress editor.\n\t */\n\tpublic function hidden_meta_box_editor() {\n\t\twp_editor( '', 'papiHiddenEditor' );\n\t}\n\n\t/**\n\t * Load post new action\n\t * Redirect to right url if no page type is set.\n\t */\n\tpublic function load_post_new() {\n\t\t$request_uri = $_SERVER['REQUEST_URI'];\n\t\t$post_types  = papi_get_post_types();\n\n\t\tif ( in_array( $this->post_type, $post_types, true ) && strpos( $request_uri, 'page_type=' ) === false ) {\n\t\t\t$parsed_url = parse_url( $request_uri );\n\n\t\t\t$only_page_type = papi_filter_settings_only_page_type( $this->post_type );\n\t\t\t$page_types     = papi_get_all_page_types( $this->post_type );\n\t\t\t$show_standard  = false;\n\n\t\t\tif ( count( $page_types ) === 1 && empty( $only_page_type ) ) {\n\t\t\t\t$show_standard  = $page_types[0]->standard_type;\n\t\t\t\t$show_standard  = $show_standard ? papi_filter_settings_show_standard_page_type( $this->post_type ) : $show_standard;\n\t\t\t\t$only_page_type = $show_standard ? '' : $page_types[0]->get_id();\n\t\t\t}\n\n\t\t\t// Check if we should show one post type or not and\n\t\t\t// create the right url for that.\n\t\t\tif ( ! empty( $only_page_type ) && ! $show_standard ) {\n\t\t\t\t$url = papi_get_page_new_url( $only_page_type, false );\n\t\t\t} else {\n\t\t\t\t$page = 'page=papi-add-new-page,' . $this->post_type;\n\n\t\t\t\tif ( $this->post_type !== 'post' ) {\n\t\t\t\t\t$page = '&' . $page;\n\t\t\t\t}\n\n\t\t\t\t$url = 'edit.php?' . $parsed_url['query'] . $page;\n\t\t\t}\n\n\t\t\twp_safe_redirect( $url );\n\t\t\tpapi_is_admin() && exit;\n\t\t}\n\t}\n\n\t/**\n\t * Redirect post location when post is in iframe mode.\n\t *\n\t * @param  string $location\n\t *\n\t * @return string\n\t */\n\tpublic function redirect_post_location( $location ) {\n\t\tif ( ! isset( $_SERVER['HTTP_REFERER'] ) ) {\n\t\t\treturn $location;\n\t\t}\n\n\t\t$referer = $_SERVER['HTTP_REFERER'];\n\t\t$referer = strtolower( $referer );\n\n\t\tif ( strpos( $referer, 'papi-iframe-mode' ) === false ) {\n\t\t\treturn $location;\n\t\t}\n\n\t\treturn sprintf( '%s&papi_css[]=papi-iframe-mode', $location );\n\t}\n\n\t/**\n\t * Setup admin entry.\n\t */\n\tpublic function setup() {\n\t\t// Preload all page types.\n\t\tforeach ( papi_get_post_types() as $post_type ) {\n\t\t\tpapi_get_all_entry_types( [\n\t\t\t\t'args' => $post_type\n\t\t\t] );\n\t\t}\n\n\t\treturn ! in_array( $this->post_type, ['revision', 'nav_menu_item'], true );\n\t}\n\n\t/**\n\t * Setup actions.\n\t */\n\tprotected function setup_actions() {\n\t\tadd_action( 'load-post-new.php', [$this, 'load_post_new'] );\n\t\tadd_action( 'add_meta_boxes', [$this, 'hidden_meta_boxes'], 10 );\n\t\tadd_action( 'redirect_post_location', [$this, 'redirect_post_location'] );\n\t}\n\n\t/**\n\t * Setup filters.\n\t */\n\tprotected function setup_filters() {\n\t\tadd_filter( 'wp_get_revision_ui_diff', [$this, 'get_revision_ui_diff'], 10, 3 );\n\t}\n}\n\nif ( papi_is_admin() ) {\n\tPapi_Admin_Entry_Post::instance();\n}\n"
  },
  {
    "path": "src/admin/class-papi-admin-entry-taxonomy.php",
    "content": "<?php\n\n/**\n * Admin class that handles taxonomy modifications.\n */\nclass Papi_Admin_Entry_Taxonomy extends Papi_Admin_Entry {\n\n\t/**\n\t * All taxonomy types.\n\t *\n\t * @var array\n\t */\n\tprotected $taxonomy_types = [];\n\n\t/**\n\t * The construct.\n\t */\n\tpublic function __construct() {\n\t\t$this->setup_actions();\n\t}\n\n\t/**\n\t * Add form fields to edit tags page.\n\t */\n\tpublic function add_form_fields() {\n\t\t$html_name       = esc_attr( papi_get_page_type_key() );\n\t\t$taxonomy        = papi_get_qs( 'taxonomy' );\n\t\t$taxonomy_object = get_taxonomy( $taxonomy );\n\n\t\t// Get only the taxonomy types that has the taxonomy.\n\t\t$taxonomy_types = array_filter( $this->taxonomy_types, function ( $taxonomy_type ) use ( $taxonomy ) {\n\t\t\treturn in_array( $taxonomy, $taxonomy_type->taxonomy, true ) && $taxonomy_type->display( $taxonomy );\n\t\t} );\n\t\t$taxonomy_types = array_values( $taxonomy_types );\n\n\t\t// Do not display empty select if no taxonomy types.\n\t\tif ( empty( $taxonomy_types ) ) {\n\t\t\treturn;\n\t\t}\n\n\t\t// Prepare taxonomy types with standard taxonomy type.\n\t\t$taxonomy_types = $this->prepare_taxonomy_types( $taxonomy_types );\n\n\t\t// Render a dropdown if more than one taxonomy types\n\t\t// exists on the taxonomy.\n\t\tif ( count( $taxonomy_types ) > 1 ):\n\t\t\t?>\n\t\t\t<div class=\"form-field\">\n\t\t\t\t<label for=\"<?php echo esc_attr( $html_name ); ?>\">\n\t\t\t\t\t<?php echo esc_html( sprintf( __( '%s type', 'papi' ), $taxonomy_object->labels->singular_name ) ); ?>\n\t\t\t\t</label>\n\t\t\t\t<select name=\"<?php echo esc_attr( $html_name ); ?>\" id=\"<?php echo esc_attr( $html_name ); ?>\" data-papi-page-type-key=\"true\">\n\t\t\t\t\t<?php\n\t\t\t\t\tforeach ( $taxonomy_types as $taxonomy_type ) {\n\t\t\t\t\t\tpapi_render_html_tag( 'option', [\n\t\t\t\t\t\t\t'data-redirect' => $taxonomy_type->redirect_after_create,\n\t\t\t\t\t\t\t'value'         => esc_attr( $taxonomy_type->get_id() ),\n\t\t\t\t\t\t\tesc_html( $taxonomy_type->name )\n\t\t\t\t\t\t] );\n\t\t\t\t\t}\n\t\t\t\t\t?>\n\t\t\t\t</select>\n\t\t\t</div>\n\t\t\t<?php\n\t\telse:\n\t\t\tpapi_render_html_tag( 'input', [\n\t\t\t\t'data-redirect'           => $taxonomy_types[0]->redirect_after_create,\n\t\t\t\t'data-papi-page-type-key' => true,\n\t\t\t\t'name'                    => esc_attr( $html_name ),\n\t\t\t\t'type'                    => 'hidden',\n\t\t\t\t'value'                   => esc_attr( $taxonomy_types[0]->get_id() )\n\t\t\t] );\n\t\tendif;\n\t}\n\n\t/**\n\t * Prepare taxonomy types, add standard taxonomy if it should be added.\n\t *\n\t * @param  array $taxonomy_types\n\t *\n\t * @return array\n\t */\n\tprotected function prepare_taxonomy_types( array $taxonomy_types ) {\n\t\t$taxonomy = papi_get_qs( 'taxonomy' );\n\n\t\tif ( papi_filter_settings_show_standard_taxonomy_type( $taxonomy ) ) {\n\t\t\t$id                      = sprintf( 'papi-standard-%s-type', $taxonomy );\n\t\t\t$taxonomy_type           = new Papi_Taxonomy_Type( $id );\n\t\t\t$taxonomy_type->id       = $id;\n\t\t\t$taxonomy_type->name     = papi_filter_settings_standard_taxonomy_type_name( $taxonomy );\n\t\t\t$taxonomy_type->taxonomy = [$taxonomy];\n\t\t\t$taxonomy_types[]        = $taxonomy_type;\n\t\t}\n\n\t\tusort( $taxonomy_types, function ( $a, $b ) {\n\t\t\treturn strcmp( $a->name, $b->name );\n\t\t} );\n\n\t\treturn papi_sort_order( array_reverse( $taxonomy_types ) );\n\t}\n\n\t/**\n\t * Setup actions.\n\t */\n\tprotected function setup_actions() {\n\t\tadd_action( 'admin_init', [$this, 'setup_taxonomies_hooks'] );\n\t}\n\n\t/**\n\t * Setup hooks for all taxonomies.\n\t */\n\tpublic function setup_taxonomies_hooks() {\n\t\t$this->taxonomy_types = papi_get_all_entry_types( [\n\t\t\t'types' => 'taxonomy'\n\t\t] );\n\n\t\t$taxonomies = array_reduce( $this->taxonomy_types, function ( $taxonomies, $taxonomy_type ) {\n\t\t\treturn array_merge( $taxonomies, $taxonomy_type->taxonomy );\n\t\t}, [] );\n\t\t$taxonomies = array_unique( $taxonomies );\n\n\t\tforeach ( $taxonomies as $taxonomy ) {\n\t\t\tif ( is_string( $taxonomy ) && taxonomy_exists( $taxonomy ) ) {\n\t\t\t\tadd_action( $taxonomy . '_add_form_fields', [$this, 'add_form_fields'] );\n\t\t\t}\n\t\t}\n\t}\n}\n\nif ( papi_is_admin() ) {\n\tPapi_Admin_Entry_Taxonomy::instance();\n}\n"
  },
  {
    "path": "src/admin/class-papi-admin-entry.php",
    "content": "<?php\n\nabstract class Papi_Admin_Entry {\n\n\t/**\n\t * The instance.\n\t *\n\t * @var Papi_Admin_Entry\n\t */\n\tprotected static $instance;\n\n\t/**\n\t * Create or get the instance.\n\t *\n\t * @return Papi_Admin_Entry\n\t */\n\tpublic static function instance() {\n\t\tif ( ! isset( static::$instance ) ) {\n\t\t\tstatic::$instance = new static;\n\t\t}\n\n\t\treturn static::$instance;\n\t}\n\n\t/**\n\t * Setup admin entry.\n\t *\n\t * @return bool\n\t */\n\tpublic function setup() {\n\t\treturn true;\n\t}\n\n\t/**\n\t * Setup actions.\n\t */\n\tprotected function setup_actions() {\n\t}\n\n\t/**\n\t * Setup filters.\n\t */\n\tprotected function setup_filters() {\n\t}\n}\n"
  },
  {
    "path": "src/admin/class-papi-admin-menu.php",
    "content": "<?php\n\n/**\n * Admin class that handles admin menus.\n */\nfinal class Papi_Admin_Menu {\n\n\t/**\n\t * The construct.\n\t */\n\tpublic function __construct() {\n\t\t$this->setup_actions();\n\t}\n\n\t/**\n\t * Fill labels on admin bar.\n\t */\n\tpublic function admin_bar_menu() {\n\t\tif ( $entry_type = $this->get_entry_type() ) {\n\t\t\t$this->override_labels( $entry_type );\n\t\t}\n\t}\n\n\t/**\n\t * Get current type class.\n\t *\n\t * @return Papi_Entry_Type\n\t */\n\tprotected function get_entry_type() {\n\t\tif ( $entry_type = papi_get_entry_type_by_id( papi_get_entry_type_id() ) ) {\n\t\t\treturn $entry_type;\n\t\t}\n\t}\n\n\t/**\n\t * Override labels with labels from the entry type.\n\t *\n\t * @param Papi_Entry_Type $entry_type\n\t */\n\tprotected function override_labels( Papi_Entry_Type $entry_type ) {\n\t\tglobal $wp_post_types, $wp_taxonomies;\n\n\t\tif ( $entry_type->get_type() === 'taxonomy' ) {\n\t\t\t$meta_type_value = papi_get_taxonomy();\n\t\t} else {\n\t\t\t$meta_type_value = papi_get_post_type();\n\t\t}\n\n\t\tif ( empty( $meta_type_value ) || ( ! isset( $wp_post_types[$meta_type_value] ) && ! isset( $wp_taxonomies[$meta_type_value] ) ) ) {\n\t\t\treturn;\n\t\t}\n\n\t\tforeach ( $entry_type->get_labels() as $key => $value ) {\n\t\t\tif ( empty( $value ) ) {\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tif ( $entry_type->get_type() === 'taxonomy' && isset( $wp_taxonomies[$meta_type_value]->labels->$key ) ) {\n\t\t\t\t$wp_taxonomies[$meta_type_value]->labels->$key = $value;\n\t\t\t} else if ( isset( $wp_post_types[$meta_type_value]->labels->$key ) ) {\n\t\t\t\t$wp_post_types[$meta_type_value]->labels->$key = $value;\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * Page items menu.\n\t *\n\t * This function will register all entry types\n\t * that has a fake post type. Like option types.\n\t */\n\tpublic function page_items_menu() {\n\t\t$entry_types = papi_get_all_entry_types( [\n\t\t\t'mode'  => 'exclude',\n\t\t\t'types' => 'page'\n\t\t] );\n\n\t\tforeach ( $entry_types as $entry_type ) {\n\t\t\tif ( empty( $entry_type->get_menu() ) || empty( $entry_type->name ) ) {\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\t$slug = sprintf(\n\t\t\t\t'papi/%s/%s',\n\t\t\t\t$entry_type->get_type(),\n\t\t\t\t$entry_type->get_id()\n\t\t\t);\n\n\t\t\tadd_submenu_page(\n\t\t\t\t$entry_type->get_menu(),\n\t\t\t\t$entry_type->name,\n\t\t\t\t$entry_type->name,\n\t\t\t\t$entry_type->capability,\n\t\t\t\t$slug,\n\t\t\t\t[$entry_type, 'render']\n\t\t\t);\n\t\t}\n\t}\n\n\t/**\n\t * Setup menu items for real post types.\n\t */\n\tpublic function post_types_menu() {\n\t\tglobal $submenu;\n\n\t\t$post_types = papi_get_post_types();\n\n\t\tforeach ( $post_types as $post_type ) {\n\t\t\tif ( ! post_type_exists( $post_type ) ) {\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tif ( $post_type === 'post' ) {\n\t\t\t\t$edit_url = 'edit.php';\n\t\t\t} else {\n\t\t\t\t$edit_url = 'edit.php?post_type=' . $post_type;\n\t\t\t}\n\n\t\t\tif ( ! isset( $submenu[$edit_url], $submenu[$edit_url][10], $submenu[$edit_url][10][2] ) ) {\n\t\t\t\t$post_type_object = get_post_type_object( $post_type );\n\n\t\t\t\tif ( $post_type_object->show_in_menu !== true ) {\n\t\t\t\t\t$submenu[$edit_url] = [\n\t\t\t\t\t\t10 => [\n\t\t\t\t\t\t\t__( 'Add New', 'papi' ),\n\t\t\t\t\t\t\t'edit_posts',\n\t\t\t\t\t\t\t'post-new.php'\n\t\t\t\t\t\t]\n\t\t\t\t\t];\n\t\t\t\t} else {\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t$only_page_type = papi_filter_settings_only_page_type( $post_type );\n\t\t\t$page_types     = papi_get_all_page_types( $post_type );\n\t\t\t$show_standard  = false;\n\n\t\t\t// Don't change menu item when no page types is found.\n\t\t\tif ( empty( $page_types ) ) {\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tif ( count( $page_types ) === 1 && empty( $only_page_type ) ) {\n\t\t\t\t$show_standard  = papi_filter_settings_show_standard_page_type( $post_type );\n\t\t\t\t$only_page_type = $show_standard ? '' : $page_types[0]->get_id();\n\t\t\t}\n\n\t\t\tif ( ! empty( $only_page_type ) && ! $show_standard ) {\n\t\t\t\t$submenu[$edit_url][10][2] = papi_get_page_new_url(\n\t\t\t\t\t$only_page_type,\n\t\t\t\t\tfalse,\n\t\t\t\t\t$post_type,\n\t\t\t\t\t[\n\t\t\t\t\t\t'post_parent',\n\t\t\t\t\t\t'lang'\n\t\t\t\t\t]\n\t\t\t\t);\n\t\t\t} else {\n\t\t\t\t$page  = 'papi-add-new-page,' . $post_type;\n\t\t\t\t$start = strpos( $edit_url, 'post_type' ) === false ? '?' : '&';\n\n\t\t\t\t$submenu[$edit_url][10][2] = sprintf(\n\t\t\t\t\t'%s%spage=%s',\n\t\t\t\t\t$edit_url,\n\t\t\t\t\t$start,\n\t\t\t\t\t$page\n\t\t\t\t);\n\n\t\t\t\t// Add menu item.\n\t\t\t\tadd_menu_page(\n\t\t\t\t\t__( 'Add New', 'papi' ),\n\t\t\t\t\t__( 'Add New', 'papi' ),\n\t\t\t\t\t'read',\n\t\t\t\t\t$page,\n\t\t\t\t\t[$this, 'render_view']\n\t\t\t\t);\n\n\t\t\t\t// Remove the menu item so it's hidden.\n\t\t\t\tremove_menu_page( $page );\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * Menu callback that loads right view depending on what the `page` query string says.\n\t */\n\tpublic function render_view() {\n\t\tif ( strpos( papi_get_qs( 'page' ), 'papi' ) !== false ) {\n\t\t\t$page = str_replace( 'papi-', '', papi_get_qs( 'page' ) );\n\t\t\t$res  = preg_replace( '/\\,.*/', '', $page );\n\n\t\t\tif ( is_string( $res ) ) {\n\t\t\t\t$page_view = $res;\n\t\t\t}\n\t\t}\n\n\t\tif ( ! isset( $page_view ) ) {\n\t\t\t$page_view = null;\n\t\t}\n\n\t\tif ( ! is_null( $page_view ) ) {\n\t\t\t$view = new Papi_Admin_View;\n\t\t\t$view->render( $page_view );\n\t\t} else {\n\t\t\techo '<h2>Papi - 404</h2>';\n\t\t}\n\t}\n\n\t/**\n\t * Setup actions.\n\t */\n\tprotected function setup_actions() {\n\t\tif ( papi_is_admin() ) {\n\t\t\tadd_action( 'admin_init', [$this, 'admin_bar_menu'] );\n\t\t\tadd_action( 'admin_menu', [$this, 'page_items_menu'] );\n\t\t\tadd_action( 'admin_menu', [$this, 'post_types_menu'] );\n\t\t} else {\n\t\t\tadd_action( 'admin_bar_menu', [$this, 'admin_bar_menu'] );\n\t\t}\n\t}\n}\n\nnew Papi_Admin_Menu;\n"
  },
  {
    "path": "src/admin/class-papi-admin-meta-box-tabs.php",
    "content": "<?php\n\n/**\n * Admin class that handles admin tabs in\n * meta boxes.\n */\nfinal class Papi_Admin_Meta_Box_Tabs {\n\n\t/**\n\t * The tabs.\n\t *\n\t * @var array\n\t */\n\tprotected $tabs = [];\n\n\t/**\n\t * The constructor.\n\t *\n\t * @param array $tabs\n\t * @param bool  $render\n\t */\n\tpublic function __construct( array $tabs = [], $render = true ) {\n\t\tif ( empty( $tabs ) ) {\n\t\t\treturn;\n\t\t}\n\n\t\t$this->tabs = papi_tabs_setup( $tabs );\n\n\t\tif ( $render ) {\n\t\t\t$this->html();\n\t\t}\n\t}\n\n\t/**\n\t * Get the tabs that are registered.\n\t *\n\t * @return array\n\t */\n\tpublic function get_tabs() {\n\t\treturn $this->tabs;\n\t}\n\n\t/**\n\t * Generate html for tabs and properties.\n\t */\n\tprotected function html() {\n\t\t?>\n\t\t<div class=\"papi-tabs-wrapper\">\n\t\t\t<div class=\"papi-tabs-table-back\"></div>\n\t\t\t<div class=\"papi-tabs-back\"></div>\n\t\t\t<ul class=\"papi-tabs\">\n\t\t\t\t<?php\n\t\t\t\tforeach ( $this->tabs as $tab ):\n\t\t\t\t\t$css_classes = $this->tabs[0] === $tab ? 'active ' : '';\n\n\t\t\t\t\tif ( empty( $tab->background ) ) {\n\t\t\t\t\t\t// Find out if the first property has a sidebar or not. If the first property\n\t\t\t\t\t\t// don't have a sidebar the tab background should be white since it looks better.\n\t\t\t\t\t\t$no_sidebar = empty( $tab->properties ) ? false : $tab->properties[0]->sidebar;\n\t\t\t\t\t\t$css_classes .= ! empty( $tab->properties ) && $no_sidebar ? '' : 'white-tab';\n\t\t\t\t\t} else {\n\t\t\t\t\t\t$css_classes .= $tab->background === 'white' ? 'white-tab' : '';\n\t\t\t\t\t}\n\t\t\t\t\t?>\n\t\t\t\t\t<li class=\"<?php echo esc_attr( $css_classes ); ?>\">\n\t\t\t\t\t\t<a href=\"#\" data-papi-tab=\"<?php echo esc_attr( $tab->id ); ?>\">\n\t\t\t\t\t\t\t<?php if ( ! empty( $tab->icon ) ): ?>\n\t\t\t\t\t\t\t\t<img src=\"<?php echo esc_attr( $tab->icon ); ?>\" alt=\"<?php echo esc_attr( $tab->title ); ?>\" />\n\t\t\t\t\t\t\t<?php endif;\n\t\t\t\t\t\t\techo esc_html( $tab->title ); ?>\n\t\t\t\t\t\t</a>\n\t\t\t\t\t</li>\n\t\t\t\t<?php endforeach; ?>\n\t\t\t</ul>\n\t\t\t<div class=\"papi-tabs-content\">\n\t\t\t\t<?php\n\t\t\t\tforeach ( $this->tabs as $tab ):\n\t\t\t\t\t?>\n\t\t\t\t\t<div class=\"<?php echo $this->tabs[0] === $tab ? 'active' : ''; ?>\" data-papi-tab=\"<?php echo esc_attr( $tab->id ); ?>\">\n\t\t\t\t\t\t<?php papi_render_properties( $tab->properties ); ?>\n\t\t\t\t\t</div>\n\t\t\t\t\t<?php\n\t\t\t\tendforeach;\n\t\t\t\t?>\n\t\t\t</div>\n\t\t</div>\n\t\t<div class=\"papi-clear\"></div>\n\t\t<?php\n\t}\n}\n"
  },
  {
    "path": "src/admin/class-papi-admin-meta-box.php",
    "content": "<?php\n\n/**\n * Admin class that handles admin meta boxes.\n */\nfinal class Papi_Admin_Meta_Box {\n\n\t/**\n\t * The core box.\n\t *\n\t * @var Papi_Core_Box\n\t */\n\tprotected $box;\n\n\t/**\n\t * The constructor.\n\t *\n\t * @param Papi_Core_Box $box\n\t */\n\tpublic function __construct( Papi_Core_Box $box ) {\n\t\t// Check if the current user is allowed to view this box.\n\t\tif ( ! papi_current_user_is_allowed( $box->capabilities ) ) {\n\t\t\treturn;\n\t\t}\n\n\t\tif ( $box->display ) {\n\t\t\t$this->box = $box;\n\t\t\t$this->setup_actions();\n\t\t}\n\t}\n\n\t/**\n\t * Add custom css for hiding boxes with no frame in screen options.\n\t *\n\t * @return string\n\t */\n\tpublic function admin_head() {\n\t\tif ( ! $this->box->frame ) {\n\t\t\techo sprintf(\n\t\t\t\t'<style type=\"text/css\">label[for=\"%s-hide\"],#%s>h2,#%s>button{display:none !important}</style>',\n\t\t\t\tesc_attr( $this->box->id ),\n\t\t\t\tesc_attr( $this->box->id ),\n\t\t\t\tesc_attr( $this->box->id )\n\t\t\t);\n\t\t}\n\t}\n\n\t/**\n\t * Add css classes to meta box.\n\t *\n\t * @param  array $classes\n\t *\n\t * @return string[]\n\t */\n\tpublic function meta_box_css_classes( array $classes ) {\n\t\treturn array_merge( $classes, [\n\t\t\t'papi-box'\n\t\t] );\n\t}\n\n\t/**\n\t * Move meta boxes after title.\n\t */\n\tpublic function move_meta_box_after_title() {\n\t\tglobal $post, $wp_meta_boxes;\n\t\tdo_meta_boxes( get_current_screen(), $this->box->context, $post );\n\t\tunset( $wp_meta_boxes[get_post_type( $post )][$this->box->context] );\n\t}\n\n\t/**\n\t * Get meta post type.\n\t *\n\t * @return string\n\t */\n\tprotected function get_post_type() {\n\t\tif ( papi_get_meta_type() === 'post' ) {\n\t\t\tif ( $post_id = papi_get_post_id() ) {\n\t\t\t\treturn get_post_type( $post_id );\n\t\t\t}\n\n\t\t\tif ( $post_type = papi_get_post_type() ) {\n\t\t\t\treturn $post_type;\n\t\t\t}\n\t\t}\n\n\t\treturn $this->box->id;\n\t}\n\n\t/**\n\t * Get meta box title.\n\t *\n\t * @return string\n\t */\n\tprotected function get_title() {\n\t\t$title = $this->box->title;\n\n\t\tif ( $this->box->get_option( 'required' ) ) {\n\t\t\t$title .= papi_property_required_html(\n\t\t\t\t$this->box->properties[0],\n\t\t\t\ttrue\n\t\t\t);\n\t\t}\n\n\t\treturn $title;\n\t}\n\n\t/**\n\t * Render the meta box\n\t *\n\t * @param array $post\n\t * @param array $args\n\t */\n\tpublic function render_meta_box( $post, array $args ) {\n\t\tif ( ! isset( $args['args'] ) ) {\n\t\t\treturn;\n\t\t}\n\n\t\t// Do a last check before all properties is rendered.\n\t\t$args['args'] = array_filter( $args['args'], 'papi_is_property' );\n\n\t\t// Inherit options from the box.\n\t\tforeach ( $args['args'] as $index => $property ) {\n\t\t\tif ( $property->layout === 'horizontal' ) {\n\t\t\t\t$args['args'][$index]->layout = $this->box->layout;\n\t\t\t}\n\t\t}\n\n\t\t// Render the properties.\n\t\tpapi_render_properties( papi_sort_order( array_reverse( $args['args'] ) ) );\n\t}\n\n\t/**\n\t * Setup action hooks.\n\t */\n\tprotected function setup_actions() {\n\t\tif ( post_type_exists( $this->get_post_type() ) && papi_get_meta_type() === 'post' ) {\n\t\t\tadd_action( 'add_meta_boxes', [$this, 'setup_meta_box'] );\n\n\t\t\tif ( $this->box->context === 'after_title' ) {\n\t\t\t\tadd_action( 'edit_form_after_title', [$this, 'move_meta_box_after_title'] );\n\t\t\t}\n\t\t} else {\n\t\t\t$this->setup_meta_box();\n\t\t}\n\n\t\t// Will be called on when you call do_meta_boxes\n\t\t// even without a real post type.\n\t\tadd_action(\n\t\t\tsprintf(\n\t\t\t\t'postbox_classes_%s_%s',\n\t\t\t\tstrtolower( $this->get_post_type() ),\n\t\t\t\t$this->box->id\n\t\t\t),\n\t\t\t[$this, 'meta_box_css_classes']\n\t\t);\n\n\t\tadd_action( 'admin_head', [$this, 'admin_head'] );\n\t}\n\n\t/**\n\t * Setup meta box.\n\t */\n\tpublic function setup_meta_box() {\n\t\t$properties = $this->box->properties;\n\n\t\t// Check all properties and remove them that can't be rendered.\n\t\tforeach ( $properties as $index => $property ) {\n\t\t\tif ( $property instanceof Papi_Property && ! $property->can_render() ) {\n\t\t\t\tunset( $properties[$index] );\n\t\t\t}\n\t\t}\n\n\t\t// Bail if properties array is empty,\n\t\t// no need to render a empty meta box.\n\t\tif ( empty( $properties ) ) {\n\t\t\treturn;\n\t\t}\n\n\t\tadd_meta_box(\n\t\t\t$this->box->id,\n\t\t\t$this->get_title(),\n\t\t\t[$this, 'render_meta_box'],\n\t\t\t$this->get_post_type(),\n\t\t\t$this->box->context,\n\t\t\t$this->box->priority,\n\t\t\t$properties\n\t\t);\n\t}\n}\n"
  },
  {
    "path": "src/admin/class-papi-admin-meta-handler.php",
    "content": "<?php\n\n/**\n * Admin class that handle meta data, like post or term.\n */\nfinal class Papi_Admin_Meta_Handler extends Papi_Core_Data_Handler {\n\n\t/**\n\t * Get meta type so we know where the data should be saved.\n\t *\n\t * @return string\n\t */\n\tprotected function get_meta_type() {\n\t\tif ( $current_filter = current_filter() ) {\n\t\t\treturn papi_get_meta_type( explode( '_', $current_filter )[1] );\n\t\t}\n\n\t\treturn papi_get_meta_type();\n\t}\n\n\t/**\n\t * Overwrite post data in `posts` table.\n\t *\n\t * @param int $post_id\n\t */\n\tprotected function overwrite_post_data( $post_id ) {\n\t\tglobal $wpdb;\n\n\t\tif ( empty( $post_id ) || empty( $this->overwrite ) ) {\n\t\t\treturn;\n\t\t}\n\n\t\t$wpdb->update( $wpdb->posts, $this->overwrite, [\n\t\t\t'ID' => $post_id\n\t\t] );\n\n\t\t// Delete cache since it can be cached even if not saved in the database.\n\t\tforeach ( array_keys( $this->overwrite ) as $key ) {\n\t\t\tpapi_cache_delete( $key, $post_id );\n\t\t}\n\n\t\tclean_post_cache( $post_id );\n\t}\n\n\t/**\n\t * Pre save page template and page type.\n\t *\n\t * @param int $id\n\t */\n\tprotected function pre_save( $id ) {\n\t\tif ( empty( $id ) ) {\n\t\t\treturn;\n\t\t}\n\n\t\t$data = $this->get_pre_data();\n\n\t\tforeach ( $data as $key => $value ) {\n\t\t\tif ( empty( $value ) ) {\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tif ( is_array( $value ) ) {\n\t\t\t\tlist( $keys, $value ) = $this->get_pre_deep_keys_value( $value );\n\t\t\t\t$key = sprintf( '%s_%s', $key, implode( '_', $keys ) );\n\t\t\t}\n\n\t\t\tupdate_metadata( $this->get_meta_type(), $id, $key, $value );\n\t\t}\n\t}\n\n\t/**\n\t * Save meta boxes.\n\t *\n\t * @param int     $id\n\t * @param object  $post\n\t */\n\tpublic function save_meta_boxes( $id, $post = null ) {\n\t\t// Check if there was a multisite switch before.\n\t\tif ( is_multisite() && ms_is_switched() ) {\n\t\t\treturn;\n\t\t}\n\n\t\t// Can't proceed without a id.\n\t\tif ( empty( $id ) ) {\n\t\t\treturn;\n\t\t}\n\n\t\t// Check if our nonce is vailed.\n\t\tif ( ! wp_verify_nonce( papi_get_sanitized_post( 'papi_meta_nonce' ), 'papi_save_data' ) ) {\n\t\t\treturn;\n\t\t}\n\n\t\t$meta_type = $this->get_meta_type();\n\t\t$post      = is_array( $post ) ? (object) $post : $post;\n\n\t\tif ( $meta_type === 'post' && $post_type = get_post_type_object( $post->post_type ) ) {\n\t\t\t// Check so the id is a post id and not a autosave post.\n\t\t\tif ( ! $this->valid_post_id( $id ) ) {\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\t// Check the `edit_posts` capability before we continue.\n\t\t\tif ( ! current_user_can( $post_type->cap->edit_posts ) ) {\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\t// Delete all oEmbed caches.\n\t\t\tif ( class_exists( 'WP_Embed' ) ) {\n\t\t\t\tglobal $wp_embed;\n\n\t\t\t\tif ( $wp_embed instanceof WP_Embed ) {\n\t\t\t\t\t$wp_embed->delete_oembed_caches( $id );\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tif ( $meta_type === 'term' && $taxonomy = get_taxonomy( papi_get_taxonomy() ) ) {\n\t\t\t// Check the `edit_terms` capability before we continue.\n\t\t\tif ( $taxonomy && ! current_user_can( $taxonomy->cap->edit_terms ) ) {\n\t\t\t\treturn;\n\t\t\t}\n\t\t}\n\n\t\t$this->save_properties( $id );\n\t}\n\n\t/**\n\t * Save custom fields for revision post.\n\t *\n\t * @param int $post_id\n\t */\n\tpublic function save_revision( $revision_id ) {\n\t\t// Check if our nonce is vailed.\n\t\tif ( ! wp_verify_nonce( papi_get_sanitized_post( 'papi_meta_nonce' ), 'papi_save_data' ) ) {\n\t\t\treturn;\n\t\t}\n\n\t\t// Fetch parent id from revision.\n\t\tif ( ! $parent_id = wp_is_post_revision( $revision_id ) ) {\n\t\t\treturn;\n\t\t}\n\n\t\t// Bail if a entry type don't exists on parent post.\n\t\tif ( papi_is_empty( papi_get_entry_type_by_meta_id( $parent_id, 'post' ) ) ) {\n\t\t\treturn;\n\t\t}\n\n\t\t$meta = get_post_meta( $parent_id );\n\n\t\tforeach ( $meta as $key => $value ) {\n\t\t\tif ( $key[0] === '_' && $key !== papi_get_page_type_key() ) {\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tpapi_data_update( $revision_id, $key, array_shift( $value ) );\n\t\t}\n\t}\n\n\t/**\n\t * Save properties.\n\t *\n\t * @param int $id\n\t */\n\tpublic function save_properties( $id ) {\n\t\t// Pre save page template, page type and some others dynamic values.\n\t\t$this->pre_save( $id );\n\n\t\t// Get properties data.\n\t\t$data = $this->get_post_data();\n\n\t\t// Prepare properties data.\n\t\t$data = $this->prepare_properties_data( $data, $id );\n\n\t\t// Get meta type.\n\t\t$meta_type = $this->get_meta_type();\n\n\t\t// Overwrite post data if any.\n\t\tif ( $meta_type === 'post' ) {\n\t\t\t$this->overwrite_post_data( $id );\n\t\t}\n\n\t\t// Save all properties value\n\t\tforeach ( $data as $key => $value ) {\n\t\t\tpapi_data_update( $id, $key, $value, $this->get_meta_type() );\n\t\t}\n\n\t\t/**\n\t\t * Fire `save_properties` action when all is done.\n\t\t *\n\t\t * @param int    $id\n\t\t * @param string $meta_type\n\t\t */\n\t\tdo_action( 'papi/save_properties', $id, $meta_type );\n\t}\n\n\t/**\n\t * Restore to post revision.\n\t *\n\t * @param int $post_id\n\t * @param int $revision_id\n\t */\n\tpublic function restore_post_revision( $post_id, $revision_id ) {\n\t\tif ( papi_is_empty( papi_get_entry_type_by_meta_id( $post_id, 'post' ) ) ) {\n\t\t\treturn;\n\t\t}\n\n\t\t$meta = get_post_meta( $revision_id );\n\n\t\tforeach ( $meta as $key => $value ) {\n\t\t\tif ( $key[0] === '_' && $key !== papi_get_page_type_key() ) {\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tpapi_data_update( $post_id, $key, array_shift( $value ) );\n\t\t}\n\t}\n\n\t/**\n\t * Setup actions.\n\t */\n\tprotected function setup_actions() {\n\t\tadd_action( '_wp_put_post_revision', [$this, 'save_revision'] );\n\t\tadd_action( 'save_post', [$this, 'save_meta_boxes'], 1, 2 );\n\t\tadd_action( 'created_term', [$this, 'save_meta_boxes'], 1 );\n\t\tadd_action( 'edit_term', [$this, 'save_meta_boxes'], 1 );\n\t\tadd_action( 'wp_restore_post_revision', [$this, 'restore_post_revision'], 10, 2 );\n\t}\n\n\t/**\n\t * Check if post id is valid or not.\n\t *\n\t * @param  int $post_id\n\t *\n\t * @return bool\n\t */\n\tprotected function valid_post_id( $post_id ) {\n\t\t$key = papi_get_sanitized_post( 'action' ) === 'save-attachment-compat' ? 'id' : 'post_ID';\n\t\t$val = papi_get_sanitized_post( $key );\n\n\t\t// When autosave is in place the post id is located deeper in the post data array, the ids should not match.\n\t\tif ( isset( $_POST['data']['wp_autosave'], $_POST['data']['wp_autosave']['post_id'] ) ) {\n\t\t\t// But if it's a auto draft the ids should match.\n\t\t\tif ( isset( $_POST['data']['wp_autosave']['auto_draft'] ) && ! empty( $_POST['data']['wp_autosave']['auto_draft'] ) ) {\n\t\t\t\treturn sanitize_text_field( $_POST['data']['wp_autosave']['post_id'] ) === strval( $post_id );\n\t\t\t}\n\n\t\t\treturn sanitize_text_field( $_POST['data']['wp_autosave']['post_id'] ) !== strval( $post_id );\n\t\t}\n\n\t\t// Should not be the same id when `wp-preview` equals `dopreview`.\n\t\tif ( isset( $_POST['wp-preview'] ) && strtolower( $_POST['wp-preview'] ) === 'dopreview' ) {\n\t\t\treturn $val !== strval( $post_id );\n\t\t}\n\n\t\treturn $val === strval( $post_id );\n\t}\n}\n\nif ( papi_is_admin() ) {\n\tnew Papi_Admin_Meta_Handler;\n}\n"
  },
  {
    "path": "src/admin/class-papi-admin-option-handler.php",
    "content": "<?php\n\n/**\n * Admin class that handle option post data.\n */\nfinal class Papi_Admin_Option_Handler extends Papi_Core_Data_Handler {\n\n\t/**\n\t * Save properties with a post id of zero.\n\t */\n\tpublic function save_properties() {\n\t\tif ( $_SERVER ['REQUEST_METHOD'] !== 'POST' || papi_get_meta_type() !== 'option' ) {\n\t\t\treturn;\n\t\t}\n\n\t\t// Check if our nonce is vailed.\n\t\tif ( ! wp_verify_nonce( papi_get_sanitized_post( 'papi_meta_nonce' ), 'papi_save_data' ) ) {\n\t\t\treturn;\n\t\t}\n\n\t\t// Get properties data.\n\t\t$data = $this->get_post_data();\n\n\t\t// Prepare properties data.\n\t\t$data = $this->prepare_properties_data( $data, 0 );\n\n\t\tforeach ( $data as $key => $value ) {\n\t\t\tpapi_data_update( 0, $key, $value, 'option' );\n\t\t}\n\n\t\t/**\n\t\t * Fire `save_properties` action when all is done.\n\t\t *\n\t\t * @param int    $id\n\t\t * @param string $meta_type\n\t\t */\n\t\tdo_action( 'papi/save_properties', 0, 'option' );\n\t}\n\n\t/**\n\t * Setup actions.\n\t */\n\tprotected function setup_actions() {\n\t\tadd_action( 'admin_init', [$this, 'save_properties'] );\n\t}\n}\n\nif ( papi_is_admin() ) {\n\tnew Papi_Admin_Option_Handler;\n}\n"
  },
  {
    "path": "src/admin/class-papi-admin-page-type-switcher.php",
    "content": "<?php\n\nclass Papi_Admin_Page_Type_Switcher {\n\n\t/**\n\t * The construct.\n\t */\n\tpublic function __construct() {\n\t\tadd_action( 'admin_init', [$this, 'admin_init'] );\n\t}\n\n\t/**\n\t * Admin init hook callback.\n\t */\n\tpublic function admin_init() {\n\t\tadd_action( 'post_submitbox_misc_actions', [$this, 'metabox'] );\n\t\tadd_action( 'save_post', [$this, 'save_post'], 10, 2 );\n\t}\n\n\t/**\n\t * Render metabox.\n\t */\n\tpublic function metabox() {\n\t\t$post_type     = papi_get_post_type();\n\t\t$page_type     = papi_get_entry_type_by_id( papi_get_page_type_id() );\n\t\t$page_type_key = papi_get_page_type_key( 'switch' );\n\t\t$page_types    = papi_get_all_page_types( $post_type );\n\t\t$show_standard = papi_filter_settings_show_standard_page_type( $post_type );\n\n\t\tif ( $show_standard ) {\n\t\t\t$standard_page_type = papi_get_standard_page_type( $post_type );\n\t\t\t$page_types[]       = $standard_page_type;\n\n\t\t\tif ( empty( $page_type ) ) {\n\t\t\t\t$page_type = $standard_page_type;\n\t\t\t}\n\t\t}\n\n\t\tusort( $page_types, function ( $a, $b ) {\n\t\t\treturn strcmp( $a->name, $b->name );\n\t\t} );\n\n\t\t$page_types = papi_sort_order( array_reverse( $page_types ) );\n\n\t\t// Don't do anything without any page types.\n\t\tif ( empty( $page_type ) || empty( $page_types ) ) {\n\t\t\treturn;\n\t\t} ?>\n\n\t\t<div class=\"misc-pub-section misc-pub-section-last papi-page-type-switcher\">\n\t\t\t<label for=\"<?php echo esc_attr( $page_type_key ); ?>\"><?php esc_html_e( 'Page Type:', 'papi' ); ?></label>\n\t\t\t<span><?php echo esc_html( $page_type->name ); ?></span>\n\n\t\t\t<?php if ( papi_current_user_is_allowed( $page_type->capabilities ) && $page_type->switcher ): ?>\n\t\t\t\t<a href=\"#\" id=\"papi-page-type-switcher-edit\" class=\"hide-if-no-js\"><?php esc_html_e( 'Edit', 'papi' ); ?></a>\n\t\t\t\t<div>\n\t\t\t\t\t<select name=\"<?php echo esc_attr( $page_type_key ); ?>\" id=\"<?php echo esc_attr( $page_type_key ); ?>\">\n\t\t\t\t\t\t<?php\n\t\t\t\t\t\tforeach ( $page_types as $pt ) {\n\t\t\t\t\t\t\tif ( ! papi_current_user_is_allowed( $pt->capabilities ) ) {\n\t\t\t\t\t\t\t\tcontinue;\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\tpapi_render_html_tag( 'option', [\n\t\t\t\t\t\t\t\t'selected' => $page_type->match_id( $pt->get_id() ),\n\t\t\t\t\t\t\t\t'value'    => esc_attr( $pt->get_id() ),\n\n\t\t\t\t\t\t\t\tesc_html( $pt->name )\n\t\t\t\t\t\t\t] );\n\t\t\t\t\t\t}\n\t\t\t\t\t\t?>\n\t\t\t\t\t</select>\n\t\t\t\t\t<a href=\"#\" id=\"papi-page-type-switcher-save\" class=\"hide-if-no-js button\"><?php esc_html_e( 'OK', 'papi' ); ?></a>\n\t\t\t\t\t<a href=\"#\" id=\"papi-page-type-switcher-cancel\" class=\"hide-if-no-js\"><?php esc_html_e( 'Cancel', 'papi' ); ?></a>\n\t\t\t\t</div>\n\t\t\t<?php endif; ?>\n\t\t</div>\n\t\t<?php\n\t}\n\n\t/**\n\t * Switch page type if all checks pass.\n\t *\n\t * @param  int     $post_id\n\t * @param  WP_post $post\n\t *\n\t * @return bool\n\t */\n\tpublic function save_post( $post_id, $post ) {\n\t\t// Check if post id and post object is empty or not.\n\t\tif ( empty( $post_id ) || empty( $post ) ) {\n\t\t\treturn false;\n\t\t}\n\n\t\t// Check if our nonce is vailed.\n\t\tif ( ! wp_verify_nonce( papi_get_sanitized_post( 'papi_meta_nonce' ), 'papi_save_data' ) ) {\n\t\t\treturn false;\n\t\t}\n\n\t\t// Check if so both page type keys exists.\n\t\tif ( empty( $_POST[papi_get_page_type_key()] ) || empty( $_POST[papi_get_page_type_key( 'switch' )] ) ) {\n\t\t\treturn false;\n\t\t}\n\n\t\t// Page type information.\n\t\t$page_type_id        = sanitize_text_field( $_POST[papi_get_page_type_key()] );\n\t\t$page_type_switch_id = sanitize_text_field( $_POST[papi_get_page_type_key( 'switch' )] );\n\n\t\t// Don't update if the same ids.\n\t\tif ( $page_type_id === $page_type_switch_id ) {\n\t\t\treturn false;\n\t\t}\n\n\t\t// Fetch right page type if standard page type id.\n\t\tif ( papi_get_standard_page_type_id( $post->post_type ) === $page_type_id ) {\n\t\t\t$page_type = papi_get_standard_page_type( $post->post_type );\n\t\t} else {\n\t\t\t$page_type = papi_get_entry_type_by_id( $page_type_id );\n\t\t}\n\n\t\t// Fetch right page type switch if standard page type id.\n\t\tif ( papi_get_standard_page_type_id( $post->post_type ) === $page_type_switch_id ) {\n\t\t\t$page_type_switch    = papi_get_standard_page_type( $post->post_type );\n\t\t\t$page_type_switch_id = '';\n\t\t} else {\n\t\t\t$page_type_switch = papi_get_entry_type_by_id( $page_type_switch_id );\n\t\t}\n\n\t\t$post_type_object = get_post_type_object( $post->post_type );\n\n\t\t// Check if page type and post type is not empty.\n\t\tif ( empty( $page_type_switch ) || empty( $post_type_object ) ) {\n\t\t\treturn false;\n\t\t}\n\n\t\t// Check if autosave.\n\t\tif ( wp_is_post_autosave( $post_id ) ) {\n\t\t\treturn false;\n\t\t}\n\n\t\t// Check if revision.\n\t\tif ( wp_is_post_revision( $post_id ) ) {\n\t\t\treturn false;\n\t\t}\n\n\t\t// Check if revision post type.\n\t\tif ( in_array( $post->post_type, ['revision', 'nav_menu_item'], true ) ) {\n\t\t\treturn false;\n\t\t}\n\n\t\t// Check so page type has the post type.\n\t\tif ( ! $page_type->has_post_type( $post->post_type ) || ! $page_type_switch->has_post_type( $post->post_type ) ) {\n\t\t\treturn false;\n\t\t}\n\n\t\t// Check page type capabilities.\n\t\tif ( ! papi_current_user_is_allowed( $page_type_switch->capabilities ) ) {\n\t\t\treturn false;\n\t\t}\n\n\t\t// Check so user can edit posts and that the user can publish posts on the post type.\n\t\tif ( ! current_user_can( 'edit_post', $post_id ) || ! current_user_can( $post_type_object->cap->publish_posts ) ) {\n\t\t\treturn false;\n\t\t}\n\n\t\t// Get properties.\n\t\t$properties        = $page_type->get_properties();\n\t\t$properties_switch = $page_type_switch->get_properties();\n\n\t\t// Delete only properties that don't have the same type and slug.\n\t\tforeach ( $properties as $property ) {\n\t\t\t$delete = true;\n\n\t\t\t// Check if the properties are the same or not.\n\t\t\tforeach ( $properties_switch as $property_switch ) {\n\t\t\t\tif ( $property_switch->type === $property->type && $property_switch->match_slug( $property->get_slug() ) ) {\n\t\t\t\t\t$delete = false;\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif ( ! $delete ) {\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\t// Delete property values.\n\t\t\t$property->delete_value( $property->get_slug( true ), $post_id, papi_get_meta_type() );\n\t\t}\n\n\t\t// Delete page type switch id.\n\t\tif ( empty( $page_type_switch_id ) ) {\n\t\t\treturn delete_post_meta( $post_id, papi_get_page_type_key() );\n\t\t}\n\n\t\t// Update page type id.\n\t\treturn papi_set_page_type_id( $post_id, $page_type_switch_id );\n\t}\n}\n\nif ( papi_is_admin() ) {\n\tnew Papi_Admin_Page_Type_Switcher;\n}\n"
  },
  {
    "path": "src/admin/class-papi-admin-view.php",
    "content": "<?php\n\n/**\n * Admin class that handle admin view rendering.\n */\nfinal class Papi_Admin_View {\n\n\t/**\n\t * Path to view dir.\n\t *\n\t * @var string\n\t */\n\tprotected $path = '';\n\n\t/**\n\t * The constructor.\n\t *\n\t * @param string $path\n\t */\n\tpublic function __construct( $path = '' ) {\n\t\t$this->path = empty( $path ) ? PAPI_PLUGIN_DIR . '/admin/views/' : $path;\n\t}\n\n\t/**\n\t * Check if file exists.\n\t *\n\t * @param  string $file\n\t *\n\t * @return bool\n\t */\n\tpublic function exists( $file ) {\n\t\treturn file_exists( $this->file( $file ) );\n\t}\n\n\t/**\n\t * Render file.\n\t *\n\t * @param  string $file\n\t *\n\t * @return string\n\t */\n\tpublic function render( $file ) {\n\t\tif ( ! empty( $file ) && $this->exists( $file ) ) {\n\t\t\trequire $this->file( $file );\n\t\t}\n\t}\n\n\t/**\n\t * Get full path to file with php exstention.\n\t *\n\t * @param  string $file\n\t *\n\t * @return string\n\t */\n\tprotected function file( $file ) {\n\t\treturn $this->path . $file . '.php';\n\t}\n}\n"
  },
  {
    "path": "src/admin/class-papi-admin.php",
    "content": "<?php\n\n/**\n * Admin class that handle loading of Papi types,\n * columns and loading of posts.\n */\nfinal class Papi_Admin {\n\n\t/**\n\t * The entry type.\n\t *\n\t * @var Papi_Entry_Type\n\t */\n\tprotected $entry_type;\n\n\t/**\n\t * The post type.\n\t *\n\t * @var string\n\t */\n\tprotected $meta_type;\n\n\t/**\n\t * The construct.\n\t */\n\tpublic function __construct() {\n\t\t$this->load_files();\n\t\t$this->setup_actions();\n\t\t$this->setup_filters();\n\t}\n\n\t/**\n\t * Cloning is forbidden.\n\t *\n\t * @codeCoverageIgnore\n\t */\n\tpublic function __clone() {\n\t\t_doing_it_wrong( __FUNCTION__, esc_html__( 'Cheatin&#8217; huh?', 'papi' ), '3.0' );\n\t}\n\n\t/**\n\t * Unserializing instances of this class is forbidden.\n\t *\n\t * @codeCoverageIgnore\n\t */\n\tpublic function __wakeup() {\n\t\t_doing_it_wrong( __FUNCTION__, esc_html__( 'Cheatin&#8217; huh?', 'papi' ), '3.0' );\n\t}\n\n\t/**\n\t * Preboot all types and setup the current type if any.\n\t */\n\tpublic function admin_init() {\n\t\t$meta_type  = papi_get_meta_type();\n\t\t$meta_type  = ucfirst( $meta_type );\n\t\t$class_name = 'Papi_Admin_Entry_' . $meta_type;\n\n\t\t// A custom class is not required, e.g\n\t\t// options don't have one.\n\t\tif ( class_exists( $class_name ) ) {\n\t\t\t$class = call_user_func( [$class_name, 'instance'] );\n\n\t\t\tif ( ! $class->setup() ) {\n\t\t\t\treturn;\n\t\t\t}\n\t\t}\n\n\t\t// Setup entry type.\n\t\tif ( $entry_type = $this->get_entry_type() ) {\n\t\t\t$entry_type->setup();\n\t\t}\n\t}\n\n\t/**\n\t * Add custom body class when it's a page type.\n\t *\n\t * @param  string $classes\n\t *\n\t * @return string\n\t */\n\tpublic function admin_body_class( $classes ) {\n\t\tif ( $entry_type = $this->get_entry_type() ) {\n\t\t\t$classes .= sprintf( ' papi-body papi-meta-type-%s', papi_get_meta_type() );\n\n\t\t\t// Add custom css classes from entry type.\n\t\t\t$arr = $entry_type->get_body_classes();\n\t\t\t$arr = is_string( $arr ) ? [ $arr ] : $arr;\n\t\t\t$arr = is_array( $arr ) ? $arr : [];\n\n\t\t\t$classes .= ' ' . implode( ' ', $arr );\n\t\t}\n\n\t\t// Add custom css classes from query string.\n\t\tif ( $css = papi_get_qs( 'papi_css' ) ) {\n\t\t\t$css = is_array( $css ) ? $css : [];\n\t\t\t$css = array_map( 'sanitize_text_field', $css );\n\t\t\t$classes .= ' ' . implode( ' ', $css );\n\t\t}\n\n\t\treturn $classes;\n\t}\n\n\t/**\n\t * Output Papi page type hidden field.\n\t */\n\tpublic function edit_form_after_title() {\n\t\twp_nonce_field( 'papi_save_data', 'papi_meta_nonce' );\n\n\t\tif ( $value = esc_attr( papi_get_entry_type_id() ) ) {\n\t\t\tpapi_render_html_tag( 'input', [\n\t\t\t\t'data-papi-page-type-key' => true,\n\t\t\t\t'name'                    => esc_attr( papi_get_page_type_key() ),\n\t\t\t\t'type'                    => 'hidden',\n\t\t\t\t'value'                   => $value\n\t\t\t] );\n\t\t}\n\t}\n\n\t/**\n\t * Get Entry Type instance.\n\t *\n\t * @return Papi_Entry_Type|false\n\t */\n\tprotected function get_entry_type() {\n\t\tif ( $this->entry_type instanceof Papi_Entry_Type ) {\n\t\t\treturn $this->entry_type;\n\t\t}\n\n\t\t$entry_type_id = papi_get_entry_type_id();\n\n\t\t// If the entry type id is empty try to load\n\t\t// the entry type id from `page` query string.\n\t\t//\n\t\t// Example:\n\t\t//   /wp-admin/options-general.php?page=papi/option/site-option-type\n\t\tif ( empty( $entry_type_id ) ) {\n\t\t\t$entry_type_id = preg_replace( '/^papi\\/\\w+\\//', '', papi_get_qs( 'page' ) );\n\t\t}\n\n\t\t// Use the default entry type id if empty.\n\t\tif ( empty( $entry_type_id ) ) {\n\t\t\t$entry_type_id = papi_get_entry_type_id();\n\t\t}\n\n\t\t// If no entry type id exists Papi can't setup a entry type.\n\t\tif ( empty( $entry_type_id ) ) {\n\t\t\treturn false;\n\t\t}\n\n\t\t$entry_type = papi_get_entry_type_by_id( $entry_type_id );\n\n\t\tif ( $entry_type instanceof Papi_Entry_Type === false ) {\n\t\t\treturn false;\n\t\t}\n\n\t\t$this->entry_type = $entry_type;\n\n\t\treturn $entry_type;\n\t}\n\n\t/**\n\t * Load admin files that are not loaded by the autoload.\n\t */\n\tprotected function load_files() {\n\t\trequire_once __DIR__ . '/class-papi-admin-meta-handler.php';\n\t\trequire_once __DIR__ . '/class-papi-admin-option-handler.php';\n\t\trequire_once __DIR__ . '/class-papi-admin-entry-post.php';\n\t\trequire_once __DIR__ . '/class-papi-admin-entry-taxonomy.php';\n\t\trequire_once __DIR__ . '/class-papi-admin-columns.php';\n\t\trequire_once __DIR__ . '/class-papi-admin-page-type-switcher.php';\n\t}\n\n\t/**\n\t * Add docs links to plugin row meta.\n\t *\n\t * @param  array  $links\n\t * @param  string $file\n\t *\n\t * @return array\n\t */\n\tpublic function plugin_row_meta( array $links, $file ) {\n\t\tif ( $file === PAPI_PLUGIN_BASENAME ) {\n\t\t\treturn array_merge( $links, [\n\t\t\t\t'docs' => '<a href=\"' . esc_url( 'https://wp-papi.github.io/docs/' ) . '\" title=\"' . esc_attr( __( 'View Papi Documentation', 'papi' ) ) . '\">' . __( 'Docs', 'papi' ) . '</a>',\n\t\t\t] );\n\t\t}\n\n\t\treturn $links;\n\t}\n\n\t/**\n\t * Setup actions.\n\t */\n\tprotected function setup_actions() {\n\t\tadd_action( 'admin_init', [$this, 'admin_init'] );\n\t\tadd_action( 'edit_form_after_title', [$this, 'edit_form_after_title'] );\n\n\t\tif ( $taxonomy = papi_get_taxonomy() ) {\n\t\t\tadd_action( $taxonomy . '_add_form', [$this, 'edit_form_after_title'] );\n\t\t\tadd_action( $taxonomy . '_edit_form', [$this, 'edit_form_after_title'] );\n\t\t}\n\t}\n\n\t/**\n\t * Setup filters.\n\t */\n\tprotected function setup_filters() {\n\t\tadd_filter( 'admin_body_class', [$this, 'admin_body_class'] );\n\t\tadd_filter( 'plugin_row_meta', [$this, 'plugin_row_meta'], 10, 2 );\n\t\tadd_filter( 'wp_link_query', [$this, 'wp_link_query'] );\n\t\tadd_filter( 'wp_refresh_nonces', [$this, 'wp_refresh_nonces'], 11 );\n\t\tadd_filter( 'pre_update_option', [$this, 'update_front_page'], 10, 2 );\n\t}\n\n\t/**\n\t * Update page type to front page type when updating\n\t * which page that should be used as front page.\n\t *\n\t * @param  mixed  $value\n\t * @param  string $option\n\t *\n\t * @return mixed\n\t */\n\tpublic function update_front_page( $value, $option ) {\n\t\tif ( $option !== 'page_on_front' ) {\n\t\t\treturn $value;\n\t\t}\n\n\t\t$front_pages = papi_get_all_entry_types( [\n\t\t\t'types' => ['front-page'],\n\t\t] );\n\n\t\tif ( count( $front_pages ) === 0 ) {\n\t\t\treturn $value;\n\t\t}\n\n\t\t$old_page_type_id = papi_get_page_type_id( $value );\n\t\t$new_page_type_id = $front_pages[0]->get_id();\n\n\t\t// Delete all old fields from old page type if any.\n\t\tif ( strtolower( $old_page_type_id ) !== strtolower( $new_page_type_id ) ) {\n\t\t\tif ( $page_type = papi_get_entry_type_by_id( $old_page_type_id ) ) {\n\t\t\t\t$slugs = array_map( function( $property ) {\n\t\t\t\t\treturn $property->get_slug( true );\n\t\t\t\t}, $page_type->get_properties() );\n\n\t\t\t\tforeach ( $slugs as $slug ) {\n\t\t\t\t\tpapi_delete_field( $value, $slug );\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tpapi_set_page_type_id( $value, $new_page_type_id );\n\n\t\treturn $value;\n\t}\n\n\t/**\n\t * Filter the link query results.\n\t *\n\t * @param  array $results\n\t *\n\t * @return array\n\t */\n\tpublic function wp_link_query( array $results ) {\n\t\tforeach ( $results as $index => $value ) {\n\t\t\t$post_type = papi_get_post_type( $value['ID'] );\n\t\t\t$name      = papi_get_page_type_name( $value['ID'] );\n\n\t\t\tif ( empty( $name ) ) {\n\t\t\t\t$name = papi_filter_settings_standard_page_type_name( $post_type );\n\t\t\t}\n\n\t\t\t$results[$index]['info'] = esc_html( $name );\n\t\t}\n\n\t\treturn $results;\n\t}\n\n\t/**\n\t * Check nonce expiration on the New/Edit Post screen and refresh if needed.\n\t *\n\t * @param  array $response\n\t *\n\t * @return array\n\t */\n\tpublic function wp_refresh_nonces( array $response ) {\n\t\tif ( ! array_key_exists( 'wp-refresh-post-nonces', $response ) ) {\n\t\t\treturn $response;\n\t\t}\n\n\t\t$response['wp-refresh-post-nonces']['replace']['papi_meta_nonce'] = wp_create_nonce( 'papi_save_data' );\n\n\t\treturn $response;\n\t}\n}\n\nif ( papi_is_admin() ) {\n\tnew Papi_Admin;\n}\n"
  },
  {
    "path": "src/admin/views/add-new-page.php",
    "content": "<?php\n$post_type_name = papi_get_post_type();\n$post_type      = get_post_type_object( $post_type_name );\n$post_type      = empty( $post_type ) ? get_post_type_object( 'page' ) : $post_type;\n?>\n<div class=\"wrap\">\n\t<h1>\n\t\t<?php echo esc_html( sprintf( __( 'Add New %s', 'papi' ), $post_type->labels->singular_name ) ); ?>\n\n\t\t<label class=\"screen-reader-text\" for=\"add-new-page-search\">\n\t\t\t<?php echo esc_html( $post_type->labels->search_items ); ?>\n\t\t</label>\n\n\t\t<input placeholder=\"<?php echo esc_html( $post_type->labels->search_items ); ?>...\" type=\"search\" name=\"add-new-page-search\" id=\"add-new-page-search\" class=\"papi-search\">\n\t</h1>\n\n\t<div class=\"papi-box-list\">\n\t\t<?php\n\t\t$parent_page_type = papi_get_entry_type_by_id( papi_get_entry_type_id() );\n\t\t$page_types       = papi_get_all_page_types( $post_type_name );\n\t\t$show_standard    = papi_filter_settings_show_standard_page_type( $post_type_name );\n\n\t\tif ( $parent_page_type instanceof Papi_Page_Type ) {\n\t\t\t$child_types = $parent_page_type->get_child_types();\n\t\t\t$page_types  = empty( $child_types ) ? $page_types : $child_types;\n\n\t\t\tif ( ! $show_standard ) {\n\t\t\t\t$show_standard = $parent_page_type->standard_type;\n\t\t\t}\n\t\t}\n\n\t\tif ( $show_standard ) {\n\t\t\t$page_types[] = papi_get_standard_page_type( $post_type_name );\n\t\t}\n\n\t\tusort( $page_types, function ( $a, $b ) {\n\t\t\treturn strcmp( $a->name, $b->name );\n\t\t} );\n\n\t\t$page_types    = papi_sort_order( array_reverse( $page_types ) );\n\t\t$use_thumbnail = false;\n\n\t\tforeach ( $page_types as $key => $page_type ) {\n\t\t\tif ( ! empty( $page_type->get_thumbnail() ) ) {\n\t\t\t\t$use_thumbnail = true;\n\t\t\t}\n\t\t}\n\n\t\tforeach ( $page_types as $key => $page_type ) {\n\t\t\tif ( ! papi_display_page_type( $page_type ) ) {\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tpapi_include_template( 'admin/views/partials/add-new-item.php', [\n\t\t\t\t'title'         => $page_type->name,\n\t\t\t\t'description'   => $page_type->description,\n\t\t\t\t'thumbnail'     => $page_type->get_thumbnail(),\n\t\t\t\t'url'           => papi_get_page_new_url( $page_type->get_id(), true, null ),\n\t\t\t\t'use_thumbnail' => $use_thumbnail\n\t\t\t] );\n\t\t}\n\t\t?>\n\t</div>\n</div>\n"
  },
  {
    "path": "src/admin/views/partials/add-new-item.php",
    "content": "<a href=\"<?php echo esc_attr( $vars['url'] ); ?>\" class=\"papi-box-item\">\n\t<?php if ( $vars['use_thumbnail'] ): ?>\n\t<div class=\"papi-page-type hide\">\n\t\t<?php if ( ! empty( $vars['thumbnail'] ) ): ?>\n\t\t\t<div class=\"papi-page-type-screenshot\" style=\"background-image:url(<?php echo esc_html( $vars['thumbnail'] ); ?>)\"></div>\n\t\t<?php else: ?>\n\t\t\t<div class=\"papi-page-type-screenshot\">\n\t\t\t\t<div>\n\t\t\t\t\t<div><?php esc_html_e( 'Thumbnail missing', 'papi' ); ?></div>\n\t\t\t\t</div>\n\t\t\t</div>\n\t\t<?php endif; ?>\n\t</div>\n\t<?php endif; ?>\n\n\t<div class=\"papi-page-type-info\">\n\t\t<h3><?php echo esc_html( $vars['title'] ); ?></h3>\n\t\t<p><?php echo esc_html( $vars['description'] ); ?></p>\n\t</div>\n\n\t<div class=\"papi-page-type-actions\">\n\t\t<span class=\"button button-primary\"><?php esc_html_e( 'Select', 'papi' ); ?></span>\n\t</div>\n</a>\n"
  },
  {
    "path": "src/assets/js/components/pikaday.js",
    "content": "import moment from 'moment';\n\n/*!\n * Pikaday\n *\n * Copyright © 2014 David Bushell | BSD & MIT license | https://github.com/dbushell/Pikaday\n */\n\n/**\n * feature detection and helper functions\n */\nvar hasMoment = typeof moment === 'function',\n  hasEventListeners = !!window.addEventListener,\n  document = window.document,\n  sto = window.setTimeout,\n  addEvent = function(el, e, callback, capture) {\n    if (hasEventListeners) {\n      el.addEventListener(e, callback, !!capture);\n    } else {\n      el.attachEvent('on' + e, callback);\n    }\n  },\n  removeEvent = function(el, e, callback, capture) {\n    if (hasEventListeners) {\n      el.removeEventListener(e, callback, !!capture);\n    } else {\n      el.detachEvent('on' + e, callback);\n    }\n  },\n  fireEvent = function(el, eventName, data) {\n    var ev;\n\n    if (document.createEvent) {\n      ev = document.createEvent('HTMLEvents');\n      ev.initEvent(eventName, true, false);\n      ev = extend(ev, data);\n      el.dispatchEvent(ev);\n    } else if (document.createEventObject) {\n      ev = document.createEventObject();\n      ev = extend(ev, data);\n      el.fireEvent('on' + eventName, ev);\n    }\n  },\n  trim = function(str) {\n    return str.trim ? str.trim() : str.replace(/^\\s+|\\s+$/g, '');\n  },\n  hasClass = function(el, cn) {\n    return (' ' + el.className + ' ').indexOf(' ' + cn + ' ') !== -1;\n  },\n  addClass = function(el, cn) {\n    if (!hasClass(el, cn)) {\n      el.className = el.className === '' ? cn : el.className + ' ' + cn;\n    }\n  },\n  removeClass = function(el, cn) {\n    el.className = trim(\n      (' ' + el.className + ' ').replace(' ' + cn + ' ', ' ')\n    );\n  },\n  isArray = function(obj) {\n    return /Array/.test(Object.prototype.toString.call(obj));\n  },\n  isDate = function(obj) {\n    return (\n      /Date/.test(Object.prototype.toString.call(obj)) && !isNaN(obj.getTime())\n    );\n  },\n  isWeekend = function(date) {\n    var day = date.getDay();\n    return day === 0 || day === 6;\n  },\n  isLeapYear = function(year) {\n    // solution by Matti Virkkunen: http://stackoverflow.com/a/4881951\n    return (year % 4 === 0 && year % 100 !== 0) || year % 400 === 0;\n  },\n  getDaysInMonth = function(year, month) {\n    return [\n      31,\n      isLeapYear(year) ? 29 : 28,\n      31,\n      30,\n      31,\n      30,\n      31,\n      31,\n      30,\n      31,\n      30,\n      31\n    ][month];\n  },\n  setToStartOfDay = function(date) {\n    if (isDate(date)) date.setHours(0, 0, 0, 0);\n  },\n  compareDates = function(a, b) {\n    // Copy so we don't change the dates being passed in\n    var _a = new Date(a.getTime());\n    var _b = new Date(b.getTime());\n    setToStartOfDay(_a);\n    setToStartOfDay(_b);\n    return _a.getTime() === _b.getTime();\n  },\n  extend = function(to, from, overwrite) {\n    var prop, hasProp;\n    for (prop in from) {\n      hasProp = to[prop] !== undefined;\n      if (\n        hasProp &&\n        typeof from[prop] === 'object' &&\n        from[prop] !== null &&\n        from[prop].nodeName === undefined\n      ) {\n        if (isDate(from[prop])) {\n          if (overwrite) {\n            to[prop] = new Date(from[prop].getTime());\n          }\n        } else if (isArray(from[prop])) {\n          if (overwrite) {\n            to[prop] = from[prop].slice(0);\n          }\n        } else {\n          to[prop] = extend({}, from[prop], overwrite);\n        }\n      } else if (overwrite || !hasProp) {\n        to[prop] = from[prop];\n      }\n    }\n    return to;\n  },\n  adjustCalendar = function(calendar) {\n    if (calendar.month < 0) {\n      calendar.year -= Math.ceil(Math.abs(calendar.month) / 12);\n      calendar.month += 12;\n    }\n    if (calendar.month > 11) {\n      calendar.year += Math.floor(Math.abs(calendar.month) / 12);\n      calendar.month -= 12;\n    }\n    return calendar;\n  },\n  /**\n   * defaults and localisation\n   */\n  defaults = {\n    // bind the picker to a form field\n    field: null,\n\n    // automatically show/hide the picker on `field` focus (default `true` if `field` is set)\n    bound: undefined,\n\n    // position of the datepicker, relative to the field (default to bottom & left)\n    // ('bottom' & 'left' keywords are not used, 'top' & 'right' are modifier on the bottom/left position)\n    position: 'bottom left',\n\n    // automatically fit in the viewport even if it means repositioning from the position option\n    reposition: true,\n\n    // the default output format for `.toString()` and `field` value\n    // set in `config` based on if showTime is set\n    format: null,\n\n    // an array giving the allowable input format(s).  As with moment,\n    // the input formats may be either a single string or an array of strings.\n    // Usually set in `config`\n    inputFormats: null,\n\n    // the initial date to view when first opened\n    defaultDate: null,\n\n    // make the `defaultDate` the initial selected value\n    setDefaultDate: false,\n\n    // first day of week (0: Sunday, 1: Monday etc)\n    firstDay: 0,\n\n    // the minimum/earliest date that can be selected\n    minDate: null,\n    // the maximum/latest date that can be selected\n    maxDate: null,\n\n    // number of years either side, or array of upper/lower range\n    yearRange: 10,\n\n    // show week numbers at head of row\n    showWeekNumber: false,\n\n    // used internally (don't config outside)\n    minYear: 0,\n    maxYear: 9999,\n    minMonth: undefined,\n    maxMonth: undefined,\n\n    isRTL: false,\n\n    // Additional text to append to the year in the calendar title\n    yearSuffix: '',\n\n    // Render the month after year in the calendar title\n    showMonthAfterYear: false,\n\n    // how many months are visible\n    numberOfMonths: 1,\n\n    // time\n    showTime: true,\n    showTimeFirst: false,\n    showSeconds: false,\n    use24hour: false,\n\n    // when numberOfMonths is used, this will help you to choose where the main calendar will be (default `left`, can be set to `right`)\n    // only used for the first display or when a selected date is not visible\n    mainCalendar: 'left',\n\n    // Specify a DOM element to render the calendar in\n    container: undefined,\n\n    // internationalization\n    i18n: {\n      previousMonth: 'Previous Month',\n      nextMonth: 'Next Month',\n      months: [\n        'January',\n        'February',\n        'March',\n        'April',\n        'May',\n        'June',\n        'July',\n        'August',\n        'September',\n        'October',\n        'November',\n        'December'\n      ],\n      weekdays: [\n        'Sunday',\n        'Monday',\n        'Tuesday',\n        'Wednesday',\n        'Thursday',\n        'Friday',\n        'Saturday'\n      ],\n      weekdaysShort: ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'],\n      midnight: 'Midnight',\n      noon: 'Noon'\n    },\n\n    // Theme Classname\n    theme: null,\n\n    // callback function\n    onSelect: null,\n    onOpen: null,\n    onClose: null,\n    onDraw: null\n  },\n  /**\n   * templating functions to abstract HTML rendering\n   */\n  renderDayName = function(opts, day, abbr) {\n    day += opts.firstDay;\n    while (day >= 7) {\n      day -= 7;\n    }\n    return abbr ? opts.i18n.weekdaysShort[day] : opts.i18n.weekdays[day];\n  },\n  renderDay = function(d, m, y, isSelected, isToday, isDisabled, isEmpty) {\n    if (isEmpty) {\n      return '<td class=\"is-empty\"></td>';\n    }\n    var arr = [];\n    if (isDisabled) {\n      arr.push('is-disabled');\n    }\n    if (isToday) {\n      arr.push('is-today');\n    }\n    if (isSelected) {\n      arr.push('is-selected');\n    }\n    return (\n      '<td data-day=\"' +\n      d +\n      '\" class=\"' +\n      arr.join(' ') +\n      '\">' +\n      '<button class=\"pika-button pika-day\" type=\"button\" ' +\n      'data-pika-year=\"' +\n      y +\n      '\" data-pika-month=\"' +\n      m +\n      '\" data-pika-day=\"' +\n      d +\n      '\">' +\n      d +\n      '</button>' +\n      '</td>'\n    );\n  },\n  renderWeek = function(d, m, y) {\n    // Lifted from http://javascript.about.com/library/blweekyear.htm, lightly modified.\n    var onejan = new Date(y, 0, 1),\n      weekNum = Math.ceil(\n        ((new Date(y, m, d) - onejan) / 86400000 + onejan.getDay() + 1) / 7\n      );\n    return '<td class=\"pika-week\">' + weekNum + '</td>';\n  },\n  renderRow = function(days, isRTL) {\n    return '<tr>' + (isRTL ? days.reverse() : days).join('') + '</tr>';\n  },\n  renderBody = function(rows) {\n    return '<tbody>' + rows.join('') + '</tbody>';\n  },\n  renderHead = function(opts) {\n    var i,\n      arr = [];\n    if (opts.showWeekNumber) {\n      arr.push('<th></th>');\n    }\n    for (i = 0; i < 7; i++) {\n      arr.push(\n        '<th scope=\"col\"><abbr title=\"' +\n          renderDayName(opts, i) +\n          '\">' +\n          renderDayName(opts, i, true) +\n          '</abbr></th>'\n      );\n    }\n    return '<thead>' + (opts.isRTL ? arr.reverse() : arr).join('') + '</thead>';\n  },\n  renderTitle = function(instance, c, year, month, refYear) {\n    var i,\n      j,\n      arr,\n      opts = instance._o,\n      isMinYear = year === opts.minYear,\n      isMaxYear = year === opts.maxYear,\n      html = '<div class=\"pika-title\">',\n      monthHtml,\n      yearHtml,\n      prev = true,\n      next = true;\n\n    for (arr = [], i = 0; i < 12; i++) {\n      arr.push(\n        '<option value=\"' +\n          (year === refYear ? i - c : 12 + i - c) +\n          '\"' +\n          (i === month ? ' selected' : '') +\n          ((isMinYear && i < opts.minMonth) || (isMaxYear && i > opts.maxMonth)\n            ? 'disabled'\n            : '') +\n          '>' +\n          opts.i18n.months[i] +\n          '</option>'\n      );\n    }\n    monthHtml =\n      '<div class=\"pika-label\">' +\n      opts.i18n.months[month] +\n      '<select class=\"pika-select pika-select-month\">' +\n      arr.join('') +\n      '</select></div>';\n\n    if (isArray(opts.yearRange)) {\n      i = opts.yearRange[0];\n      j = opts.yearRange[1] + 1;\n    } else {\n      i = year - opts.yearRange;\n      j = 1 + year + opts.yearRange;\n    }\n\n    for (arr = []; i < j && i <= opts.maxYear; i++) {\n      if (i >= opts.minYear) {\n        arr.push(\n          '<option value=\"' +\n            i +\n            '\"' +\n            (i === year ? ' selected' : '') +\n            '>' +\n            i +\n            '</option>'\n        );\n      }\n    }\n    yearHtml =\n      '<div class=\"pika-label\">' +\n      year +\n      opts.yearSuffix +\n      '<select class=\"pika-select pika-select-year\">' +\n      arr.join('') +\n      '</select></div>';\n\n    if (opts.showMonthAfterYear) {\n      html += yearHtml + monthHtml;\n    } else {\n      html += monthHtml + yearHtml;\n    }\n\n    if (isMinYear && (month === 0 || opts.minMonth >= month)) {\n      prev = false;\n    }\n\n    if (isMaxYear && (month === 11 || opts.maxMonth <= month)) {\n      next = false;\n    }\n\n    if (c === 0) {\n      html +=\n        '<button class=\"pika-prev' +\n        (prev ? '' : ' is-disabled') +\n        '\" type=\"button\">' +\n        opts.i18n.previousMonth +\n        '</button>';\n    }\n    if (c === instance._o.numberOfMonths - 1) {\n      html +=\n        '<button class=\"pika-next' +\n        (next ? '' : ' is-disabled') +\n        '\" type=\"button\">' +\n        opts.i18n.nextMonth +\n        '</button>';\n    }\n\n    return (html += '</div>');\n  },\n  renderTable = function(opts, data) {\n    return (\n      '<table cellpadding=\"0\" cellspacing=\"0\" class=\"pika-table\">' +\n      renderHead(opts) +\n      renderBody(data) +\n      '</table>'\n    );\n  },\n  renderTimePicker = function(\n    num_options,\n    selected_val,\n    select_class,\n    display_func\n  ) {\n    var to_return = '<td><select class=\"pika-select ' + select_class + '\">';\n    for (var i = 0; i < num_options; i++) {\n      to_return +=\n        '<option value=\"' +\n        i +\n        '\" ' +\n        (i == selected_val ? 'selected' : '') +\n        '>' +\n        display_func(i) +\n        '</option>';\n    }\n    to_return += '</select></td>';\n    return to_return;\n  },\n  renderTime = function(hh, mm, ss, opts) {\n    var to_return =\n      '<table cellpadding=\"0\" cellspacing=\"0\" class=\"pika-time\"><tbody><tr>' +\n      renderTimePicker(24, hh, 'pika-select-hour', function(i) {\n        if (opts.use24hour) {\n          return i;\n        } else {\n          var to_return = (i % 12) + (i < 12 ? ' AM' : ' PM');\n          if (to_return == '0 AM') {\n            return opts.i18n.midnight;\n          } else if (to_return == '0 PM') {\n            return opts.i18n.noon;\n          } else {\n            return to_return;\n          }\n        }\n      }) +\n      '<td>:</td>' +\n      renderTimePicker(60, mm, 'pika-select-minute', function(i) {\n        if (i < 10) return '0' + i;\n        return i;\n      });\n\n    if (opts.showSeconds) {\n      to_return +=\n        '<td>:</td>' +\n        renderTimePicker(60, ss, 'pika-select-second', function(i) {\n          if (i < 10) return '0' + i;\n          return i;\n        });\n    }\n    return to_return + '</tr></tbody></table>';\n  },\n  /**\n   * Pikaday constructor\n   */\n  Pikaday = function(options) {\n    var self = this,\n      opts = self.config(options);\n\n    self._onMouseDown = function(e) {\n      if (!self._v) {\n        return;\n      }\n      e = e || window.event;\n      var target = e.target || e.srcElement;\n      if (!target) {\n        return;\n      }\n\n      // Do not preventDefaul when using time picker\n      if (\n        !hasClass(target, 'pika-select-hour') &&\n        !hasClass(target, 'pika-select-minute') &&\n        !hasClass(target, 'pika-select-second')\n      ) {\n        e.preventDefault();\n      }\n\n      if (!hasClass(target, 'is-disabled')) {\n        if (hasClass(target, 'pika-button') && !hasClass(target, 'is-empty')) {\n          var newDate = new Date(\n            target.getAttribute('data-pika-year'),\n            target.getAttribute('data-pika-month'),\n            target.getAttribute('data-pika-day')\n          );\n          // Preserve time selection when date changed\n          if (self._d && opts.showTime) {\n            newDate.setHours(self._d.getHours());\n            newDate.setMinutes(self._d.getMinutes());\n          }\n          self.setDate(newDate);\n          if (opts.bound) {\n            sto(function() {\n              self.hide();\n              if (opts.field) {\n                opts.field.blur();\n              }\n            }, 100);\n          }\n          return;\n        } else if (hasClass(target, 'pika-prev')) {\n          self.prevMonth();\n        } else if (hasClass(target, 'pika-next')) {\n          self.nextMonth();\n        }\n      }\n      if (!hasClass(target, 'pika-select')) {\n        if (e.preventDefault) {\n          e.preventDefault();\n        } else {\n          e.returnValue = false;\n          return false;\n        }\n      } else {\n        self._c = true;\n      }\n    };\n\n    self._onChange = function(e) {\n      e = e || window.event;\n      var target = e.target || e.srcElement;\n      if (!target) {\n        return;\n      }\n      if (hasClass(target, 'pika-select-month')) {\n        self.gotoMonth(target.value);\n      } else if (hasClass(target, 'pika-select-year')) {\n        self.gotoYear(target.value);\n      } else if (hasClass(target, 'pika-select-hour')) {\n        self.setTime(target.value);\n      } else if (hasClass(target, 'pika-select-minute')) {\n        self.setTime(null, target.value);\n      } else if (hasClass(target, 'pika-select-second')) {\n        self.setTime(null, null, target.value);\n      }\n    };\n\n    self._onInputChange = function(e) {\n      var date;\n\n      if (e.firedBy === self) {\n        return;\n      }\n      if (hasMoment) {\n        date = moment(opts.field.value, opts.inputFormats);\n        date = date && date.isValid() ? date.toDate() : null;\n      } else {\n        date = new Date(Date.parse(opts.field.value));\n      }\n      self.setDate(isDate(date) ? date : null);\n      if (!self._v) {\n        self.show();\n      }\n    };\n\n    self._onInputFocus = function() {\n      self.show();\n    };\n\n    self._onInputClick = function() {\n      self.show();\n    };\n\n    self._onInputBlur = function() {\n      // IE allows pika div to gain focus; catch blur the input field\n      var pEl = document.activeElement;\n      do {\n        if (hasClass(pEl, 'pika-single')) {\n          return;\n        }\n      } while ((pEl = pEl.parentNode));\n\n      if (!self._c) {\n        self._b = sto(function() {\n          self.hide();\n        }, 50);\n      }\n      self._c = false;\n    };\n\n    self._onClick = function(e) {\n      e = e || window.event;\n      var target = e.target || e.srcElement,\n        pEl = target;\n      if (!target) {\n        return;\n      }\n      if (!hasEventListeners && hasClass(target, 'pika-select')) {\n        if (!target.onchange) {\n          target.setAttribute('onchange', 'return;');\n          addEvent(target, 'change', self._onChange);\n        }\n      }\n      do {\n        if (\n          hasClass(pEl, 'pika-single') ||\n          pEl === opts.trigger ||\n          (opts.showTime && hasClass(pEl, 'pika-time-container'))\n        ) {\n          return;\n        }\n      } while ((pEl = pEl.parentNode));\n      if (self._v && target !== opts.trigger && pEl !== opts.trigger) {\n        self.hide();\n      }\n    };\n\n    self.el = document.createElement('div');\n    self.el.className =\n      'pika-single' +\n      (opts.isRTL ? ' is-rtl' : '') +\n      (opts.theme ? ' ' + opts.theme : '');\n\n    addEvent(\n      self.el,\n      'ontouchend' in document ? 'ontouchend' : 'mousedown',\n      self._onMouseDown,\n      true\n    );\n    addEvent(self.el, 'change', self._onChange);\n\n    if (opts.field) {\n      if (opts.container) {\n        opts.container.appendChild(self.el);\n      } else if (opts.bound) {\n        document.body.appendChild(self.el);\n      } else {\n        opts.field.parentNode.insertBefore(self.el, opts.field.nextSibling);\n      }\n      addEvent(opts.field, 'change', self._onInputChange);\n\n      if (!opts.defaultDate) {\n        if (hasMoment && opts.field.value) {\n          opts.defaultDate = moment(\n            opts.field.value,\n            opts.inputFormats\n          ).toDate();\n        } else {\n          opts.defaultDate = new Date(Date.parse(opts.field.value));\n        }\n        opts.setDefaultDate = true;\n      }\n    }\n\n    var defDate = opts.defaultDate;\n\n    if (isDate(defDate)) {\n      if (opts.setDefaultDate) {\n        self.setDate(defDate, true);\n      } else {\n        self.gotoDate(defDate);\n      }\n    } else {\n      self.gotoDate(new Date());\n    }\n\n    if (opts.bound) {\n      this.hide();\n      self.el.className += ' is-bound';\n      addEvent(opts.trigger, 'click', self._onInputClick);\n      addEvent(opts.trigger, 'focus', self._onInputFocus);\n      addEvent(opts.trigger, 'blur', self._onInputBlur);\n    } else {\n      this.show();\n    }\n  };\n\n/**\n * public Pikaday API\n */\nPikaday.prototype = {\n  /**\n   * configure functionality\n   */\n  config: function(options) {\n    if (!this._o) {\n      this._o = extend({}, defaults, true);\n    }\n\n    var opts = extend(this._o, options, true);\n\n    opts.isRTL = !!opts.isRTL;\n\n    opts.field = opts.field && opts.field.nodeName ? opts.field : null;\n\n    opts.theme =\n      typeof opts.theme == 'string' && opts.theme ? opts.theme : null;\n\n    opts.bound = !!(opts.bound !== undefined\n      ? opts.field && opts.bound\n      : opts.field);\n\n    opts.trigger =\n      opts.trigger && opts.trigger.nodeName ? opts.trigger : opts.field;\n\n    opts.disableWeekends = !!opts.disableWeekends;\n\n    opts.disableDayFn =\n      typeof opts.disableDayFn == 'function' ? opts.disableDayFn : null;\n\n    var nom = parseInt(opts.numberOfMonths, 10) || 1;\n    opts.numberOfMonths = nom > 4 ? 4 : nom;\n\n    if (!isDate(opts.minDate)) {\n      opts.minDate = false;\n    }\n    if (!isDate(opts.maxDate)) {\n      opts.maxDate = false;\n    }\n    if (opts.minDate && opts.maxDate && opts.maxDate < opts.minDate) {\n      opts.maxDate = opts.minDate = false;\n    }\n    if (opts.minDate) {\n      this.setMinDate(opts.minDate);\n    }\n    if (opts.maxDate) {\n      if (!opts.showTime) setToStartOfDay(opts.maxDate);\n      opts.maxYear = opts.maxDate.getFullYear();\n      opts.maxMonth = opts.maxDate.getMonth();\n    }\n\n    if (isArray(opts.yearRange)) {\n      var fallback = new Date().getFullYear() - 10;\n      opts.yearRange[0] = parseInt(opts.yearRange[0], 10) || fallback;\n      opts.yearRange[1] = parseInt(opts.yearRange[1], 10) || fallback;\n    } else {\n      opts.yearRange =\n        Math.abs(parseInt(opts.yearRange, 10)) || defaults.yearRange;\n      if (opts.yearRange > 100) {\n        opts.yearRange = 100;\n      }\n    }\n\n    // If no format is given, set based on showTime\n    if (opts.format === null) {\n      opts.format = 'YYYY-MM-DD';\n      if (opts.showTime) {\n        opts.format += ' HH:mm:ss';\n      }\n    }\n\n    if (!opts.inputFormats) {\n      opts.inputFormats = opts.format;\n    }\n\n    return opts;\n  },\n\n  /**\n   * return a formatted string of the current selection (using Moment.js if available)\n   */\n  toString: function(format) {\n    return !isDate(this._d)\n      ? ''\n      : hasMoment\n      ? moment(this._d).format(format || this._o.format)\n      : this._o.showTime\n      ? this._d.toString()\n      : this._d.toDateString();\n  },\n\n  /**\n   * return a Moment.js object of the current selection (if available)\n   */\n  getMoment: function() {\n    return hasMoment ? moment(this._d) : null;\n  },\n\n  /**\n   * set the current selection from a Moment.js object (if available)\n   */\n  setMoment: function(date, preventOnSelect) {\n    if (hasMoment && moment.isMoment(date)) {\n      this.setDate(date.toDate(), preventOnSelect);\n    }\n  },\n\n  /**\n   * return a Date object of the current selection\n   */\n  getDate: function() {\n    return isDate(this._d) ? new Date(this._d.getTime()) : null;\n  },\n\n  /**\n   * set time components\n   * Currently defaulting to setting date to today if not set\n   */\n  setTime: function(hours, minutes, seconds) {\n    if (!this._d) {\n      this._d = new Date();\n      this._d.setHours(0, 0, 0, 0);\n    }\n    if (hours) {\n      this._d.setHours(hours);\n    }\n    if (minutes) {\n      this._d.setMinutes(minutes);\n    }\n    if (seconds) {\n      this._d.setSeconds(seconds);\n    }\n    this.setDate(this._d);\n  },\n\n  /**\n   * set the current selection\n   */\n  setDate: function(date, preventOnSelect) {\n    if (!date) {\n      this._d = null;\n\n      if (this._o.field) {\n        this._o.field.value = '';\n        fireEvent(this._o.field, 'change', { firedBy: this });\n      }\n\n      return this.draw();\n    }\n    if (typeof date === 'string') {\n      date = new Date(Date.parse(date));\n    }\n    if (!isDate(date)) {\n      return;\n    }\n\n    var min = this._o.minDate,\n      max = this._o.maxDate;\n\n    if (isDate(min) && date < min) {\n      date = min;\n    } else if (isDate(max) && date > max) {\n      date = max;\n    }\n\n    this._d = new Date(date.getTime());\n\n    if (this._o.showTime && !this._o.showSeconds) {\n      this._d.setSeconds(0);\n    } else if (!this._o.showTime) {\n      setToStartOfDay(this._d);\n    }\n\n    this.gotoDate(this._d);\n\n    if (this._o.field) {\n      this._o.field.value = this.toString();\n      fireEvent(this._o.field, 'change', { firedBy: this });\n    }\n    if (!preventOnSelect && typeof this._o.onSelect === 'function') {\n      this._o.onSelect.call(this, this.getDate());\n    }\n  },\n\n  /**\n   * change view to a specific date\n   */\n  gotoDate: function(date) {\n    var newCalendar = true;\n\n    if (!isDate(date)) {\n      return;\n    }\n\n    if (this.calendars) {\n      var firstVisibleDate = new Date(\n          this.calendars[0].year,\n          this.calendars[0].month,\n          1\n        ),\n        lastVisibleDate = new Date(\n          this.calendars[this.calendars.length - 1].year,\n          this.calendars[this.calendars.length - 1].month,\n          1\n        ),\n        visibleDate = date.getTime();\n      // get the end of the month\n      lastVisibleDate.setMonth(lastVisibleDate.getMonth() + 1);\n      lastVisibleDate.setDate(lastVisibleDate.getDate() - 1);\n      newCalendar =\n        visibleDate < firstVisibleDate.getTime() ||\n        lastVisibleDate.getTime() < visibleDate;\n    }\n\n    if (newCalendar) {\n      this.calendars = [\n        {\n          month: date.getMonth(),\n          year: date.getFullYear(),\n          hour: date.getHours(),\n          minute: date.getMinutes(),\n          second: date.getSeconds()\n        }\n      ];\n      if (this._o.mainCalendar === 'right') {\n        this.calendars[0].month += 1 - this._o.numberOfMonths;\n      }\n    }\n\n    this.adjustCalendars();\n  },\n\n  adjustCalendars: function() {\n    this.calendars[0] = adjustCalendar(this.calendars[0]);\n    for (var c = 1; c < this._o.numberOfMonths; c++) {\n      this.calendars[c] = adjustCalendar({\n        month: this.calendars[0].month + c,\n        year: this.calendars[0].year\n      });\n    }\n    this.draw();\n  },\n\n  gotoToday: function() {\n    this.gotoDate(new Date());\n  },\n\n  /**\n   * change view to a specific month (zero-index, e.g. 0: January)\n   */\n  gotoMonth: function(month) {\n    if (!isNaN(month)) {\n      this.calendars[0].month = parseInt(month, 10);\n      this.adjustCalendars();\n    }\n  },\n\n  nextMonth: function() {\n    this.calendars[0].month++;\n    this.adjustCalendars();\n  },\n\n  prevMonth: function() {\n    this.calendars[0].month--;\n    this.adjustCalendars();\n  },\n\n  /**\n   * change view to a specific full year (e.g. \"2012\")\n   */\n  gotoYear: function(year) {\n    if (!isNaN(year)) {\n      this.calendars[0].year = parseInt(year, 10);\n      this.adjustCalendars();\n    }\n  },\n\n  /**\n   * change the minDate\n   */\n  setMinDate: function(value) {\n    if (!this._o.showTime) setToStartOfDay(this._o.minDate);\n    setToStartOfDay(value);\n    this._o.minDate = value;\n    this._o.minYear = value.getFullYear();\n    this._o.minMonth = value.getMonth();\n  },\n\n  /**\n   * change the maxDate\n   */\n  setMaxDate: function(value) {\n    this._o.maxDate = value;\n  },\n\n  /**\n   * refresh the HTML\n   */\n  draw: function(force) {\n    if (!this._v && !force) {\n      return;\n    }\n    var opts = this._o,\n      minYear = opts.minYear,\n      maxYear = opts.maxYear,\n      minMonth = opts.minMonth,\n      maxMonth = opts.maxMonth,\n      html = '';\n\n    if (this._y <= minYear) {\n      this._y = minYear;\n      if (!isNaN(minMonth) && this._m < minMonth) {\n        this._m = minMonth;\n      }\n    }\n    if (this._y >= maxYear) {\n      this._y = maxYear;\n      if (!isNaN(maxMonth) && this._m > maxMonth) {\n        this._m = maxMonth;\n      }\n    }\n\n    var timeHtml = '';\n\n    if (opts.showTime) {\n      timeHtml =\n        '<div class=\"pika-time-container\">' +\n        renderTime(\n          this._d ? this._d.getHours() : 0,\n          this._d ? this._d.getMinutes() : 0,\n          this._d ? this._d.getSeconds() : 0,\n          opts\n        ) +\n        '</div>';\n    }\n\n    for (var c = 0; c < opts.numberOfMonths; c++) {\n      html +=\n        '<div class=\"pika-lendar\">' +\n        renderTitle(\n          this,\n          c,\n          this.calendars[c].year,\n          this.calendars[c].month,\n          this.calendars[0].year\n        ) +\n        (opts.showTime && opts.showTimeFirst ? timeHtml : '') +\n        this.render(this.calendars[c].year, this.calendars[c].month) +\n        '</div>';\n    }\n\n    if (opts.showTime && !opts.showTimeFirst) {\n      html += timeHtml;\n    }\n\n    this.el.innerHTML = html;\n\n    if (opts.bound) {\n      if (opts.field.type !== 'hidden') {\n        sto(function() {\n          opts.trigger.focus();\n        }, 1);\n      }\n    }\n\n    if (typeof this._o.onDraw === 'function') {\n      var self = this;\n      sto(function() {\n        self._o.onDraw.call(self);\n      }, 0);\n    }\n  },\n\n  adjustPosition: function() {\n    if (this._o.container) return;\n    var field = this._o.trigger,\n      pEl = field,\n      width = this.el.offsetWidth,\n      height = this.el.offsetHeight,\n      viewportWidth = window.innerWidth || document.documentElement.clientWidth,\n      viewportHeight =\n        window.innerHeight || document.documentElement.clientHeight,\n      scrollTop =\n        window.pageYOffset ||\n        document.body.scrollTop ||\n        document.documentElement.scrollTop,\n      left,\n      top,\n      clientRect;\n\n    if (typeof field.getBoundingClientRect === 'function') {\n      clientRect = field.getBoundingClientRect();\n      left = clientRect.left + window.pageXOffset;\n      top = clientRect.bottom + window.pageYOffset;\n    } else {\n      left = pEl.offsetLeft;\n      top = pEl.offsetTop + pEl.offsetHeight;\n      while ((pEl = pEl.offsetParent)) {\n        left += pEl.offsetLeft;\n        top += pEl.offsetTop;\n      }\n    }\n\n    // default position is bottom & left\n    if (\n      (this._o.reposition && left + width > viewportWidth) ||\n      (this._o.position.indexOf('right') > -1 &&\n        left - width + field.offsetWidth > 0)\n    ) {\n      left = left - width + field.offsetWidth;\n    }\n    if (\n      (this._o.reposition && top + height > viewportHeight + scrollTop) ||\n      (this._o.position.indexOf('top') > -1 &&\n        top - height - field.offsetHeight > 0)\n    ) {\n      top = top - height - field.offsetHeight;\n    }\n\n    this.el.style.cssText = [\n      'position: absolute',\n      'left: ' + left + 'px',\n      'top: ' + top + 'px'\n    ].join(';');\n  },\n\n  /**\n   * render HTML for a particular month\n   */\n  render: function(year, month) {\n    var opts = this._o,\n      now = new Date(),\n      days = getDaysInMonth(year, month),\n      before = new Date(year, month, 1).getDay(),\n      data = [],\n      row = [];\n    if (!opts.showTime) setToStartOfDay(now);\n    if (opts.firstDay > 0) {\n      before -= opts.firstDay;\n      if (before < 0) {\n        before += 7;\n      }\n    }\n    var cells = days + before,\n      after = cells;\n    while (after > 7) {\n      after -= 7;\n    }\n    cells += 7 - after;\n\n    // Ensure we only compare date portion when deciding to show a date in picker\n    var minDate_date = opts.minDate\n      ? new Date(\n          opts.minDate.getFullYear(),\n          opts.minDate.getMonth(),\n          opts.minDate.getDate()\n        )\n      : null;\n    var maxDate_date = opts.maxDate\n      ? new Date(\n          opts.maxDate.getFullYear(),\n          opts.maxDate.getMonth(),\n          opts.maxDate.getDate()\n        )\n      : null;\n\n    for (var i = 0, r = 0; i < cells; i++) {\n      var day = new Date(year, month, 1 + (i - before)),\n        isSelected = isDate(this._d) ? compareDates(day, this._d) : false,\n        isToday = compareDates(day, now),\n        isEmpty = i < before || i >= days + before,\n        isDisabled =\n          (minDate_date && day < minDate_date) ||\n          (maxDate_date && day > maxDate_date) ||\n          (opts.disableWeekends && isWeekend(day)) ||\n          (opts.disableDayFn && opts.disableDayFn(day));\n\n      row.push(\n        renderDay(\n          1 + (i - before),\n          month,\n          year,\n          isSelected,\n          isToday,\n          isDisabled,\n          isEmpty\n        )\n      );\n\n      if (++r === 7) {\n        if (opts.showWeekNumber) {\n          row.unshift(renderWeek(i - before, month, year));\n        }\n        data.push(renderRow(row, opts.isRTL));\n        row = [];\n        r = 0;\n      }\n    }\n    return renderTable(opts, data);\n  },\n\n  isVisible: function() {\n    return this._v;\n  },\n\n  show: function() {\n    if (!this._v) {\n      removeClass(this.el, 'is-hidden');\n      this._v = true;\n      this.draw();\n      if (this._o.bound) {\n        addEvent(document, 'click', this._onClick);\n        this.adjustPosition();\n      }\n      if (typeof this._o.onOpen === 'function') {\n        this._o.onOpen.call(this);\n      }\n    }\n  },\n\n  hide: function() {\n    var v = this._v;\n    if (v !== false) {\n      if (this._o.bound) {\n        removeEvent(document, 'click', this._onClick);\n      }\n      this.el.style.cssText = '';\n      addClass(this.el, 'is-hidden');\n      this._v = false;\n      if (v !== undefined && typeof this._o.onClose === 'function') {\n        this._o.onClose.call(this);\n      }\n    }\n  },\n\n  /**\n   * GAME OVER\n   */\n  destroy: function() {\n    this.hide();\n    removeEvent(this.el, 'mousedown', this._onMouseDown, true);\n    removeEvent(this.el, 'change', this._onChange);\n    if (this._o.field) {\n      removeEvent(this._o.field, 'change', this._onInputChange);\n      if (this._o.bound) {\n        removeEvent(this._o.trigger, 'click', this._onInputClick);\n        removeEvent(this._o.trigger, 'focus', this._onInputFocus);\n        removeEvent(this._o.trigger, 'blur', this._onInputBlur);\n      }\n    }\n    if (this.el.parentNode) {\n      this.el.parentNode.removeChild(this.el);\n    }\n  }\n};\n\nexport default Pikaday;\n"
  },
  {
    "path": "src/assets/js/components/select2.js",
    "content": "import $ from 'jquery';\n\nexport default function (selector) {\n  return {\n    templateSelection: function (result) {\n      // placeholder.\n      if (result.id === '') {\n        result.element = $(selector).find('[data-placeholder]');\n      }\n\n      let htmlText = '<div><span>' + result.text + '</span> <!--edit--> <!--new--></div>';\n\n      const editUrl = $(result.element).data('edit-url');\n      if (editUrl) {\n        const editText = papiL10n.edit;\n        const editHtml = '<a href=\"#\" class=\"papi-iframe-link-open\" data-url=\"' + editUrl + '\" data-title=\"' + editText + '\" data-selector=\"' + selector.name + '\">' + editText + '</a>';\n        htmlText = htmlText.replace('<!--edit-->', editHtml);\n      }\n\n      const newUrl = $(result.element).data('new-url');\n      if (newUrl) {\n        const newText = papiL10n.new;\n        const newHtml = '<a href=\"#\" class=\"papi-iframe-link-open\" data-url=\"' + newUrl + '\" data-title=\"' + newText + '\" data-selector=\"' + selector.name + '\">' + newText + '</a>';\n        htmlText = htmlText.replace('<!--new-->', newHtml);\n      }\n\n      if (typeof htmlText !== 'undefined') {\n        const $html = $(htmlText);\n\n        $html.find('a').on('mousedown', function(e) {\n          e.stopPropagation();\n        }).on('click', function(e) {\n          e.preventDefault();\n        });\n\n        return $html;\n      }\n\n      return result.text;\n    }\n  }\n};\n"
  },
  {
    "path": "src/assets/js/core.js",
    "content": "import $ from 'jquery';\nimport select2Options from 'components/select2';\n\nclass Core {\n  /**\n   * Initialize Papi core class.\n   */\n  static init () {\n    const core = new Core();\n\n    core.addCurrentClassToMenuItem();\n    core.binds();\n    core.pageTypeSwitcher();\n    core.prepareBoxes();\n    core.setSelectedMenuItem();\n  }\n\n  /**\n   * Autosave fields on heartbeat.\n   *\n   * @param {object} e\n   * @param {object} xhr\n   * @param {object} options\n   */\n  autosave (e, xhr, options) {\n    const reg = /action=(.+?)&/;\n    const val = reg.exec(options.data);\n\n    if (val !== null && val.length && val[1] === 'heartbeat') {\n      const formdata = $('form#post').serializeArray();\n\n      $.each(formdata, function (index, field) {\n        if (field.name.substring(0, 5) === 'papi_' || field.name.substring(0, 5) === '_papi') {\n          // Fetch editor id if any.\n          const editorID = $('[name=\"' + field.name + '\"]').attr('id');\n\n          // Test if the editor id is a editor.\n          if (typeof window.tinymce !== 'undefined' && window.tinymce.get(editorID)) {\n            const editor = window.tinymce.get(editorID);\n\n            // Save the editor content.\n            if (editor && editor.isDirty() && !editor.isHidden()) {\n              editor.save();\n            }\n\n            // Modify field content with real editor content.\n            field.value = $('#' + editorID).val();\n          }\n\n          options.data += '&' + field.name + '=' + field.value;\n        }\n      });\n    }\n  }\n\n  /**\n   * Bind elements with functions.\n   */\n  binds () {\n    $('.papi-box-list > li > p').on('click', this.redirect);\n    $('input[name=\"add-new-page-search\"]').on('keyup', this.search);\n    $('[data-papi-href]').on('click touchstart', this.redirect);\n\n    if ('select2' in $.fn) {\n      $('.inside .papi-table tr .papi-component-select2').each(function (i, elm) {\n        $(elm).select2(select2Options(elm));\n      });\n\n      // Fix issue with browsers where selected attribute is not removed correct.\n      $(document.body).on('change', 'select.papi-component-select2:not([multiple])', function () {\n        $(this).find('option[selected]').removeAttr('selected');\n      });\n    }\n\n    $('.papi-meta-type-term button.handlediv').on('click', this.handlediv);\n\n    // Autosave fields on heartbeat.\n    $(document).ajaxSend(this.autosave);\n  }\n\n  /**\n   * Add current class to menu item.\n   */\n  addCurrentClassToMenuItem () {\n    let $submenu = $('.wp-has-current-submenu .wp-submenu');\n    let $menuitem = $submenu.find('a[href*=\"papi-add-new-page\"]').parent();\n\n    if (!$menuitem.hasClass('current') && !$submenu.find('li.current').length) {\n      $menuitem.addClass('current');\n    }\n  }\n\n  /**\n   * Handle expanded postbox div.\n   *\n   * @param  {object} e\n   */\n  handlediv (e) {\n    e.preventDefault();\n\n    const $this = $(this);\n    const $parent = $this.parent();\n    const $inside = $parent.find('.inside');\n\n    $parent.parent().toggleClass('closed');\n    $inside.toggleClass('papi-hide');\n    $this.attr('aria-expanded', !$inside.hasClass('papi-hide'));\n  }\n\n  /**\n   * Page type switcher.\n   */\n  pageTypeSwitcher () {\n    $('.misc-pub-section.curtime.misc-pub-section-last').removeClass('misc-pub-section-last');\n    $('#papi-page-type-switcher-edit').on('click', function (e) {\n      e.preventDefault();\n      $(this).hide();\n      $('.papi-page-type-switcher > div').slideDown();\n    });\n    $('#papi-page-type-switcher-save').on('click', function (e) {\n      e.preventDefault();\n      $('.papi-page-type-switcher > div').slideUp();\n      $('#papi-page-type-switcher-edit').show();\n      $('.papi-page-type-switcher > span').text($('.papi-page-type-switcher select :selected').text());\n    });\n    $('#papi-page-type-switcher-cancel').on('click', function (e) {\n      e.preventDefault();\n      $('.papi-page-type-switcher > div').slideUp();\n      $('#papi-page-type-switcher-edit').show();\n    });\n  }\n\n  /**\n   * Redirect to location from `papi-href` data attribute\n   * or closest tag with href attribute.\n   *\n   * @param {object} e\n   */\n  redirect (e) {\n    e.preventDefault();\n    let $this = $(this);\n    let papiHref = $this.data().papiHref;\n\n    if (papiHref !== undefined) {\n      window.location = papiHref;\n    } else {\n      window.location = $(this).closest('[href]').attr('href');\n    }\n  }\n\n  /**\n   * Search in page types box list.\n   *\n   * @param {object} e\n   */\n  search (e) {\n    e.preventDefault();\n\n    let $this = $(this);\n    let $list = $('.papi-box-list');\n    let val = $this.val();\n\n    // Destroy masonry before searching the list.\n    $('.papi-box-list').masonry('destroy');\n\n    $list.find('.papi-box-item').each(function () {\n      let $item = $(this);\n\n      if ($item.text().toLowerCase().indexOf(val) === -1) {\n        $item.addClass('papi-hide');\n      } else {\n        $item.removeClass('papi-hide');\n      }\n    });\n\n    // Enable masonry after searching the list.\n    $('.papi-box-list').masonry({\n      itemSelector: '.papi-box-item:not(.papi-hide)',\n      isResizable: true\n    });\n  }\n\n  /**\n   * Prepare boxes with equal height.\n   */\n  prepareBoxes () {\n    let $boxItems = $('.papi-box-item');\n    let thumbnails = $boxItems.find('.papi-page-type-screenshot').length;\n    let boxMaxHeight = 0;\n\n    if (!thumbnails) {\n      $boxItems = $('.papi-page-type-info');\n    }\n\n    $boxItems.each(function () {\n      let $this = $(this);\n\n      if (thumbnails) {\n        $this.find('.papi-post-type-info').removeAttr('style');\n      } else {\n        $this.removeAttr('style');\n      }\n\n      let height = $this.height();\n      boxMaxHeight = height > boxMaxHeight ? height : boxMaxHeight;\n    });\n\n    $boxItems.each(function () {\n      let $this = $(this);\n      let height = boxMaxHeight;\n\n      if (thumbnails) {\n        let $thumb = $this.find('.papi-page-type-screenshot');\n\n        if ($thumb.length) {\n          height = height - $thumb.height();\n        } else {\n          height += 5;\n        }\n\n        $this.find('.papi-page-type-info').height(height);\n      } else {\n        $this.height(height);\n      }\n    });\n\n    // Enable masonry after setting equal height.\n    $('.papi-box-list').masonry({\n      itemSelector: '.papi-box-item:not(.papi-hide)',\n      isResizable: true\n    });\n  }\n\n  /**\n   * Set selected menu item if it isn't selected.\n   */\n  setSelectedMenuItem () {\n    let href = typeof window.location === 'string' ? window.location : window.location.href;\n    let $adminmenu = $('#adminmenu');\n\n    if (!$adminmenu.find('li.current > a.current').length) {\n      href = href.substr(href.lastIndexOf('/') + 1);\n      href = href.replace(/%2F/g, '/');\n      $('a[href=\"' + href + '\"]', $adminmenu).addClass('current').parent().addClass('current');\n    }\n  }\n}\n\nexport default Core;\n"
  },
  {
    "path": "src/assets/js/modals/iframe.js",
    "content": "import $ from 'jquery';\n\n/**\n * Iframe modal.\n */\nclass Iframe {\n\n  /**\n   * The iframe template to use.\n   *\n   * @return {function}\n   */\n  get template() {\n    const template = [\n      '<div tabindex=\"0\" id=\"papi-iframe-modal-dialog\" role=\"dialog\">',\n        '<div class=\"papi-iframe-modal\">',\n          '<div class=\"papi-iframe-modal-content\">',\n            '<div class=\"papi-iframe-modal-header\">',\n            '<button type=\"button\" class=\"papi-iframe-modal-header-close\">',\n              '<span><span class=\"screen-reader-text\"><%= closeText %></span></span>',\n            '</button>',\n            '<div class=\"papi-iframe-modal-header-title\">',\n              '<h1><%= title %></h1>',\n            '</div>',\n          '</div>',\n          '<iframe id=\"papi-iframe-modal-frame\" src=\"\" frameborder=\"0\" allowtransparency=\"true\"></iframe>',\n        '</div>',\n      '</div>',\n      '<div class=\"papi-iframe-modal-backdrop\" role=\"presentation\"></div>'\n    ].join('');\n\n    return window._.template(template);\n  }\n\n  /**\n   * Initialize Papi iframe modal class.\n   */\n  static init() {\n    if ($('body').hasClass('papi-iframe-mode')) {\n      return;\n    }\n\n    $(document).on('click', 'a.papi-iframe-link-open', function(e) {\n      e.preventDefault();\n\n      if (typeof window.papi.iframe !== 'undefined') {\n        window.papi.iframe.close();\n        window.papi.iframe = undefined;\n      }\n\n      const $this = $(this);\n\n      window.papi.iframe = new Iframe({\n        selector: $this.data('selector'),\n        title: $this.data('title'),\n        url: $this.data('url')\n      });\n      window.papi.iframe.open();\n    });\n  }\n\n  /**\n   * Constructor.\n   *\n   * @param {object} options\n   */\n  constructor(options) {\n    this.options = options || {\n      selector: '',\n      title: '',\n      url: ''\n    };\n\n    this.url = options.url + '&papi_css[]=papi-iframe-mode';\n\n    this.$iframe = $(this.template({\n      closeText: papiL10n.close,\n      title: options.title\n    }));\n  }\n\n  /**\n   * Open iframe modal.\n   */\n  open() {\n    // Add iframe source and load event.\n    this.$iframe.find('#papi-iframe-modal-frame')\n      .attr('src', this.url)\n      .on('load', this.loaded.bind(this));\n\n    // Add click event to close button.\n    this.$iframe.find('.papi-iframe-modal-header-close')\n      .on('click', this.close.bind(this));\n\n    $('body').css({'overflow': 'hidden'}).append(this.$iframe);\n  }\n\n  /**\n   * Add iframe events when iframe is loaded.\n   *\n   * @param {object} e\n   */\n  loaded(e) {\n    let $this = $(e.currentTarget);\n    let $contents = $this.contents();\n\n    $contents.find('#post').on('submit', this.submit.bind(this));\n  }\n\n  /**\n   * Change current title in dropdown on iframe submit.\n   *\n   * @param {object} e\n   */\n  submit(e) {\n    $(document).trigger('papi/iframe/submit', [{\n      selector: this.options.selector,\n      iframe: e.currentTarget,\n      url: this.options.url\n    }]);\n  }\n\n  /**\n   * Close iframe modal.\n   */\n  close() {\n    $('#papi-iframe-modal-dialog').hide();\n    $(document).off('focusin');\n    $('body').css({'overflow': 'auto'});\n    $('.papi-iframe-modal-close').off('click');\n    $('#papi-iframe-modal-dialog').remove();\n  }\n}\n\nexport default Iframe;\n"
  },
  {
    "path": "src/assets/js/properties/color.js",
    "content": "import $ from 'jquery';\n\n/**\n * Property Color.\n *\n * Uses the build in color picker in WordPress.\n */\nclass Color {\n  /**\n   * Initialize Property Color.\n   */\n  static init () {\n    new Color().binds();\n  }\n\n  /**\n   * Bind elements with functions.\n   */\n  binds () {\n    let self = this;\n\n    $('.papi-property-color-picker input').each(self.showColorPicker);\n\n    // Bind all new inputs when added in repeater.\n    $(document).on('papi/property/repeater/added', '[data-property=\"color\"]', function () {\n      $('.papi-property-color-picker input').each(self.showColorPicker);\n    });\n  }\n\n  /**\n   * Show color picker.\n   */\n  showColorPicker () {\n    const $el = $(this);\n    const settings = $el.data().settings || {};\n\n    if (!$el.parent().hasClass('papi-property-color-picker')) {\n      return;\n    }\n\n    if (typeof settings.default_color !== 'undefined') {\n      settings.defaultColor = settings.default_color;\n      delete settings.default_color;\n    }\n\n    if (typeof settings.palettes !== 'undefined') {\n      settings.palettes = $.isArray(settings.palettes) && settings.palettes.length ? settings.palettes : true;\n    }\n\n    $el.wpColorPicker(settings);\n  }\n}\n\nexport default Color;\n"
  },
  {
    "path": "src/assets/js/properties/datetime.js",
    "content": "import $ from 'jquery';\nimport Pikaday from 'components/Pikaday';\n\n/**\n * Property Datetime.\n *\n * Using Pikaday with time fields and some custom fixes.\n */\nclass Datetime {\n  /**\n   * Initialize Property Datetime.\n   */\n  static init () {\n    new Datetime().binds();\n  }\n\n  /**\n   * Bind elements with functions.\n   */\n  binds () {\n    this.pikaday($('.inside > .papi-table > tbody > tr > td > input.papi-property-datetime'));\n    this.pikaday($('.papi-table .papi-table input.papi-property-datetime'));\n\n    $(document).on('papi/property/repeater/added', '[data-property=\"datetime\"]', this.updateSelect.bind(this));\n  }\n\n  /**\n   * Initialize Pikaday.\n   *\n   * @param {object} $prop\n   */\n  pikaday ($props) {\n    if (!$props.length) {\n      return;\n    }\n\n    $props.each(function () {\n      let $prop = $(this);\n      let settings = $prop.data().settings;\n\n      // Expects settings to be defined.\n      if (typeof settings === 'undefined') {\n        return;\n      }\n\n      // Fixes to 24 hours actually works if you forget to change the format.\n      if (settings.use24hour) {\n        settings.format = settings.format.replace(/hh/, 'HH');\n      }\n\n      settings.field = $prop[0];\n\n      /* eslint-disable */\n      new Pikaday(settings);\n      /* eslint-enable */\n    });\n  }\n\n  /**\n   * Initialize pikaday field when added to repeater.\n   *\n   * @param {object} e\n   */\n  updateSelect (e) {\n    e.preventDefault();\n    this.pikaday($(e.currentTarget).prev().find('.papi-property-datetime'));\n  }\n}\n\nexport default Datetime;\n"
  },
  {
    "path": "src/assets/js/properties/dropdown.js",
    "content": "import $ from 'jquery';\n\n/**\n * Property Dropdown.\n *\n * Using Select2.\n */\nclass Dropdown {\n  /**\n   * Initialize Property Color.\n   */\n  static init () {\n    new Dropdown().binds();\n  }\n\n  /**\n   * Bind elements with functions.\n   */\n  binds () {\n    $(document).on('papi/property/repeater/added', '[data-property=\"dropdown\"]', this.update.bind(this));\n  }\n\n  /**\n   * Update select if isn't a select2.\n   */\n  update (e) {\n    e.preventDefault();\n\n    const $select = $(e.currentTarget).parent().find('select');\n\n    if ($select.hasClass('papi-component-select2') && 'select2' in $.fn) {\n      $select.select2();\n    }\n  }\n}\n\nexport default Dropdown;\n"
  },
  {
    "path": "src/assets/js/properties/editor.js",
    "content": "import $ from 'jquery';\n\n/* global tinymce, tinyMCE, tinyMCEPreInit, QTags */\n\nclass Editor {\n  /**\n   * Custom TinyMCE settings.\n   *\n   * @var {object}\n   */\n  get customTinyMCESettings () {\n    return {\n      elements: [],\n      mode: 'exact',\n      /* eslint-disable */\n      theme_advanced_resizing: true\n      /* eslint-enable */\n    };\n  }\n\n  /**\n   * Bind elements with functions.\n   */\n  binds () {\n    $(document).on('papi/property/repeater/added', '[data-property=\"editor\"]', this.update.bind(this));\n    $(this.loaded);\n  }\n\n  /**\n   * Get TinyMCE editor.\n   *\n   * @param {string} id\n   *\n   * @return {object}\n   */\n  createTinyMceEditor (id) {\n    let mceInit;\n\n    if (!tinyMCEPreInit.mceInit[id]) {\n      const obj = tinyMCEPreInit.mceInit.content === undefined\n        ? tinyMCEPreInit.mceInit.papiHiddenEditor : tinyMCEPreInit.mceInit.content;\n      mceInit = tinyMCEPreInit.mceInit[id] = $.extend({}, obj);\n    } else {\n      mceInit = tinyMCEPreInit.mceInit[id];\n    }\n\n    mceInit = $.extend(mceInit, this.customTinyMCESettings, {\n      selector: '#' + id,\n      elements: id\n    });\n\n    tinymce.init(mceInit);\n  }\n\n  /**\n   * Close all QTags.\n   *\n   * @param {object} $iframe\n   */\n  closeAllQTags ($iframe) {\n    if (typeof QTags === 'undefined' || typeof $iframe.canvas === 'undefined') {\n      return;\n    }\n\n    QTags.closeAllTags($iframe.id);\n  }\n\n  /**\n   * Get elements.\n   *\n   * @param {object} selectors\n   *\n   * @return {object}\n   */\n  getElements (selectors) {\n    return {\n      $dom: tinyMCE.DOM,\n      $iframe: $('#' + selectors.iframe)\n    };\n  }\n\n  /**\n   * Get TinyMCE Id.\n   *\n   * @param {string} id\n   *\n   * @return {string}\n   */\n  getId (id) {\n    return id.replace('wp-', '').replace('-wrap', '');\n  }\n\n  /**\n   * Get selectors.\n   *\n   * @param {string} id\n   *\n   * @return {object}\n   */\n  getSelectors (id) {\n    return {\n      id: id,\n      iframe: id + '_ifr',\n      htmlTab: '#' + id + '-html',\n      visualTab: '#' + id + '-tmce',\n      wrap: 'wp-' + id + '-wrap'\n    };\n  }\n\n  /**\n   * Fix so visual tab is visible at page load.\n   */\n  static init () {\n    new Editor().binds();\n  }\n\n  /**\n   * Change post id if zero when dom is loaded.\n   */\n  loaded () {\n    if (window.wp.media.view.settings.post.id === 0) {\n      window.wp.media.view.settings.post.id = parseInt($('#post_ID').val(), 10);\n    }\n  }\n\n  /**\n   * Initialize QTags.\n   *\n   * @param {string} id\n   */\n  qtInit (id) {\n    const qtContent = tinyMCEPreInit.qtInit.content === undefined\n      ? tinyMCEPreInit.qtInit.papiHiddenEditor : tinyMCEPreInit.qtInit.content;\n\n    if (qtContent !== undefined) {\n      const qtInit = tinyMCEPreInit.qtInit[id] = $.extend({}, qtContent, {\n        id: id,\n        buttons: qtContent.buttons.replace(',fullscreen', '')\n      });\n\n      try {\n        /* eslint-disable */\n        new QTags(qtInit);\n        /* eslint-enable */\n      } catch (e) {\n      }\n\n      QTags._buttonsInit();\n    }\n  }\n\n  /**\n   * Update editor when it is added to repeater.\n   *\n   * @param {object} e\n   */\n  update (e) {\n    e.preventDefault();\n\n    const $this = $(e.currentTarget);\n    const $prev = $this.prev();\n    let id = '';\n\n    // Support `vertical` layout in flexible and repeater.\n    if ($prev.length && $prev[0].tagName.toLowerCase() === 'tr') {\n      id = this.getId($prev.find('div[id]').attr('id'));\n    } else {\n      id = this.getId($this.parent().find('div[id]').attr('id'));\n    }\n\n    if (tinyMCE.editors[id] !== undefined) {\n      return;\n    }\n\n    const selectors = this.getSelectors(id);\n    const elements = this.getElements(selectors);\n\n    this.closeAllQTags(elements.$iframe);\n    this.qtInit(selectors.id);\n    this.createTinyMceEditor(id);\n\n    $(selectors.visualTab).removeAttr('onclick').on('click', function (e) {\n      e.preventDefault();\n\n      if (typeof window.switchEditors.switchto === 'function') {\n        window.switchEditors.switchto(this);\n      } else {\n        window.switchEditors.go(id, 'tmce');\n      }\n    });\n\n    $(selectors.htmlTab).removeAttr('onclick').on('click', function (e) {\n      e.preventDefault();\n\n      if (typeof window.switchEditors.switchto === 'function') {\n        window.switchEditors.switchto(this);\n      } else {\n        window.switchEditors.go(id, 'html');\n      }\n    });\n\n    elements.$dom.addClass(selectors.wrap, 'tmce-active');\n    elements.$dom.removeClass(selectors.wrap, 'html-active');\n  }\n}\n\nexport default Editor;\n"
  },
  {
    "path": "src/assets/js/properties/file.js",
    "content": "import $ from 'jquery';\nimport Utils from 'utils';\n\n/**\n * Property File.\n *\n * Using the build in media management in WordPress.\n */\nclass File {\n  /**\n   * The file template to compile.\n   *\n   * @return {function}\n   */\n  get template () {\n    return window.wp.template('papi-property-file');\n  }\n\n  /**\n   * Initialize Property Image.\n   */\n  static init () {\n    new File().binds();\n  }\n\n  /**\n   * Bind elements with functions.\n   */\n  binds () {\n    $('.inside .papi-table > tbody .papi-property-file.multiple .attachments').sortable({\n      revert: true\n    });\n\n    $(document).on('click', '.papi-property-file .papi-file-select > .button', this.add.bind(this));\n    $(document).on('mouseenter mouseleave', '.papi-property-file .attachment', this.hover.bind(this));\n    $(document).on('click', '.papi-property-file .attachment a', this.remove.bind(this));\n    $(document).on('change', '.papi-property-repeater-top', this.update.bind(this));\n    $(document).on('click', '.papi-property-file .attachment', this.replace.bind(this));\n  }\n\n  /**\n   * Add new image.\n   *\n   * @param {object} e\n   */\n  add (e) {\n    e.preventDefault();\n\n    const $this = $(e.currentTarget);\n    const $prop = $this.closest('.papi-property-file');\n    const $select = $this.closest('p');\n    const $target = $prop.find('.attachments');\n    const fileType = $prop.data('file-type');\n    const multiple = $prop.hasClass('multiple');\n    const slug = $this.data().slug;\n    const self = this;\n    let library = {};\n\n    if (fileType === 'image') {\n      library.type = 'image';\n    }\n\n    Utils.wpMediaEditor({\n      library: library,\n      multiple: multiple\n    }).on('insert', (attachment) => {\n      let data = {\n        alt: attachment.alt,\n        id: attachment.id,\n        slug: slug,\n        url: attachment.url\n      };\n\n      if (attachment.sizes !== undefined && attachment.sizes.thumbnail !== undefined) {\n        data.url = attachment.sizes.thumbnail.url;\n      }\n\n      if (attachment.type !== 'image') {\n        data.url = attachment.icon;\n      }\n\n      if (fileType === 'file') {\n        data.filename = attachment.filename;\n      }\n\n      self.render($target, data);\n\n      if (!multiple) {\n        $select.addClass('papi-hide');\n      }\n    }).open();\n  }\n\n  /**\n   * Toggle the remove button.\n   *\n   * @param {object} e\n   */\n  hover (e) {\n    e.preventDefault();\n    $(e.currentTarget).find('a').toggle();\n  }\n\n  /**\n   * Remove a image.\n   *\n   * @param {object} e\n   */\n  remove (e) {\n    e.stopPropagation();\n    e.preventDefault();\n\n    const $this = $(e.currentTarget);\n    const $prop = $this.closest('.papi-property-file');\n\n    $prop\n      .find('.papi-file-select')\n      .removeClass('papi-hide');\n\n    $this.closest('.attachment')\n      .remove();\n\n    // Trigger conditional rule.\n    $prop\n      .find('input[type=hidden]')\n      .trigger('change');\n  }\n\n  /**\n   * Render the image with the template.\n   *\n   * @param {object} $el\n   * @param {object} data\n   */\n  render ($el, data) {\n    let template = this.template;\n    template = window._.template($.trim(template()));\n    $el.append('<div class=\"attachment\">' + template(data) + '</div>');\n\n    // Trigger conditional rule.\n    $el.find('input[type=hidden]').trigger('change');\n  }\n\n  /**\n   * Replace image with another one.\n   *\n   * @param {object} e\n   */\n  replace (e) {\n    e.preventDefault();\n\n    const $this = $(e.currentTarget);\n    const $prop = $this.closest('.papi-property-file');\n    const $img = $this.find('img[src]');\n    const $input = $this.find('input[type=hidden]');\n    const postId = $input.val();\n    let library = {};\n\n    if ($prop.data('file-type') === 'image') {\n      library.type = 'image';\n    }\n\n    Utils.wpMediaEditor({\n      library: library,\n      multiple: false\n    }).on('open', () => {\n      let selection = Utils.wpMediaFrame.state().get('selection');\n      const attachment = window.wp.media.attachment(postId);\n\n      attachment.fetch();\n      selection.add(attachment ? [attachment] : []);\n    }).on('insert', (attachment) => {\n      let url = attachment.url;\n\n      if (attachment.sizes !== undefined && attachment.sizes.thumbnail !== undefined) {\n        url = attachment.sizes.thumbnail.url;\n      }\n\n      if (attachment.type !== 'image') {\n        url = attachment.icon;\n      }\n\n      if ($prop.data('file-type') === 'file') {\n        $this.find('.filename div').text(attachment.filename);\n      }\n\n      $img.attr('src', url).attr('alt', attachment.alt);\n      $input.val(attachment.id);\n\n      // Trigger conditional rule.\n      $input.trigger('change');\n    }).open();\n  }\n\n  /**\n   * Update when added to repeater.\n   *\n   * @param {object} e\n   */\n  update (e) {\n    e.preventDefault();\n\n    $(e.currentTarget)\n      .find('tr').last()\n      .find('.attachments')\n      .sortable({\n        revert: true\n      });\n  }\n}\n\nexport default File;\n"
  },
  {
    "path": "src/assets/js/properties/flexible.js",
    "content": "import $ from 'jquery';\nimport Repeater from 'properties/repeater';\nimport Utils from 'utils';\n\nclass Flexible extends Repeater {\n  /**\n   * The template to use.\n   *\n   * @var {function}\n   */\n  get template () {\n    return window.wp.template('papi-property-flexible-row');\n  }\n\n  /**\n   * Initialize Property Flexible.\n   */\n  static init () {\n    new Flexible().binds();\n  }\n\n  /**\n   * Prepare to add a new row to the repeater\n   * and then call fetch to fetch Papi ajax data.\n   *\n   * @param {object} $this\n   */\n  add ($this) {\n    const $repeater = $this.closest('.papi-property-repeater-top');\n    const $tbody = $repeater.find('.repeater-tbody').first();\n    const counter = $tbody.children().length;\n    const jsonText = this.getJSON($this);\n    const layout = $this.data().layout;\n    const limit = $repeater.data().limit;\n    const append = limit === undefined || limit === -1 || $tbody.find('> tr').length < limit;\n\n    if (!jsonText.length || !append) {\n      return;\n    }\n\n    let properties = $.parseJSON(jsonText);\n\n    const self = this;\n    this.fetch(properties, counter, layout, function (res) {\n      self.addRow($tbody, counter, res);\n    });\n  }\n\n  /**\n   * Bind elements with functions.\n   */\n  binds () {\n    const self = this;\n\n    $('.repeater-tbody').sortable({\n      revert: true,\n      handle: '.handle',\n      helper: function (e, ui) {\n        ui.children().each(function () {\n          $(this).width($(this).width());\n        });\n        return ui;\n      },\n      start: function (e, ui) {\n        let editorIds = $.map($(ui.item).find('.wp-editor-area').get(), function (elem) { return elem.id; });\n        self.deactivateEditors(editorIds);\n      },\n      stop: function (e, ui) {\n        self.updateRowNumber($(this).closest('.repeater-tbody'));\n\n        let editorIds = $.map($(ui.item).find('.wp-editor-area').get(), function (elem) { return elem.id; });\n        self.activateEditors(editorIds);\n      }\n    });\n\n    $(document).on('click', '.papi-property-flexible > .bottom button[type=\"button\"]', function (e) {\n      e.preventDefault();\n\n      const $this = $(this);\n      const $prev = $this.prev();\n      const offset = $this.closest('.flexible-layouts-btn-wrap').offset().top - $('#wpadminbar').offset().top;\n\n      if ($prev.height() > offset) {\n        $prev.removeClass('flexible-layouts-top').addClass('flexible-layouts-bottom');\n      } else {\n        $prev.removeClass('flexible-layouts-bottom').addClass('flexible-layouts-top');\n      }\n\n      $prev.removeClass('flexible-layouts-hidden');\n    });\n\n    $(document).on('click', '.papi-property-flexible .flexible-layouts li a', function (e) {\n      e.preventDefault();\n      $(this).closest('.flexible-layouts').addClass('flexible-layouts-hidden');\n      self.add($(this));\n    });\n\n    $(document).on('mouseup', 'body', function (e) {\n      const $layouts = $('.flexible-layouts:not(.flexible-layouts-hidden)');\n      if (!$layouts.is(e.target) && $layouts.has(e.target).length === 0) {\n        $layouts.addClass('flexible-layouts-hidden');\n      }\n    });\n\n    $(document).on('click', '.papi-property-flexible .repeater-remove-item', function (e) {\n      e.preventDefault();\n      self.remove($(this));\n    });\n  }\n\n  /**\n   * Fetch properties from Papi ajax.\n   *\n   * @param {array} properties\n   * @param {int} counter\n   * @param {string} flexibleLayout\n   * @param {function} callback\n   */\n  fetch (properties, counter, flexibleLayout, callback) {\n    const params = {\n      'action': 'get_properties',\n      'counter': counter,\n      'flexible_layout': flexibleLayout,\n      'meta_type': Utils.getMetaType()\n    };\n\n    params[Utils.getMetaTypeKey()] = Utils.getMetaTypeValue();\n\n    $.ajax({\n      type: 'POST',\n      data: {\n        properties: JSON.stringify(properties)\n      },\n      url: papi.ajaxUrl + '?' + $.param(params),\n      dataType: 'json'\n    }).success(callback);\n  }\n\n  /**\n   * Remove item from the flexible repeater.\n   *\n   * @param {object} $this\n   */\n  remove ($this) {\n    let $tbody = $this.closest('.papi-property-repeater-top');\n\n    if ($tbody.hasClass('papi-property-flexible')) {\n      $tbody = $tbody.find('.repeater-tbody');\n      $this.closest('tr').remove();\n      this.updateRowNumber($tbody);\n    }\n  }\n\n  /**\n   * Update database row number.\n   *\n   * @param {object} $tbody\n   */\n  updateDatabaseRowNumber ($tbody) {\n    let counter = $tbody.find('tr tbody tr').length;\n\n    $tbody\n      .closest('.papi-property-repeater-top')\n      .find('.papi-property-repeater-rows')\n      .val($tbody.find('tr tbody tr').length);\n\n    this.triggerRule($tbody, counter);\n  }\n}\n\nexport default Flexible;\n"
  },
  {
    "path": "src/assets/js/properties/link.js",
    "content": "import $ from 'jquery';\n\n/**\n * Property Link.\n *\n * Using the build in link manager in WordPress.\n */\nclass Link {\n  /**\n   * The link template to compile.\n   *\n   * @return {function}\n   */\n  get template () {\n    return window.wp.template('papi-property-link');\n  }\n\n  /**\n   * Initialize Property Link.\n   */\n  static init () {\n    new Link().binds();\n  }\n\n  /**\n   * Bind elements with functions.\n   */\n  binds () {\n    $(document).on('click', '.papi-property-link button[data-link-action=\"add\"]', this.add.bind(this));\n    $(document).on('click', '.papi-property-link button[data-link-action=\"edit\"]', this.edit.bind(this));\n    $(document).on('click', '.papi-property-link button[data-link-action=\"remove\"]', this.remove.bind(this));\n    $(document).on('click', '#wp-link-submit', this.render.bind(this));\n  }\n\n  /**\n   * Add new link.\n   *\n   * @param {object} e\n   */\n  add (e) {\n    e.preventDefault();\n\n    const $this = $(e.currentTarget);\n\n    this.$el = $this.closest('.papi-property-link');\n    this.$p = this.$el.find('p');\n\n    // Create a new window.wpLink.update that only\n    // close the wpLink window. Save the old to later.\n    this.oldLinkUpdate = window.wpLink.update;\n    window.wpLink.update = function () {\n      window.wpLink.close();\n    };\n\n    window.wpLink.open();\n  }\n\n  /**\n   * Add new link.\n   *\n   * @param {object} e\n   */\n  edit (e) {\n    e.preventDefault();\n\n    const $this = $(e.currentTarget);\n\n    this.$el = $this.closest('.papi-property-link');\n    this.$p = this.$el.find('p');\n\n    // Create a new window.wpLink.update that only\n    // close the wpLink window. Save the old to later.\n    this.oldLinkUpdate = window.wpLink.update;\n    window.wpLink.update = function () {\n      window.wpLink.close();\n    };\n\n    window.wpLink.open();\n\n    const url = this.$el.find('.wp-link-url').val();\n    const text = this.$el.find('.wp-link-text').val();\n\n    $('#wp-link-url').val($.trim(url));\n    $('#wp-link-text').val($.trim(text));\n\n    if ($.trim(this.$el.find('.wp-link-target').val()) === '_blank') {\n      $('#wp-link-target').attr('checked', 'checked');\n    }\n  }\n\n  /**\n   * Remove a link.\n   *\n   * @param {object} e\n   */\n  remove (e) {\n    e.preventDefault();\n\n    const $this = $(e.currentTarget);\n    const $prop = $this.closest('.papi-property-link');\n\n    $prop.find('.link-table').remove();\n    $prop.find('input[type=\"hidden\"]:first').val('');\n\n    const $spans = $prop.find('p > span');\n    $spans.first().removeClass('papi-hide');\n    $spans.last().addClass('papi-hide');\n\n    // Trigger conditional rule.\n    $prop\n      .find('data-papi-rule')\n      .trigger('change');\n  }\n\n  /**\n   * Render the image with the template.\n   *\n   * @param {object} e\n   */\n  render (e) {\n    if (typeof this.$el === 'undefined') {\n      return;\n    }\n\n    e.preventDefault();\n\n    const data = window.wpLink.getAttrs();\n    let template = this.template;\n    template = window._.template($.trim(template()));\n\n    data.link = '<a href=\"' + data.href + '\" target=\"_blank\">' + data.href + '</a>';\n    data.title = $('#wp-link-text').val();\n    data.slug = this.$el.data('slug');\n    data.target = data.target || '';\n\n    this.$el.find('input[type=\"hidden\"]:first').val(1);\n\n    this.$el.find('.link-table').remove();\n    this.$p.before(template(data));\n\n    const $spans = this.$p.find('> span');\n    $spans.first().addClass('papi-hide');\n    $spans.last().removeClass('papi-hide');\n\n    // Trigger conditional rule.\n    this.$el\n      .find('data-papi-rule')\n      .trigger('change');\n\n    // Restore window.wpLink.update\n    window.wpLink.update = this.oldLinkUpdate;\n\n    this.wpLink = this.$el = this.$p = undefined;\n  }\n}\n\nexport default Link;\n"
  },
  {
    "path": "src/assets/js/properties/module.js",
    "content": "import $ from 'jquery';\nimport select2Options from 'components/select2';\n\n/**\n * Property Module.\n *\n * Using Select2.\n */\nclass Module {\n  /**\n   * The option template to compile.\n   *\n   * @return {function}\n   */\n  get optionTemplate () {\n    return window.wp.template('papi-property-module-option');\n  }\n\n  /**\n   * The option placeholder template to compile.\n   *\n   * @return {function}\n   */\n  get optionPlaceholderTemplate () {\n    return window.wp.template('papi-property-module-option-placeholder');\n  }\n\n  /**\n   * Initialize Property Module.\n   */\n  static init () {\n    new Module().binds();\n  }\n\n  /**\n   * Bind elements with functions.\n   */\n  binds () {\n    $(document).on('papi/property/repeater/added', '[data-property=\"post\"]', this.update.bind(this));\n    $(document).on('change', '.papi-property-module-right', this.change.bind(this));\n    $(document).on('papi/iframe/submit', this.iframeSubmit.bind(this));\n  }\n\n  /**\n   * Change template dropdown when selecting\n   * a different module.\n   *\n   * @param {object} e\n   */\n  change (e) {\n    e.preventDefault();\n\n    const self = this;\n    const $this = $(e.currentTarget);\n\n    const entryTypeId = $this.find('option:selected').data('entry-type');\n    const params = {\n      'action': 'get_entry_type',\n      'entry_type': entryTypeId\n    };\n    const $prop = $this.closest('.papi-property-module');\n    const $select = $prop.find('.papi-property-module-left');\n\n    $.get(papi.ajaxUrl + '?' + $.param(params), function (entryType) {\n      $select.empty();\n\n      if ($select.data('placeholder') && entryType.length) {\n        const optionPlaceholderTemplate = self.optionPlaceholderTemplate;\n        const template1 = window._.template($.trim(optionPlaceholderTemplate()));\n\n        $select.append(template1());\n      }\n\n      const optionTemplate = self.optionTemplate;\n      const template2 = window._.template($.trim(optionTemplate()));\n\n      for (var key in entryType.template) {\n        const template = entryType.template[key];\n        let item = {};\n\n        // Convert string value to item object.\n        if (typeof template === 'string') {\n          item = {\n            'label': template,\n            'template': template,\n            'default': false\n          };\n        }\n\n        // Check if template is a object\n        // or bail.\n        if ($.isPlainObject(template)) {\n          item = template;\n        } else {\n          continue;\n        }\n\n        $select.append(template2({\n          title: item.label,\n          value: key\n        }));\n\n        if (item.default) {\n          $select.val(key);\n        }\n      }\n\n      if ($select.hasClass('papi-component-select2') && 'select2' in $.fn) {\n        $select.trigger('change');\n      }\n    });\n  }\n\n  /**\n   * Update select when iframe is submitted.\n   *\n   * @param {object} e\n   * @param {object} data\n   */\n  iframeSubmit (e, data) {\n    if (!data.iframe) {\n      return;\n    }\n\n    const $elm = $(data.iframe);\n    const title = $elm.find('[name=\"post_title\"]').val();\n    const id = $elm.find('[name=\"post_ID\"]').val();\n    const $select = $('[name=' + data.selector + ']');\n\n    if (data.url.indexOf('post-new') !== -1) {\n      // new\n      const optionTemplate = this.optionTemplate;\n      const template = window._.template($.trim(optionTemplate()));\n\n      if ($select.find('option[value=' + id + ']').length) {\n        return;\n      }\n\n      $select.append(template({\n        id: id,\n        title: title\n      }));\n    } else {\n      // edit\n      const $option = $select.find('option[data-edit-url=\"' + data.url + '\"]');\n      $option.removeData('data');\n      $option.text(title);\n    }\n\n    $select.trigger('change');\n    $select.val(id);\n  }\n\n  /**\n   * Initialize select2 field when added to repeater.\n   *\n   * @param {object} e\n   */\n  update (e) {\n    e.preventDefault();\n\n    const $select = $(e.currentTarget).parent().find('select');\n\n    if ($select.hasClass('papi-component-select2') && 'select2' in $.fn) {\n      $select.select2(select2Options($select[0]));\n    }\n  }\n}\n\nexport default Module;\n"
  },
  {
    "path": "src/assets/js/properties/post.js",
    "content": "import $ from 'jquery';\nimport select2Options from 'components/select2';\n\n/**\n * Property Post.\n *\n * Using Select2.\n */\nclass Post {\n  /**\n   * The option template to compile.\n   *\n   * @return {function}\n   */\n  get optionTemplate () {\n    return window.wp.template('papi-property-post-option');\n  }\n\n  /**\n   * The option placeholder template to compile.\n   *\n   * @return {function}\n   */\n  get optionPlaceholderTemplate () {\n    return window.wp.template('papi-property-post-option-placeholder');\n  }\n\n  /**\n   * Initialize Property Post.\n   */\n  static init () {\n    new Post().binds();\n  }\n\n  /**\n   * Bind elements with functions.\n   */\n  binds () {\n    $(document).on('papi/property/repeater/added', '[data-property=\"post\"]', this.update.bind(this));\n    $(document).on('change', '.papi-property-post-left', this.change.bind(this));\n    $(document).on('papi/iframe/submit', this.iframeSubmit.bind(this));\n  }\n\n  /**\n   * Change post dropdown when selecting\n   * a different post type.\n   *\n   * @param {object} e\n   */\n  change (e) {\n    e.preventDefault();\n\n    const self = this;\n    const $this = $(e.currentTarget);\n    const query = $this.data('post-query').length\n      ? $this.data('post-query')\n      : {};\n\n    query.post_type = $this.val();\n\n    const params = {\n      'action': 'get_posts',\n      'fields': ['ID', 'post_title', 'post_type'],\n      'query': query\n    };\n    const $prop = $this.closest('.papi-property-post');\n    const $select = $prop.find('.papi-property-post-right');\n\n    $('[for=\"' + $select.attr('id') + '\"]')\n      .parent()\n      .find('label')\n      .text($this.data('select-item').replace('%s', $this.find('option:selected').text()));\n\n    $.get(papi.ajaxUrl + '?' + $.param(params), function (posts) {\n      $select.empty();\n\n      if ($select.data('placeholder').length && posts.length) {\n        const optionPlaceholderTemplate = self.optionPlaceholderTemplate;\n        const template1 = window._.template($.trim(optionPlaceholderTemplate()));\n\n        $select.append(template1({\n          type: posts[0].post_type\n        }));\n      }\n\n      const optionTemplate = self.optionTemplate;\n      const template2 = window._.template($.trim(optionTemplate()));\n\n      $.each(posts, function (index, post) {\n        $select.append(template2({\n          id: post.ID,\n          title: post.post_title,\n          type: post.post_type\n        }));\n      });\n\n      if ($select.hasClass('papi-component-select2') && 'select2' in $.fn) {\n        $select.trigger('change');\n      }\n    });\n  }\n\n  /**\n   * Update select when iframe is submitted.\n   *\n   * @param {object} e\n   * @param {object} data\n   */\n  iframeSubmit (e, data) {\n    if (!data.iframe) {\n      return;\n    }\n\n    const $elm = $(data.iframe);\n    const title = $elm.find('[name=\"post_title\"]').val();\n    const id = $elm.find('[name=\"post_ID\"]').val();\n    const $select = $('[name=' + data.selector + ']');\n\n    if (data.url.indexOf('post-new') !== -1) {\n      // new\n      const optionTemplate = this.optionTemplate;\n      const template = window._.template($.trim(optionTemplate()));\n\n      if ($select.find('option[value=' + id + ']').length) {\n        return;\n      }\n\n      $select.append(template({\n        id: id,\n        title: title\n      }));\n    } else {\n      // edit\n      const $option = $select.find('option[data-edit-url=\"' + data.url + '\"]');\n      $option.removeData('data');\n      $option.text(title);\n    }\n\n    $select.trigger('change');\n    $select.val(id);\n  }\n\n  /**\n   * Initialize select2 field when added to repeater.\n   *\n   * @param {object} e\n   */\n  update (e) {\n    e.preventDefault();\n\n    const $select = $(e.currentTarget).parent().find('select');\n\n    if ($select.hasClass('papi-component-select2') && 'select2' in $.fn) {\n      $select.select2(select2Options($select[0]));\n    }\n  }\n}\n\nexport default Post;\n"
  },
  {
    "path": "src/assets/js/properties/reference.js",
    "content": "import $ from 'jquery';\n\n/**\n * Property Reference.\n */\nclass Reference {\n  /**\n   * Initialize Property Reference.\n   */\n  static init () {\n    new Reference().binds();\n  }\n\n  /**\n   * Bind elements with functions.\n   */\n  binds () {\n    $(document).on('click', '.papi-property-reference .handlediv', this.toggle.bind(this));\n  }\n\n  /**\n   * Toggle page type div.\n   *\n   * @parma {object} e\n   */\n  toggle (e) {\n    e.preventDefault();\n\n    $(e.currentTarget)\n      .parent()\n      .toggleClass('closed')\n      .next()\n      .toggle();\n  }\n}\n\nexport default Reference;\n"
  },
  {
    "path": "src/assets/js/properties/relationship.js",
    "content": "import $ from 'jquery';\n\n/**\n * Property Relationship.\n */\nclass Relationship {\n  /**\n   * Initialize Property Relationship.\n   */\n  static init () {\n    new Relationship().binds();\n  }\n  /**\n   * Add new page to the list.\n   *\n   * @param {object} e\n   */\n  add (e) {\n    e.preventDefault();\n\n    const $this = $(e.currentTarget);\n    const $li = $this.clone();\n    const $prop = $this.closest('.papi-property-relationship');\n    const $right = $prop.find('.relationship-right');\n    const $list = $right.find('ul');\n    const settings = $prop.data().settings;\n    const limit = settings.limit;\n    const append = limit === undefined || limit === -1 || $list.find('li').length < limit;\n\n    if (append) {\n      $li.find('span.icon').removeClass('plus').addClass('minus');\n      $li.find('input').attr('name', $li.find('input').data().name);\n\n      $li.appendTo($list);\n      this.triggerRule($prop);\n\n      if (settings.onlyOnce) {\n        $this.addClass('papi-hide');\n      }\n    }\n  }\n\n  /**\n   * Bind elements with functions.\n   */\n  binds () {\n    $('.relationship-right > ul').sortable({\n      placeholder: 'ui-state-highlight',\n      start: function (e, ui) {\n        ui.item.addClass('sortable');\n      },\n      stop: function (e, ui) {\n        ui.item.removeClass('sortable');\n      }\n    }).disableSelection();\n\n    $(document).on('click', '.papi-property-relationship .relationship-left li', this.add.bind(this));\n    $(document).on('click', '.papi-property-relationship .relationship-right li', this.remove.bind(this));\n    $(document).on('keyup', '.papi-property-relationship input[type=\"search\"]', this.search.bind(this));\n    $(document).on('papi/property/repeater/added', '[data-property=\"relationship\"]', this.update.bind(this));\n  }\n\n  /**\n   * Remove the selected page.\n   *\n   * @param {object} e\n   */\n  remove (e) {\n    const $this = $(e.currentTarget);\n    const $prop = $this.closest('.papi-property-relationship');\n    const settings = $prop.data().settings;\n\n    if (settings.onlyOnce) {\n      $prop\n        .find('.relationship-left input[value=\"' + $this.find('input[type=\"hidden\"]').val() + '\"]')\n        .closest('li')\n        .removeClass('papi-hide');\n    }\n\n    $this.remove();\n    this.triggerRule($prop);\n  }\n\n  /**\n   * Search for a page in the list.\n   *\n   * @param {object} e\n   */\n  search (e) {\n    e.preventDefault();\n\n    const $this = $(e.currentTarget);\n    const $list = $this.closest('.papi-property-relationship').find('.relationship-left ul');\n    const val = $this.val().toLowerCase();\n\n    $list.find('li').each(function () {\n      let $li = $(this);\n      if ($li.text().toLowerCase().indexOf(val) === -1) {\n        $li.addClass('papi-hide');\n      } else {\n        $li.removeClass('papi-hide');\n      }\n    });\n  }\n\n  /**\n   * Trigger conditional rule.\n   *\n   * @param {object} $prop\n   */\n  triggerRule ($prop) {\n    $prop\n      .find('[data-papi-rule]')\n      .trigger('change');\n  }\n\n  /**\n   * Fix name attribute when added to a repeater.\n   *\n   * @param {object} e\n   */\n  update (e) {\n    e.preventDefault();\n\n    let $this = $(e.currentTarget);\n    const $prop = $this.prev();\n\n    $prop.find('.relationship-left [name]').each(function () {\n      $this = $(this);\n      $this.data('name', $this.attr('name'));\n      $this.removeAttr('name');\n    });\n  }\n}\n\nexport default Relationship;\n"
  },
  {
    "path": "src/assets/js/properties/repeater.js",
    "content": "import $ from 'jquery';\nimport Utils from 'utils';\n\n/* global tinymce, tinyMCEPreInit */\n\n/**\n * Property Repeater.\n */\nclass Repeater {\n  /**\n   * The template to use.\n   *\n   * @var {function}\n   */\n  get template () {\n    return window.wp.template('papi-property-repeater-row');\n  }\n\n  /**\n   * Initialize Property Repeater.\n   */\n  static init () {\n    new Repeater().binds();\n  }\n\n  /**\n   * Prepare to add a new row to the repeater\n   * and then call fetch to fetch Papi ajax data.\n   *\n   * @param {object} $this\n   */\n  add ($this) {\n    const $repeater = $this.closest('.papi-property-repeater-top');\n    const $tbody = $repeater.find('.repeater-tbody').first();\n    const counter = $tbody.children().length;\n    const jsonText = this.getJSON($this);\n    const limit = $repeater.data().limit;\n    const append = limit === undefined || limit === -1 || $tbody.find('> tr').length < limit;\n\n    if (!jsonText.length || !append) {\n      return;\n    }\n\n    let properties = $.parseJSON(jsonText);\n\n    const self = this;\n    this.fetch(properties, counter, function (res) {\n      self.addRow($tbody, counter, res);\n    });\n  }\n\n  /**\n   * Add a new row to the repeater.\n   *\n   * @param {object} $tbody\n   * @param {int} counter\n   * @param {array} items\n   */\n  addRow ($tbody, counter, res) {\n    let columns = [];\n\n    for (var i = 0, l = res.html.length; i < l; i++) {\n      if (typeof res.html[i] === 'string') {\n        columns.push(res.html[i]);\n      }\n    }\n\n    let $row = this.getHtml({\n      columns: columns.join(''),\n      counter: counter\n    });\n\n    $row.appendTo($tbody);\n    $row.find('[name*=\"_property\"]').trigger('papi/property/repeater/added');\n    $row.find('[data-papi-rules=\"true\"]').trigger('init');\n\n    this.scrollDownTable($tbody);\n    this.updateDatabaseRowNumber($tbody);\n\n    $tbody.closest('.papi-property-repeater-top').trigger('change');\n  }\n\n  /**\n   * Bind elements with functions.\n   */\n  binds () {\n    const self = this;\n\n    $('.repeater-tbody').sortable({\n      revert: true,\n      handle: '.handle',\n      helper: function (e, ui) {\n        ui.children().each(function () {\n          $(this).width($(this).width());\n        });\n        return ui;\n      },\n      start: function (e, ui) {\n        let editorIds = $.map($(ui.item).find('.wp-editor-area').get(), function (elem) {\n          return elem.id;\n        });\n        self.deactivateEditors(editorIds);\n      },\n      stop: function (e, ui) {\n        const $tbody = $(this).closest('.repeater-tbody');\n        self.updateRowNumber($tbody);\n\n        let editorIds = $.map($(ui.item).find('.wp-editor-area').get(), function (elem) {\n          return elem.id;\n        });\n        self.activateEditors(editorIds);\n      }\n    });\n\n    $(document).on('click', '.papi-property-repeater .bottom button[type=\"button\"]', function (e) {\n      e.preventDefault();\n      self.add($(this));\n    });\n\n    $(document).on('click', '.repeater-tbody span.toggle', function (e) {\n      e.preventDefault();\n      self.toggle($(this));\n    });\n\n    $(document).on('click', '.papi-property-repeater .repeater-remove-item', function (e) {\n      e.preventDefault();\n      self.remove($(this));\n    });\n  }\n\n  /**\n   * Deactivate rich editors by instance ids.\n   *\n   * @param  {array} ids tinyMCE editor ids\n   */\n  deactivateEditors (ids) {\n    $.each(ids, function () {\n      const editor = tinymce.get(this);\n\n      if (typeof this !== 'string' || !this.length || this[0] !== 'p') {\n        return;\n      }\n\n      const $textarea = $('#' + this);\n      let editorHeight;\n\n      $textarea.data('papi-editor-reinit', !!editor);\n\n      if (editor) {\n        editorHeight = editor.isHidden() ? $textarea.height() : $(editor.getWin()).height();\n\n        // wpautop is killing paragraphs on remove(SaveContent)\n        // it will be reactivated on init anyway.\n        editor.settings.wpautop = false;\n\n        $textarea.outerHeight(editorHeight).data('papi-editor-html', editor.isHidden());\n        editor.remove();\n      }\n    });\n  }\n\n  /**\n   * Reactivate rich editors by instance ids.\n   *\n   * @param  {array} ids tinyMCE editor ids\n   */\n  activateEditors (ids) {\n    $.each(ids, function () {\n      if (typeof this !== 'string' || !this.length || this[0] !== 'p') {\n        return;\n      }\n\n      const $textarea = $('#' + this);\n\n      if (!tinymce.get(this) && $textarea.data('papi-editor-reinit')) {\n        const init = tinymce.extend({}, tinyMCEPreInit.mceInit[this], { height: $textarea.outerHeight() });\n\n        // don't reinit if editor is in text mode\n        // wp will reinit it on mode switch\n        if (!$textarea.data('papi-editor-html')) {\n          tinymce.init(init);\n        }\n      }\n    });\n  }\n\n  /**\n   * Fetch properties from Papi ajax.\n   *\n   * @param {array} properties\n   * @param {int} counter\n   * @param {function} callback\n   */\n  fetch (properties, counter, callback) {\n    const params = {\n      'action': 'get_properties',\n      'counter': counter,\n      'meta_type': Utils.getMetaType()\n    };\n\n    params[Utils.getMetaTypeKey()] = Utils.getMetaTypeValue();\n\n    $.ajax({\n      type: 'POST',\n      data: {\n        properties: JSON.stringify(properties)\n      },\n      url: papi.ajaxUrl + '?' + $.param(params),\n      dataType: 'json'\n    }).success(callback);\n  }\n\n  /**\n   * Get JSON properties template.\n   *\n   * @param {object} $this\n   *\n   * @return {string}\n   */\n  getJSON ($this) {\n    return $('script[data-papi-json=\"' + $this.data().papiJson + '\"]').first().text();\n  }\n\n  /**\n   * Get row html as jQuery object.\n   *\n   * @param {object} data\n   *\n   * @return {object}\n   */\n  getHtml (data) {\n    let template = this.template;\n    template = window._.template($.trim(template()));\n    return $(template(data));\n  }\n\n  /**\n   * Replace array number in name array.\n   *\n   * @param  {string} name\n   * @param  {int} j\n   * @param  {int} p\n   *\n   * @return {string}\n   */\n  replaceArrayNumber (name, j, p) {\n    name = name.split('[');\n\n    if (typeof p === 'undefined') {\n      p = 0;\n    }\n\n    for (var i = 0, l = name.length; i < l; i++) {\n      var part = name[i];\n\n      if (/\\d+$/.test(part.replace(']', '')) && (p === 0 || p === i)) {\n        name[i] = j + ']';\n        break;\n      }\n    }\n\n    return name.join('[');\n  }\n\n  /**\n   * Remove item from the repeater.\n   *\n   * @param {object} e\n   */\n  remove ($this) {\n    let $tbody = $this.closest('.papi-property-repeater-top');\n\n    if (!$tbody.hasClass('papi-property-flexible')) {\n      $tbody = $tbody.find('.repeater-tbody').first();\n      $this.closest('tr').remove();\n      this.updateRowNumber($tbody);\n    }\n  }\n\n  /**\n   * Scroll down to table.\n   *\n   * @param {object} $tbody\n   */\n  scrollDownTable ($tbody) {\n    const $tr = $('> tr:last', $tbody);\n    $('html, body').animate({\n      scrollTop: $tr.offset().top - $tr.height()\n    });\n  }\n\n  /**\n   * Toggle the row content.\n   *\n   * @param {object} $this\n   */\n  toggle ($this) {\n    $this.closest('tr').toggleClass('closed');\n  }\n\n  /**\n   * Trigger conditional rule.\n   *\n   * @param {object} $prop\n   */\n  triggerRule ($tbody, counter) {\n    const $top = $tbody.closest('.papi-property-repeater-top');\n    const name = $top.find('.bottom').next().attr('name').replace('[]', '');\n    $('[data-papi-rule=\"' + name + '\"]').data('papi-rule-value', counter).trigger('change');\n  }\n\n  /**\n   * Update table row number.\n   *\n   * @param {object} $tbody\n   */\n  updateRowNumber ($tbody) {\n    let self = this;\n\n    $tbody.first().find('> tr').each((i, el) => {\n      let $el = $(el);\n      let $rpt = $el.closest('.papi-property-repeater-top');\n      let position = 0;\n      let count = 0;\n\n      if ($rpt.length) {\n        while ($rpt.length) {\n          if (!$rpt.parentsUntil('.papi-property-repeater-top').length) {\n            break;\n          }\n\n          $rpt = $rpt.closest('.papi-property-repeater-top').prev();\n\n          if ($rpt.length) {\n            count++;\n\n            if (count > 1) {\n              position += 3;\n            }\n          }\n        }\n      }\n\n      $el.find('> td:first-child .count').text(i + 1);\n\n      // Replace `data-slug` attribute.\n      $el.find('[data-replace-slug=\"true\"]').each(function () {\n        let $prop = $(this);\n        $prop.attr('data-slug', self.replaceArrayNumber($prop.attr('data-slug'), i, position));\n      });\n\n      // Replace `data-papi-rule` attribute.\n      $el.find('[data-papi-rule*=\"papi_\"]').each(function () {\n        let $prop = $(this);\n        $prop.attr('data-papi-rule', self.replaceArrayNumber($prop.attr('data-papi-rule'), i, position));\n      });\n\n      // Replace `data-papi-json` attribute.\n      $el.find('[data-papi-json*=\"papi_\"]').each(function () {\n        let $prop = $(this);\n        $prop.attr('data-papi-json', self.replaceArrayNumber($prop.attr('data-papi-json'), i, position));\n      });\n\n      // Replace id attribute.\n      $el.find('[id*=\"papi_\"]').each(function () {\n        let $prop = $(this);\n        $prop.attr('id', self.replaceArrayNumber($prop.attr('id'), i, position));\n      });\n\n      // Replace name attribute.\n      $el.find('[name*=\"papi_\"]').each(function () {\n        let $prop = $(this);\n        $prop.attr('name', self.replaceArrayNumber($prop.attr('name'), i, position));\n      });\n    });\n\n    this.updateDatabaseRowNumber($tbody);\n  }\n\n  /**\n   * Update database row number.\n   *\n   * @param {object} $el\n   */\n  updateDatabaseRowNumber ($tbody) {\n    let counter = $tbody.find('tr').length;\n\n    $tbody\n      .closest('.papi-property-repeater-top')\n      .find('.papi-property-repeater-rows')\n      .val();\n\n    this.triggerRule($tbody, counter);\n  }\n}\n\nexport default Repeater;\n"
  },
  {
    "path": "src/assets/js/properties/term.js",
    "content": "import $ from 'jquery';\n\n/**\n * Property Term.\n *\n * Using Select2.\n */\nclass Term {\n  /**\n   * Initialize Property Term.\n   */\n  static init () {\n    new Term().binds();\n  }\n\n  /**\n   * Bind elements with functions.\n   */\n  binds () {\n    $(document).on('papi/property/repeater/added', '[data-property=\"term\"]', this.update.bind(this));\n    $(document).on('change', '.papi-property-term-left', this.change.bind(this));\n  }\n\n  /**\n   * Change term dropdown when selecting\n   * a different taxonomy.\n   *\n   * @param {object} e\n   */\n  change (e) {\n    e.preventDefault();\n\n    const $this = $(e.currentTarget);\n    const query = $this.data('term-query').length\n      ? $this.data('term-query')\n      : {};\n\n    const params = {\n      'action': 'get_terms',\n      'taxonomy': $this.val(),\n      'query': query\n    };\n    const $prop = $this.closest('.papi-property-term');\n    const $select = $prop.find('.papi-property-term-right');\n\n    $('[for=\"' + $select.attr('id') + '\"]')\n      .parent()\n      .find('label')\n      .text($this.data('select-item').replace('%s', $this.find('option:selected').text()));\n\n    $.get(papi.ajaxUrl + '?' + $.param(params), function (terms) {\n      $select.empty();\n\n      $.each(terms, function (termId, termName) {\n        $select.append($('<option></option>').attr('value', termId).text(termName));\n      });\n\n      if ($select.hasClass('papi-component-select2') && 'select2' in $.fn) {\n        $select.select2();\n      }\n    });\n  }\n\n  /**\n   * Initialize pikaday field when added to repeater.\n   *\n   * @param {object} e\n   */\n  update (e) {\n    e.preventDefault();\n\n    const $select = $(e.currentTarget).parent().find('select');\n\n    if ($select.hasClass('papi-component-select2') && 'select2' in $.fn) {\n      $select.select2();\n    }\n  }\n}\n\nexport default Term;\n"
  },
  {
    "path": "src/assets/js/properties/url.js",
    "content": "import $ from 'jquery';\nimport Utils from 'utils';\n\n/**\n * Property Url.\n */\nclass Url {\n  /**\n   * Initialize Property Url.\n   */\n  static init () {\n    new Url().binds();\n  }\n\n  /**\n   * Add new media file.\n   *\n   * @param {object} e\n   */\n  add (e) {\n    e.preventDefault();\n\n    const $this = $(e.currentTarget);\n\n    Utils.wpMediaEditor().on('insert', (attachment) => {\n      $this.prev().val(attachment.url);\n    }).open();\n  }\n\n  /**\n   * Bind elements with functions.\n   */\n  binds () {\n    $(document).on('click', '.papi-url-media-button', this.add.bind(this));\n  }\n}\n\nexport default Url;\n"
  },
  {
    "path": "src/assets/js/required.js",
    "content": "import $ from 'jquery';\n\nclass Required {\n  /**\n   * Initialize Papi core class.\n   */\n  static init () {\n    new Required().binds();\n  }\n\n  /**\n   * Bind elements with functions.\n   */\n  binds () {\n    $('body').on('click', '.papi-rq-link', this.requiredLink);\n    $('#publish').on('click', this.publishPost);\n  }\n\n  /**\n   * Animate down to required field.\n   *\n   * @param {object} e\n   */\n  requiredLink (e) {\n    $('html, body').animate({\n      scrollTop: $('[for=' + $(this).attr('href').replace('#', '') + ']').offset().top - 45\n    });\n  }\n\n  /**\n   * Collect all required fields that don't have any value\n   * and output error message.\n   *\n   * @param {object} e\n   */\n  publishPost (e) {\n    const $button = $(this);\n    const $fields = $('.papi-rq');\n    const $spinner = $('#publishing-action .spinner');\n    let $errors = [];\n\n    for (let i = 0, l = $fields.length; i < l; i++) {\n      let $this = $($fields[i]);\n\n      if ($this.parent().parent().hasClass('metabox-prefs') || !$this.is(':visible')) {\n        continue;\n      }\n\n      let data = $this.data();\n      let $field = $('[name=\"' + data.propertyId + '\"]');\n\n      if (!$field.length) {\n        $field = $('[name=\"' + data.propertyId + '[]\"]').first();\n      }\n\n      if ($field.val() === undefined || !$field.val().length) {\n        $errors.push($fields[i]);\n      }\n    }\n\n    if ($errors.length) {\n      e.preventDefault();\n\n      $spinner.hide();\n      $button.removeClass('button-primary-disabled');\n      $('#message').remove();\n\n      let items = '';\n\n      for (let i = 0, l = $errors.length; i < l; i++) {\n        let $field = $($errors[i]);\n        let data = $field.data();\n        items += '<a class=\"papi-rq-link\" href=\"#' + data.propertyId + '\">' + data.propertyName + '</a>';\n\n        if (i + 1 !== $errors.length) {\n          items += ', ';\n        }\n\n        $('.wrap h1').after('<div id=\"message\" class=\"error below-h2\"><p>' + window.papiL10n.requiredError + ' ' + items + '</p></div>');\n      }\n    }\n  }\n}\n\nexport default Required;\n"
  },
  {
    "path": "src/assets/js/rules.js",
    "content": "import $ from 'jquery';\nimport Utils from 'utils';\n\nclass Rules {\n  /**\n   * Initialize Papi rules class.\n   */\n  static init () {\n    new Rules().binds();\n  }\n\n  /**\n   * Bind rule.\n   *\n   * @param {string} slug\n   * @param {object} rule\n   */\n  bindRule (slug, rule) {\n    const ruleSlug = this.getRuleSlug(slug, rule);\n    const $target = this.getTarget(slug);\n    const selector = this.getSelector(ruleSlug);\n    const self = this;\n\n    $('body').on('keyup change', selector, function (e) {\n      const source = $('[data-papi-rule-source-slug=\"' + slug + '\"]').text();\n      let values = {};\n\n      if (!source.length) {\n        return;\n      }\n\n      const rules = $.parseJSON(source);\n\n      for (let key in rules) {\n        let rule = rules[key];\n\n        if ($.type(rule) !== 'object') {\n          continue;\n        }\n\n        values[key] = rule;\n        values[key].slug = ruleSlug;\n\n        if (values[key].source == null) {\n          values[key].source = self.getValue(rule.slug);\n        }\n      }\n\n      let attr = {\n        rules: values,\n        slug: slug,\n        $target: $target\n      };\n\n      if (e.type === 'change') {\n        self.display(attr);\n      } else {\n        self.debounce(function () {\n          self.display(attr);\n        }, 200);\n      }\n    });\n  }\n\n  /**\n   * Bind elements with functions.\n   */\n  binds () {\n    const self = this;\n    $('body').on('init', '[data-papi-rules=\"true\"]', function () {\n      $('[data-papi-rules=\"true\"]').each(function () {\n        self.setupRules($(this));\n      });\n    });\n    $('[data-papi-rules=\"true\"]').trigger('init');\n  }\n\n  /**\n   * Debounce function after\n   * given wait time.\n   *\n   * @param {funciton} fn\n   * @param {int} wait\n   * @param {bool} immediate\n   */\n  debounce (fn, wait, immediate) {\n    let timeout;\n    (function () {\n      const context = this;\n      const args = arguments;\n      const later = function () {\n        timeout = null;\n        if (!immediate) {\n          fn.apply(context, args);\n        }\n      };\n      const callNow = immediate && !timeout;\n\n      clearTimeout(timeout);\n      timeout = setTimeout(later, wait);\n\n      if (callNow) {\n        fn.apply(context, args);\n      }\n    })();\n  }\n\n  /**\n   * Display field or hide.\n   *\n   * @param {object} options\n   */\n  display (options) {\n    this.fetch(options, function (res) {\n      options.$target.closest('.papi-rules-exists')[res.render ? 'removeClass' : 'addClass']('papi-hide');\n    });\n  }\n\n  /**\n   * Fetch rule result from Papi ajax.\n   *\n   * @param {object} options\n   * @param {function} callback\n   */\n  fetch (options, callback) {\n    const params = {\n      'action': 'get_rules_result',\n      'entry_type': this.getPageTypeId(),\n      'post': $('#post_ID').val()\n    };\n    const data = {\n      'rules': options.rules,\n      'slug': options.$target.attr('name')\n    };\n\n    $.ajax({\n      type: 'POST',\n      data: {\n        data: JSON.stringify(data)\n      },\n      dataType: 'json',\n      url: papi.ajaxUrl + '?' + $.param(params)\n    }).success(callback);\n  }\n\n  /**\n   * Get Page Type id.\n   *\n   * @return {string}\n   */\n  getPageTypeId () {\n    let pageType = Utils.getParameterByName('page_type');\n\n    if (!pageType || !pageType.length) {\n      pageType = $('[data-papi-page-type-key=\"true\"]').val();\n    }\n\n    if (!pageType || !pageType.length) {\n      pageType = Utils.getParameterByName('page');\n    }\n\n    return pageType;\n  }\n\n  /**\n   * Get rule slug.\n   *\n   * @param {string} slug\n   * @param {object} rule\n   *\n   * @return string\n   */\n  getRuleSlug (slug, rule) {\n    const arrReg = /\\[\\d+\\](\\[\\w+\\])$/;\n    const arrReg2 = /^\\w+\\[(\\w+)\\]$/;\n    const papiReg = /^papi_/;\n\n    if (rule.slug.indexOf('.') !== -1) {\n      const parts = rule.slug.split('.');\n\n      if (parts.length > 1) {\n        rule.slug = parts[0] + '][' + parts.slice(1).join('][');\n      } else {\n        rule.slug = parts[0];\n      }\n    }\n\n    if (arrReg.test(slug)) {\n      slug = slug.replace(arrReg.exec(slug)[1], '[' + rule.slug.replace(papiReg, '') + ']');\n\n      if ($('[name=\"' + slug + '\"]').length) {\n        rule.slug = slug;\n      }\n    } else if (arrReg2.test(slug)) {\n      slug = slug.replace(arrReg2.exec(slug)[1], rule.slug.replace('papi_', ''));\n\n      if ($('[name=\"' + slug + '\"]').length) {\n        rule.slug = slug;\n      }\n    }\n\n    return rule.slug;\n  }\n\n  /**\n   * Get selector for rule slug.\n   *\n   * @param {string} ruleSlug\n   *\n   * @return {string}\n   */\n  getSelector (ruleSlug) {\n    return '[name=\"' + ruleSlug + '[]\"], [name=\"' + ruleSlug + '\"], [data-papi-rule=\"' + ruleSlug + '\"]';\n  }\n\n  /**\n   * Get property target.\n   *\n   * @param {string} slug\n   *\n   * @return {object}\n   */\n  getTarget (slug) {\n    let $target = $('[name=\"' + slug + '\"]');\n\n    if (!$target.length && slug.substr(-1) !== ']') {\n      $target = $('[name=\"' + slug + '[]\"]');\n    }\n\n    if (!$target.length) {\n      $target = $('[data-papi-rule=\"' + slug + '\"]');\n      $target.attr('name', slug);\n    }\n\n    return $target;\n  }\n\n  /**\n   * Get value from field.\n   *\n   * @param {string} slug\n   */\n  getValue (slug) {\n    const selector = this.getSelector(slug);\n    let $prop = $('[name=\"' + slug + '[]\"]');\n    let val;\n\n    if ($prop.length) {\n      val = [];\n    } else {\n      $prop = $(selector).filter(function () {\n        let $this = $(this);\n        if ($this.attr('type') === 'hidden') {\n          return false;\n        }\n        return $this.data('papi-rule-value') || $(this).val() !== '';\n      });\n\n      if (!$prop.length) {\n        $prop = $(selector).filter(function () {\n          let $this = $(this);\n          return $this.attr('type') === 'hidden' ||\n            $this.data('papi-rule-value') ||\n            $(this).val() !== '';\n        });\n      }\n    }\n\n    if (!$prop.length) {\n      return;\n    }\n\n    $prop.each(function () {\n      let $this = $(this);\n\n      switch ($this.attr('type')) {\n        case 'checkbox':\n        case 'radio':\n          if (!$this.is(':checked')) {\n            return;\n          }\n          break;\n        default:\n          break;\n      }\n\n      if (val == null || val instanceof Array) {\n        let prv = $this.data('papi-rule-value');\n        if (prv !== undefined) {\n          if (val instanceof Array) {\n            val.push(prv);\n          } else {\n            val = prv;\n          }\n        } else {\n          let v = $this.val();\n          if (v !== '') {\n            if (val instanceof Array) {\n              val.push(v);\n            } else {\n              val = v;\n            }\n          }\n        }\n      }\n    });\n\n    return val;\n  }\n\n  /**\n   * Setup rules.\n   *\n   * @param {object} $this\n   */\n  setupRules ($this) {\n    const rules = $.parseJSON($this.text());\n    let slug = $this.data('papi-rule-source-slug');\n\n    for (let key in rules) {\n      let rule = rules[key];\n\n      if ($.type(rule) !== 'object') {\n        continue;\n      }\n\n      this.bindRule(slug, rule, rules);\n    }\n\n    $this.removeAttr('data-papi-rules');\n  }\n}\n\nexport default Rules;\n"
  },
  {
    "path": "src/assets/js/tabs.js",
    "content": "import $ from 'jquery';\n\nclass Tabs {\n  /**\n   * Initialize Papi tabs class.\n   */\n  static init () {\n    new Tabs().binds();\n  }\n\n  /**\n   * Bind elements with functions.\n   */\n  binds () {\n    $('a[data-papi-tab]').on('click', this.changeTab.bind(this));\n    this.updateTabsTableBack($('ul.papi-tabs li.active'));\n  }\n\n  /**\n   * Change tab.\n   *\n   * @param {object} e\n   */\n  changeTab (e) {\n    e.preventDefault();\n\n    const $this = $(e.currentTarget);\n    const $parent = $this.parent();\n    const tab = $this.data().papiTab;\n\n    $('a[data-papi-tab]').parent().removeClass('active');\n    $parent.addClass('active');\n\n    $('div[data-papi-tab]').removeClass('active').addClass('papi-hide');\n    const $tabContent = $('div[data-papi-tab=\"' + tab + '\"]').addClass('active').removeClass('papi-hide');\n\n    const forceUpdate = $('.papi-tabs-content').height() < $('.papi-tabs').height() &&\n      !$tabContent.find('tr').last().find('.papi-table-sidebar').length;\n\n    this.updateTabsTableBack($parent, forceUpdate);\n  }\n\n  /**\n   * Update tabs table back css class.\n   *\n   * @param {object} $activeTab\n   * @param {bool} addClass\n   */\n  updateTabsTableBack ($activeTab, addClass = false) {\n    const $tabsTableBack = $activeTab.closest('.papi-tabs-wrapper').find('.papi-tabs-table-back');\n    $tabsTableBack[$activeTab.hasClass('white-tab') || addClass ? 'addClass' : 'removeClass']('white-tab');\n  }\n}\n\nexport default Tabs;\n"
  },
  {
    "path": "src/assets/js/taxonomy.js",
    "content": "import $ from 'jquery';\n\nclass Taxonomy {\n  /**\n   * Initialize Papi taxonomy class.\n   */\n  static init () {\n    new Taxonomy().binds();\n  }\n\n  /**\n   * Bind elements with functions.\n   */\n  binds () {\n    $('#submit').on('click', this.addNewTerm.bind(this));\n  }\n\n  /**\n   * Redirect if a new term is added and redirect is activated.\n   *\n   * @param {object} e\n   */\n  addNewTerm (e) {\n    const $title = $('#tag-name');\n    const title = $title.val();\n    const $pageType = $('[data-papi-page-type-key=\"true\"]');\n\n    if (!$pageType.data('redirect') && !$pageType.find(':selected').data('redirect')) {\n      return;\n    }\n\n    if (!title.length) {\n      return;\n    }\n\n    e.preventDefault();\n\n    let interval;\n\n    interval = setInterval(() => {\n      if ($('#ajax-response').children().length) {\n        clearInterval(interval);\n        return;\n      }\n\n      const $thelist = $('#the-list');\n      const $rowTitles = $thelist.find('td.column-name a.row-title');\n      const $rows = $rowTitles.contents().filter(function () {\n        return $(this).text().trim() === title.trim();\n      });\n\n      if ($rows.length) {\n        clearInterval(interval);\n        window.location = $rows[0].parentElement.href;\n      }\n    }, 500);\n  }\n}\n\nexport default Taxonomy;\n"
  },
  {
    "path": "src/assets/js/utils.js",
    "content": "import $ from 'jquery';\n\nconst wp = window.wp;\n\nclass Utils {\n  /**\n   * Initialize Papi core class.\n   */\n  static init () {\n    new Utils().binds();\n  }\n\n  /**\n   * Get meta type.\n   *\n   * @return {string}\n   */\n  static getMetaType () {\n    const $body = $('body');\n\n    if ($body.hasClass('papi-meta-type-term')) {\n      return 'term';\n    }\n\n    if ($body.hasClass('papi-meta-type-post')) {\n      return 'post';\n    }\n\n    if (/page=.*papi(?:%2F|\\/)option/.test(window.location.search)) {\n      return 'option';\n    }\n  }\n\n  /**\n   * Get meta type key.\n   *\n   * @return {string}\n   */\n  static getMetaTypeKey () {\n    switch (Utils.getMetaType()) {\n      case 'post':\n        return 'post_type';\n      case 'term':\n        return 'taxonomy';\n      default:\n        break;\n    }\n  }\n\n  /**\n   * Get meta type value.\n   *\n   * @return {string}\n   */\n  static getMetaTypeValue () {\n    switch (Utils.getMetaType()) {\n      case 'post':\n        const value = Utils.getParameterByName('post_type');\n\n        if (value.length) {\n          return value;\n        }\n\n        return $('#post_type').val();\n      case 'term':\n        return Utils.getParameterByName('taxonomy');\n      default:\n        break;\n    }\n  }\n\n  /**\n   * Get parameter by name.\n   *\n   * @param {string} name\n   *\n   * @return {mixed}\n   */\n  static getParameterByName (name) {\n    name = name.replace(/[[]/, '\\\\[').replace(/[\\]]/, '\\\\]');\n    const regex = new RegExp('[\\\\?&]' + name + '=([^&#]*)');\n    const results = regex.exec(window.location.search);\n    return results === null ? '' : decodeURIComponent(results[1].replace(/\\+/g, ' '));\n  }\n\n  /**\n   * Open WordPress media editor.\n   *\n   * @param {object} options\n   */\n  static wpMediaEditor (options) {\n    if (Utils.wpMediaFrame !== undefined) {\n      Utils.wpMediaFrame.dispose();\n    }\n\n    Utils.wpMediaFrame = wp.media(options)\n      .on('select', () => {\n        const attachments = Utils.wpMediaFrame.state().get('selection').toJSON();\n        for (let i = 0, l = attachments.length; i < l; i++) {\n          if (attachments[i] === null) {\n            continue;\n          }\n\n          Utils.wpMediaFrame.trigger('insert', attachments[i]);\n        }\n      });\n\n    return Utils.wpMediaFrame;\n  }\n\n  /**\n   * Get media frame.\n   *\n   * @return {object}\n   */\n  static get wpMediaFrame () {\n    return this._wpMediaFrame;\n  }\n\n  /**\n   * Set media frame.\n   *\n   * @param {object} obj\n   */\n  static set wpMediaFrame (obj) {\n    this._wpMediaFrame = obj;\n  }\n}\n\nexport default Utils;\n"
  },
  {
    "path": "src/assets/scss/components/pikaday.scss",
    "content": ".papi-body {\n\n  /*!\n   * Pikaday\n   * Copyright © 2014 David Bushell | BSD & MIT license | http://dbushell.com/\n   */\n\n  .pika-single {\n    z-index: 9999;\n    display: block;\n    position: relative;\n    color: #333;\n    background: #fff;\n    border: 1px solid #ccc;\n    border-bottom-color: #bbb;\n    font-family: \"Helvetica Neue\", Helvetica, Arial, sans-serif;\n  }\n\n  /*\n  clear child float (pika-lendar), using the famous micro clearfix hack\n  http://nicolasgallagher.com/micro-clearfix-hack/\n  */\n  .pika-single:before,\n  .pika-single:after {\n    content: \" \";\n    display: table;\n  }\n\n  .pika-single:after {\n    clear: both\n  }\n\n  .pika-single {\n    *zoom: 1\n  }\n\n  .pika-single.is-hidden {\n    display: none;\n  }\n\n  .pika-single.is-bound {\n    position: absolute;\n    box-shadow: 0 5px 15px -5px rgba(0, 0, 0, .5);\n  }\n\n  .pika-lendar {\n    float: left;\n    width: 240px;\n    margin: 8px;\n  }\n\n  .pika-title {\n    position: relative;\n    text-align: center;\n  }\n\n  .pika-label {\n    display: inline-block;\n    *display: inline;\n    position: relative;\n    z-index: 9999;\n    overflow: hidden;\n    margin: 0;\n    padding: 5px 3px;\n    font-size: 14px;\n    line-height: 20px;\n    font-weight: bold;\n    background-color: #fff;\n  }\n\n  .pika-title select {\n    cursor: pointer;\n    position: absolute;\n    z-index: 9998;\n    margin: 0;\n    left: 0;\n    top: 5px;\n    filter: alpha(opacity=0);\n    opacity: 0;\n  }\n\n  .pika-prev,\n  .pika-next {\n    display: block;\n    cursor: pointer;\n    position: relative;\n    outline: none;\n    border: 0;\n    padding: 0;\n    width: 20px;\n    height: 30px;\n    color: rgba(0, 0, 0, 0);\n    white-space: nowrap;\n    overflow: hidden;\n    background-color: transparent;\n    background-position: center center;\n    background-repeat: no-repeat;\n    background-size: 75% 75%;\n    opacity: .5;\n    *position: absolute;\n    *top: 0;\n  }\n\n  .pika-prev:hover,\n  .pika-next:hover {\n    opacity: 1;\n  }\n\n  .pika-prev,\n  .is-rtl .pika-next {\n    float: left;\n    background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAeCAYAAAAsEj5rAAAAUklEQVR42u3VMQoAIBADQf8Pgj+OD9hG2CtONJB2ymQkKe0HbwAP0xucDiQWARITIDEBEnMgMQ8S8+AqBIl6kKgHiXqQqAeJepBo/z38J/U0uAHlaBkBl9I4GwAAAABJRU5ErkJggg==');\n    *left: 0;\n  }\n\n  .pika-next,\n  .is-rtl .pika-prev {\n    float: right;\n    background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAeCAYAAAAsEj5rAAAAU0lEQVR42u3VOwoAMAgE0dwfAnNjU26bYkBCFGwfiL9VVWoO+BJ4Gf3gtsEKKoFBNTCoCAYVwaAiGNQGMUHMkjGbgjk2mIONuXo0nC8XnCf1JXgArVIZAQh5TKYAAAAASUVORK5CYII=');\n    *right: 0;\n  }\n\n  .pika-prev.is-disabled,\n  .pika-next.is-disabled {\n    cursor: default;\n    opacity: .2;\n  }\n\n  .pika-select {\n    display: inline-block;\n    *display: inline;\n  }\n\n  .pika-table {\n    width: 100%;\n    border-collapse: collapse;\n    border-spacing: 0;\n    border: 0;\n  }\n\n  .pika-table th,\n  .pika-table td {\n    width: 14.285714285714286%;\n    padding: 0;\n  }\n\n  .pika-table th {\n    color: #0073aa;\n    font-size: 12px;\n    line-height: 25px;\n    font-weight: normal;\n    text-align: center;\n  }\n\n  .pika-button {\n    cursor: pointer;\n    display: block;\n    box-sizing: border-box;\n    -moz-box-sizing: border-box;\n    outline: none;\n    border: 0;\n    margin: 0;\n    width: 100%;\n    padding: 5px;\n    color: #333;\n    font-size: 12px;\n    line-height: 15px;\n    text-align: right;\n    background: #f1f1f1;\n  }\n\n  .pika-week {\n    font-size: 11px;\n    color: #999;\n  }\n\n  .is-today .pika-button {\n    color: #0073aa;\n    font-weight: bold;\n  }\n\n  .is-selected .pika-button {\n    color: #fff;\n    font-weight: bold;\n    background: #0073aa;\n  }\n\n  .is-disabled .pika-button {\n    pointer-events: none;\n    cursor: default;\n    color: #999;\n    opacity: .3;\n  }\n\n  .pika-button:hover {\n    color: #fff !important;\n    background: #0073aa !important;\n  }\n\n  /* styling for abbr */\n  .pika-table abbr {\n    border-bottom: none;\n    cursor: help;\n  }\n\n  .pika-time-container {\n    clear: both;\n  }\n\n  .pika-time {\n    margin: 7px auto 7px;\n  }\n\n}\n"
  },
  {
    "path": "src/assets/scss/components/select2.scss",
    "content": ".papi-body {\n  .select2-container {\n    box-sizing: border-box;\n    display: inline-block;\n    margin: 0;\n    position: relative;\n    vertical-align: middle;\n  }\n  .select2-container .select2-selection--single {\n    box-sizing: border-box;\n    cursor: pointer;\n    display: block;\n    height: 28px;\n    user-select: none;\n    -webkit-user-select: none;\n  }\n  .select2-container .select2-selection--single .select2-selection__rendered {\n    display: block;\n    padding-left: 8px;\n    padding-right: 20px;\n    overflow: hidden;\n    text-overflow: ellipsis;\n    white-space: nowrap;\n  }\n  .select2-container .select2-selection--single .select2-selection__clear {\n    position: relative;\n  }\n  .select2-container[dir=\"rtl\"] .select2-selection--single .select2-selection__rendered {\n    padding-right: 8px;\n    padding-left: 20px;\n  }\n  .select2-container .select2-selection--multiple {\n    box-sizing: border-box;\n    cursor: pointer;\n    display: block;\n    min-height: 32px;\n    user-select: none;\n    -webkit-user-select: none;\n  }\n  .select2-container .select2-selection--multiple .select2-selection__rendered {\n    display: inline-block;\n    overflow: hidden;\n    padding-left: 8px;\n    text-overflow: ellipsis;\n    white-space: nowrap;\n  }\n  .select2-container .select2-search--inline {\n    float: left;\n  }\n  .select2-container .select2-search--inline .select2-search__field {\n    box-sizing: border-box;\n    border: none;\n    font-size: 100%;\n    margin-top: 5px;\n    padding: 0;\n  }\n  .select2-container .select2-search--inline .select2-search__field::-webkit-search-cancel-button {\n    -webkit-appearance: none;\n  }\n\n  .select2-dropdown {\n    background-color: white;\n    border: 1px solid #aaa;\n    border-radius: 4px;\n    box-sizing: border-box;\n    display: block;\n    position: absolute;\n    left: -100000px;\n    width: 100%;\n    z-index: 1051;\n  }\n\n  .select2-results {\n    display: block;\n  }\n\n  .select2-results__options {\n    list-style: none;\n    margin: 0;\n    padding: 0;\n  }\n\n  .select2-results__option {\n    padding: 6px;\n    user-select: none;\n    -webkit-user-select: none;\n  }\n  .select2-results__option[aria-selected] {\n    cursor: pointer;\n  }\n\n  .select2-container--open .select2-dropdown {\n    left: 0;\n  }\n\n  .select2-container--open .select2-dropdown--above {\n    border-bottom: none;\n    border-bottom-left-radius: 0;\n    border-bottom-right-radius: 0;\n  }\n\n  .select2-container--open .select2-dropdown--below {\n    border-top: none;\n    border-top-left-radius: 0;\n    border-top-right-radius: 0;\n  }\n\n  .select2-search--dropdown {\n    display: block;\n    padding: 4px;\n  }\n  .select2-search--dropdown .select2-search__field {\n    padding: 4px;\n    width: 100%;\n    box-sizing: border-box;\n  }\n  .select2-search--dropdown .select2-search__field::-webkit-search-cancel-button {\n    -webkit-appearance: none;\n  }\n  .select2-search--dropdown.select2-search--hide {\n    display: none;\n  }\n\n  .select2-close-mask {\n    border: 0;\n    margin: 0;\n    padding: 0;\n    display: block;\n    position: fixed;\n    left: 0;\n    top: 0;\n    min-height: 100%;\n    min-width: 100%;\n    height: auto;\n    width: auto;\n    opacity: 0;\n    z-index: 99;\n    background-color: #fff;\n    filter: alpha(opacity=0);\n  }\n\n  .select2-hidden-accessible {\n    border: 0 !important;\n    clip: rect(0 0 0 0) !important;\n    height: 1px !important;\n    margin: -1px !important;\n    overflow: hidden !important;\n    padding: 0 !important;\n    position: absolute !important;\n    width: 1px !important;\n  }\n\n  .select2-container--default .select2-selection--single {\n    background-color: #fff;\n    border: 1px solid #aaa;\n    border-radius: 4px;\n  }\n  .select2-container--default .select2-selection--single .select2-selection__rendered {\n    color: #444;\n    line-height: 28px;\n  }\n  .select2-container--default .select2-selection--single .select2-selection__clear {\n    cursor: pointer;\n    float: right;\n    font-weight: bold;\n  }\n  .select2-container--default .select2-selection--single .select2-selection__placeholder {\n    color: #999;\n  }\n  .select2-container--default .select2-selection--single .select2-selection__arrow {\n    height: 26px;\n    position: absolute;\n    top: 1px;\n    right: 1px;\n    width: 20px;\n  }\n  .select2-container--default .select2-selection--single .select2-selection__arrow b {\n    border-color: #888 transparent transparent transparent;\n    border-style: solid;\n    border-width: 5px 4px 0 4px;\n    height: 0;\n    left: 50%;\n    margin-left: -4px;\n    margin-top: -2px;\n    position: absolute;\n    top: 50%;\n    width: 0;\n  }\n\n  .select2-container--default[dir=\"rtl\"] .select2-selection--single .select2-selection__clear {\n    float: left;\n  }\n\n  .select2-container--default[dir=\"rtl\"] .select2-selection--single .select2-selection__arrow {\n    left: 1px;\n    right: auto;\n  }\n\n  .select2-container--default.select2-container--disabled .select2-selection--single {\n    background-color: #eee;\n    cursor: default;\n  }\n  .select2-container--default.select2-container--disabled .select2-selection--single .select2-selection__clear {\n    display: none;\n  }\n\n  .select2-container--default.select2-container--open .select2-selection--single .select2-selection__arrow b {\n    border-color: transparent transparent #888 transparent;\n    border-width: 0 4px 5px 4px;\n  }\n\n  .select2-container--default .select2-selection--multiple {\n    background-color: white;\n    border: 1px solid #aaa;\n    border-radius: 4px;\n    cursor: text;\n  }\n  .select2-container--default .select2-selection--multiple .select2-selection__rendered {\n    box-sizing: border-box;\n    list-style: none;\n    margin: 0;\n    padding: 0 5px;\n    width: 100%;\n  }\n  .select2-container--default .select2-selection--multiple .select2-selection__rendered li {\n    list-style: none;\n  }\n  .select2-container--default .select2-selection--multiple .select2-selection__placeholder {\n    color: #999;\n    margin-top: 5px;\n    float: left;\n  }\n  .select2-container--default .select2-selection--multiple .select2-selection__clear {\n    cursor: pointer;\n    float: right;\n    font-weight: bold;\n    margin-top: 5px;\n    margin-right: 10px;\n  }\n  .select2-container--default .select2-selection--multiple .select2-selection__choice {\n    background-color: #e4e4e4;\n    border: 1px solid #aaa;\n    border-radius: 4px;\n    cursor: default;\n    float: left;\n    margin-right: 5px;\n    margin-top: 5px;\n    padding: 0 5px;\n  }\n  .select2-container--default .select2-selection--multiple .select2-selection__choice__remove {\n    color: #999;\n    cursor: pointer;\n    display: inline-block;\n    font-weight: bold;\n    margin-right: 2px;\n  }\n  .select2-container--default .select2-selection--multiple .select2-selection__choice__remove:hover {\n    color: #333;\n  }\n\n  .select2-container--default[dir=\"rtl\"] .select2-selection--multiple .select2-selection__choice, .select2-container--default[dir=\"rtl\"] .select2-selection--multiple .select2-selection__placeholder, .select2-container--default[dir=\"rtl\"] .select2-selection--multiple .select2-search--inline {\n    float: right;\n  }\n\n  .select2-container--default[dir=\"rtl\"] .select2-selection--multiple .select2-selection__choice {\n    margin-left: 5px;\n    margin-right: auto;\n  }\n\n  .select2-container--default[dir=\"rtl\"] .select2-selection--multiple .select2-selection__choice__remove {\n    margin-left: 2px;\n    margin-right: auto;\n  }\n\n  .select2-container--default.select2-container--focus .select2-selection--multiple {\n    border: solid #aaa 1px;\n    outline: 0;\n  }\n\n  .select2-container--default.select2-container--disabled .select2-selection--multiple {\n    background-color: #eee;\n    cursor: default;\n  }\n\n  .select2-container--default.select2-container--disabled .select2-selection__choice__remove {\n    display: none;\n  }\n\n  .select2-container--default.select2-container--open.select2-container--above .select2-selection--single, .select2-container--default.select2-container--open.select2-container--above .select2-selection--multiple {\n    border-top-left-radius: 0;\n    border-top-right-radius: 0;\n  }\n\n  .select2-container--default.select2-container--open.select2-container--below .select2-selection--single, .select2-container--default.select2-container--open.select2-container--below .select2-selection--multiple {\n    border-bottom-left-radius: 0;\n    border-bottom-right-radius: 0;\n  }\n\n  .select2-container--default .select2-search--dropdown .select2-search__field {\n    border: 1px solid #aaa;\n  }\n\n  .select2-container--default .select2-search--inline .select2-search__field {\n    background: transparent;\n    border: none;\n    outline: 0;\n    box-shadow: none;\n    -webkit-appearance: textfield;\n  }\n\n  .select2-container--default .select2-results > .select2-results__options {\n    max-height: 200px;\n    overflow-y: auto;\n  }\n\n  .select2-container--default .select2-results__option[role=group] {\n    padding: 0;\n  }\n\n  .select2-container--default .select2-results__option[aria-disabled=true] {\n    color: #999;\n  }\n\n  .select2-container--default .select2-results__option[aria-selected=true] {\n    background-color: #f5f5f5;\n  }\n\n  .select2-container--default .select2-results__option .select2-results__option {\n    padding-left: 1em;\n  }\n  .select2-container--default .select2-results__option .select2-results__option .select2-results__group {\n    padding-left: 0;\n  }\n  .select2-container--default .select2-results__option .select2-results__option .select2-results__option {\n    margin-left: -1em;\n    padding-left: 2em;\n  }\n  .select2-container--default .select2-results__option .select2-results__option .select2-results__option .select2-results__option {\n    margin-left: -2em;\n    padding-left: 3em;\n  }\n  .select2-container--default .select2-results__option .select2-results__option .select2-results__option .select2-results__option .select2-results__option {\n    margin-left: -3em;\n    padding-left: 4em;\n  }\n  .select2-container--default .select2-results__option .select2-results__option .select2-results__option .select2-results__option .select2-results__option .select2-results__option {\n    margin-left: -4em;\n    padding-left: 5em;\n  }\n  .select2-container--default .select2-results__option .select2-results__option .select2-results__option .select2-results__option .select2-results__option .select2-results__option .select2-results__option {\n    margin-left: -5em;\n    padding-left: 6em;\n  }\n\n  .select2-container--default .select2-results__option--highlighted[aria-selected] {\n    background-color: #0073aa;\n    color: white;\n  }\n\n  .select2-container--default .select2-results__group {\n    cursor: default;\n    display: block;\n    padding: 6px;\n  }\n\n  .select2-container--classic .select2-selection--single {\n    background-color: #f6f6f6;\n    border: 1px solid #aaa;\n    border-radius: 4px;\n    outline: 0;\n    background-image: -webkit-linear-gradient(top, white 50%, #eeeeee 100%);\n    background-image: -o-linear-gradient(top, white 50%, #eeeeee 100%);\n    background-image: linear-gradient(to bottom, white 50%, #eeeeee 100%);\n    background-repeat: repeat-x;\n    filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#FFFFFFFF', endColorstr='#FFEEEEEE', GradientType=0);\n  }\n  .select2-container--classic .select2-selection--single:focus {\n    border: 1px solid #0073aa;\n  }\n  .select2-container--classic .select2-selection--single .select2-selection__rendered {\n    color: #444;\n    line-height: 28px;\n  }\n  .select2-container--classic .select2-selection--single .select2-selection__clear {\n    cursor: pointer;\n    float: right;\n    font-weight: bold;\n    margin-right: 10px;\n  }\n  .select2-container--classic .select2-selection--single .select2-selection__placeholder {\n    color: #999;\n  }\n  .select2-container--classic .select2-selection--single .select2-selection__arrow {\n    background-color: #f5f5f5;\n    border: none;\n    border-left: 1px solid #aaa;\n    border-top-right-radius: 4px;\n    border-bottom-right-radius: 4px;\n    height: 26px;\n    position: absolute;\n    top: 1px;\n    right: 1px;\n    width: 20px;\n    background-image: -webkit-linear-gradient(top, #eeeeee 50%, #cccccc 100%);\n    background-image: -o-linear-gradient(top, #eeeeee 50%, #cccccc 100%);\n    background-image: linear-gradient(to bottom, #eeeeee 50%, #cccccc 100%);\n    background-repeat: repeat-x;\n    filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#FFEEEEEE', endColorstr='#FFCCCCCC', GradientType=0);\n  }\n  .select2-container--classic .select2-selection--single .select2-selection__arrow b {\n    border-color: #888 transparent transparent transparent;\n    border-style: solid;\n    border-width: 5px 4px 0 4px;\n    height: 0;\n    left: 50%;\n    margin-left: -4px;\n    margin-top: -2px;\n    position: absolute;\n    top: 50%;\n    width: 0;\n  }\n\n  .select2-container--classic[dir=\"rtl\"] .select2-selection--single .select2-selection__clear {\n    float: left;\n  }\n\n  .select2-container--classic[dir=\"rtl\"] .select2-selection--single .select2-selection__arrow {\n    border: none;\n    border-right: 1px solid #aaa;\n    border-radius: 0;\n    border-top-left-radius: 4px;\n    border-bottom-left-radius: 4px;\n    left: 1px;\n    right: auto;\n  }\n\n  .select2-container--classic.select2-container--open .select2-selection--single {\n    border: 1px solid #0073aa;\n  }\n  .select2-container--classic.select2-container--open .select2-selection--single .select2-selection__arrow {\n    background: transparent;\n    border: none;\n  }\n  .select2-container--classic.select2-container--open .select2-selection--single .select2-selection__arrow b {\n    border-color: transparent transparent #888 transparent;\n    border-width: 0 4px 5px 4px;\n  }\n\n  .select2-container--classic.select2-container--open.select2-container--above .select2-selection--single {\n    border-top: none;\n    border-top-left-radius: 0;\n    border-top-right-radius: 0;\n    background-image: -webkit-linear-gradient(top, white 0%, #eeeeee 50%);\n    background-image: -o-linear-gradient(top, white 0%, #eeeeee 50%);\n    background-image: linear-gradient(to bottom, white 0%, #eeeeee 50%);\n    background-repeat: repeat-x;\n    filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#FFFFFFFF', endColorstr='#FFEEEEEE', GradientType=0);\n  }\n\n  .select2-container--classic.select2-container--open.select2-container--below .select2-selection--single {\n    border-bottom: none;\n    border-bottom-left-radius: 0;\n    border-bottom-right-radius: 0;\n    background-image: -webkit-linear-gradient(top, #eeeeee 50%, white 100%);\n    background-image: -o-linear-gradient(top, #eeeeee 50%, white 100%);\n    background-image: linear-gradient(to bottom, #eeeeee 50%, white 100%);\n    background-repeat: repeat-x;\n    filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#FFEEEEEE', endColorstr='#FFFFFFFF', GradientType=0);\n  }\n\n  .select2-container--classic .select2-selection--multiple {\n    background-color: white;\n    border: 1px solid #aaa;\n    border-radius: 4px;\n    cursor: text;\n    outline: 0;\n  }\n  .select2-container--classic .select2-selection--multiple:focus {\n    border: 1px solid #0073aa;\n  }\n  .select2-container--classic .select2-selection--multiple .select2-selection__rendered {\n    list-style: none;\n    margin: 0;\n    padding: 0 5px;\n  }\n  .select2-container--classic .select2-selection--multiple .select2-selection__clear {\n    display: none;\n  }\n  .select2-container--classic .select2-selection--multiple .select2-selection__choice {\n    background-color: #e4e4e4;\n    border: 1px solid #aaa;\n    border-radius: 4px;\n    cursor: default;\n    float: left;\n    margin-right: 5px;\n    margin-top: 5px;\n    padding: 0 5px;\n  }\n  .select2-container--classic .select2-selection--multiple .select2-selection__choice__remove {\n    color: #888;\n    cursor: pointer;\n    display: inline-block;\n    font-weight: bold;\n    margin-right: 2px;\n  }\n  .select2-container--classic .select2-selection--multiple .select2-selection__choice__remove:hover {\n    color: #555;\n  }\n\n  .select2-container--classic[dir=\"rtl\"] .select2-selection--multiple .select2-selection__choice {\n    float: right;\n  }\n\n  .select2-container--classic[dir=\"rtl\"] .select2-selection--multiple .select2-selection__choice {\n    margin-left: 5px;\n    margin-right: auto;\n  }\n\n  .select2-container--classic[dir=\"rtl\"] .select2-selection--multiple .select2-selection__choice__remove {\n    margin-left: 2px;\n    margin-right: auto;\n  }\n\n  .select2-container--classic.select2-container--open .select2-selection--multiple {\n    border: 1px solid #0073aa;\n  }\n\n  .select2-container--classic.select2-container--open.select2-container--above .select2-selection--multiple {\n    border-top: none;\n    border-top-left-radius: 0;\n    border-top-right-radius: 0;\n  }\n\n  .select2-container--classic.select2-container--open.select2-container--below .select2-selection--multiple {\n    border-bottom: none;\n    border-bottom-left-radius: 0;\n    border-bottom-right-radius: 0;\n  }\n\n  .select2-container--classic .select2-search--dropdown .select2-search__field {\n    border: 1px solid #aaa;\n    outline: 0;\n  }\n\n  .select2-container--classic .select2-search--inline .select2-search__field {\n    outline: 0;\n    box-shadow: none;\n  }\n\n  .select2-container--classic .select2-dropdown {\n    background-color: white;\n    border: 1px solid transparent;\n  }\n\n  .select2-container--classic .select2-dropdown--above {\n    border-bottom: none;\n  }\n\n  .select2-container--classic .select2-dropdown--below {\n    border-top: none;\n  }\n\n  .select2-container--classic .select2-results > .select2-results__options {\n    max-height: 200px;\n    overflow-y: auto;\n  }\n\n  .select2-container--classic .select2-results__option[role=group] {\n    padding: 0;\n  }\n\n  .select2-container--classic .select2-results__option[aria-disabled=true] {\n    color: grey;\n  }\n\n  .select2-container--classic .select2-results__option--highlighted[aria-selected] {\n    background-color: #3875d7;\n    color: white;\n  }\n\n  .select2-container--classic .select2-results__group {\n    cursor: default;\n    display: block;\n    padding: 6px;\n  }\n\n  .select2-container--classic.select2-container--open .select2-dropdown {\n    border-color: #0073aa;\n  }\n}\n"
  },
  {
    "path": "src/assets/scss/modules/_admin.scss",
    "content": "#papi-hidden-editor,\n#wp-papiHiddenEditor-wrap,\nlabel[for=\"papi-hidden-editor-hide\"],\n.papi-iframe-mode .compat-field-papi_meta_nonce,\n.papi-iframe-mode #wpbody-content .wrap > a {\n  display: none !important;\n}\n\n// Hide classes for hiding html\n// elements that can be hidden\n// with a filter or something like that.\n$hide-list: #edit-slug-box #pageparentdiv;\n@each $item in $hide-list {\n  .papi-hide-#{str-slice($item, 2)} #{$item} {\n    display: none;\n  }\n}\n\n// Page type switcher.\n#post-body .papi-page-type-switcher {\n  > span {\n    font-weight: bold;\n  }\n\n  &:before {\n    content: \"\\f105\";\n    font: 400 20px/1 dashicons;\n    speak: none;\n    display: inline-block;\n    padding: 0 2px 0 0;\n    top: 0;\n    left: -1px;\n    position: relative;\n    vertical-align: top;\n    -webkit-font-smoothing: antialiased;\n    -moz-osx-font-smoothing: grayscale;\n    text-decoration: none !important;\n    color: #888;\n  }\n\n  > div {\n    line-height: 2.5em;\n    margin-top: 3px;\n    display: none;\n\n    > select {\n      margin-right: 2px;\n    }\n\n    > a {\n      vertical-align: middle;\n      margin-right: 2px;\n    }\n  }\n}\n\n//\n// Iframe mode, hide menus, admin tab and other things.\n//\n.papi-iframe-mode {\n  #adminmenumain, // admin menu\n  #wpadminbar, // admin bar\n  #wpfooter, // admin footer\n  .wrap > #message.updated > p > a, // view post link\n  .submitdelete.deletion // move to trash link\n  {\n    display: none !important;\n    height: 1px;\n    width: 1px;\n  }\n  #wpcontent {\n    margin-top: -32px;\n    margin-left: 0px !important;\n\n    .wrap h1,\n    #screen-meta,\n    #screen-meta-links,\n    .delete-action {\n      display: none;\n    }\n  }\n}\n"
  },
  {
    "path": "src/assets/scss/modules/_attachment.scss",
    "content": ".papi-media-title {\n  margin-bottom: 5px;\n}\n"
  },
  {
    "path": "src/assets/scss/modules/_base.scss",
    "content": "// Add new page\n.papi-search {\n  font-size: 16px;\n  font-weight: 300;\n  left: 20px;\n  line-height: 1.5;\n  position: relative;\n  top: -2px;\n  width: 280px;\n\n  @media only screen and (max-width: 650px) {\n    clear: both;\n    float: none;\n    left: 0;\n    margin: 10px 0;\n    right: 0;\n    top: 0;\n    width: 100%;\n  }\n}\n\n// Sizes\n.papi-table,\n.papi-fullwidth {\n  width: 100%;\n}\n\n.papi-halfwidth {\n  width: 50%;\n}\n\n.papi-pull-left {\n  float: left;\n}\n\n.papi-pull-right {\n  float: right;\n}\n\n.papi-panel {\n  background: #fff;\n  border: 1px #eaeaea solid;\n  margin: 10px 0 -5px -2px;\n  padding: 10px;\n\n  p {\n    margin: 0;\n  }\n}\n\n// Boxes that exists on the add new page\n.papi-box-list {\n  clear: both;\n  margin-top: 20px;\n\n  @media only screen and (max-width: 480px) {\n    margin-left: 0;\n  }\n\n  .papi-box-item {\n    background: #fff;\n    border: 1px solid #dedede;\n    -webkit-box-shadow: 0 1px 1px -1px rgba(0, 0, 0, .1);\n    box-shadow: 0 1px 1px -1px rgba(0, 0, 0, .1);\n    -moz-box-sizing: border-box;\n    -webkit-box-sizing: border-box;\n    box-sizing: border-box;\n    cursor: pointer;\n    display: block;\n    float: left;\n    margin: 0 2.3% 3% 0;\n    position: relative;\n    width: 22.7%;\n    text-decoration: none;\n\n    @media only screen and (max-width: 1120px) {\n      margin-right: 2%;\n      width: 48%;\n    }\n\n    @media only screen and (max-width: 480px) {\n      margin-right: 0;\n      width: 100%;\n    }\n\n    .papi-page-type-screenshot {\n      background: #eaeaea;\n      background-size: 100%;\n      color: #aaa;\n      display: inline-block;\n      height: 200px;\n      width: 100%;\n\n      div {\n        display: table;\n        height: 200px;\n        width: 100%;\n\n        div {\n          display: table-cell;\n          text-align: center;\n          vertical-align: middle;\n        }\n      }\n    }\n\n    img {\n      border-bottom: 1px solid #dedede;\n      height: auto;\n      left: 0;\n      top: 0;\n      -webkit-transform: translateZ(0);\n      -webkit-transition: opacity .2s ease-in-out;\n      transition: opacity .2s ease-in-out;\n      width: 100%;\n    }\n\n    .papi-page-type-info {\n      margin-top: -4px;\n      max-width: 240px;\n      padding: 15px;\n      position: relative;\n      word-break: break-word;\n\n      h3 {\n        border: 0;\n        color: #23282d;\n        font-weight: 400;\n        margin: 0;\n        margin-bottom: 5px;\n      }\n\n      p {\n        border: 0;\n        color: #777;\n        margin: 0;\n      }\n    }\n\n    .papi-page-type-actions {\n      background-color: #fafafa;\n      border-top: 1px solid #dedede;\n      height: 38px;\n      padding: 9px 10px 0;\n    }\n  }\n}\n"
  },
  {
    "path": "src/assets/scss/modules/_box.scss",
    "content": "// Papi meta box style\n\n.papi-box h3 {\n  font-size: 14px;\n  line-height: 1.4;\n  margin: 0;\n  padding: 8px 12px;\n}\n\n#poststuff .papi-box {\n  .inside {\n    border-top: 1px transparent solid;\n    margin-right: -13px !important;\n    margin-top: 5px;\n    padding-right: 0;\n\n    > .papi-table {\n      margin: -7px -12px -12px;\n    }\n  }\n}\n\n#side-sortables {\n  .papi-box > .papi-table {\n    tr td {\n      float: left;\n      padding: 10px 0;\n      width: 99.5%;\n\n      > label {\n        padding-left: 12px;\n      }\n\n      > input,\n      select,\n      textarea {\n        margin-left: 12px;\n        width: 90.5%;\n      }\n    }\n  }\n}\n"
  },
  {
    "path": "src/assets/scss/modules/_iframe.scss",
    "content": ".papi-iframe-link-open {\n  float: right;\n  margin-right: 10px;\n}\n\n.papi-iframe-modal {\n  position: fixed;\n  top: 30px;\n  left: 30px;\n  right: 30px;\n  bottom: 30px;\n  z-index: 160000;\n  font-family: sans-serif;\n  font-size: 12px;\n  &-header {\n    background: #fff;\n    min-height: 50px;\n    border-bottom: 1px #ddd solid;\n    &-title {\n      h1 {\n        padding: 0 16px;\n        font-size: 22px;\n        line-height: 50px;\n        margin: 0;\n      }\n    }\n    &-close {\n      background-color: transparent;\n      border: 0;\n      border-left: 1px solid #ddd;\n      cursor: pointer;\n      color: #777;\n      float: right;\n      height: 50px;\n      padding: 0;\n      // position: absolute;\n      text-align: center;\n      width: 50px;\n\n      span {\n        &:before {\n          color: #666;\n          content: \"\\f335\";\n          font: 400 22px/1 dashicons;\n          speak: none;\n          vertical-align: middle;\n        }\n      }\n    }\n  }\n  &-content {\n    position: absolute;\n    top: 0;\n    left: 0;\n    right: 0;\n    bottom: 0;\n    overflow: hidden;\n    min-height: 300px;\n    background: #fff url(../../../../../wp-admin/images/spinner.gif) center center no-repeat;\n\n    @media print, (-webkit-min-device-pixel-ratio: 1.25), (min-resolution: 120dpi) {\n      background-image: url(../../../../../wp-admin/images/spinner-2x.gif);\n    }\n  }\n  &-backdrop {\n    position: fixed;\n    top: 0;\n    left: 0;\n    right: 0;\n    bottom: 0;\n    min-height: 360px;\n    background: #000;\n    opacity: .7;\n    z-index: 159900;\n  }\n}\n\n#papi-iframe-modal-frame {\n  position: relative;\n  padding: 0;\n  margin: 0;\n  width: 100%;\n  height: 100%;\n  overflow: hidden;\n}\n"
  },
  {
    "path": "src/assets/scss/modules/_options.scss",
    "content": ".papi-options-logo {\n  background: url('../images/logotype.png') no-repeat;\n  -moz-background-size: cover;\n  -o-background-size: cover;\n  -webkit-background-size: cover;\n  background-size: cover;\n  -moz-border-radius: 5px;\n  -webkit-border-radius: 5px;\n  border-radius: 5px;\n  float: left;\n  height: 50px;\n  margin: 0 10px 10px 0;\n  width: 50px;\n}\n\n.papi-options-table {\n  tr:nth-child(2n) {\n    background: #fcfcfc;\n  }\n}\n\n.papi-options-description {\n  margin-top: -12px;\n}\n\n.papi-options-postbox-outer {\n  float: left;\n  margin-right: 10px;\n  position: static;\n  width: 250px;\n}\n\n.papi-options-postbox {\n  width: 250px;\n\n  label {\n    display: block;\n    font-size: 1em;\n    font-weight: 600;\n    margin: 1.33em 0;\n  }\n}\n"
  },
  {
    "path": "src/assets/scss/modules/_table.scss",
    "content": "// Table\n.papi-table,\n.papi-meta-type-term .form-table {\n  border-spacing: 0;\n  cursor: default !important;\n\n  * {\n    word-wrap: break-word;\n  }\n\n  tr {\n    -webkit-box-shadow: 0 1px 1px rgba(0, 0, 0, .04);\n    box-shadow: 0 1px 1px rgba(0, 0, 0, .04);\n    margin: 0;\n\n    td, th[scope=\"row\"] {\n      border-bottom: 0;\n      border-top: 1px solid #eaeaea;\n      padding: 10px 16px 10px 15px;\n      vertical-align: top;\n\n      &:first-child {\n        background: #f9f9f9;\n        border-right: 1px solid #eaeaea;\n        border-top: 1px solid #eaeaea;\n        width: 15%;\n\n        label {\n          color: #333;\n          display: block;\n          font-size: 13px;\n          font-weight: bold;\n          line-height: 1.4em;\n          margin: 0 0 3px;\n          padding: 0;\n        }\n\n        > p {\n          color: #899194;\n          font-size: 12px;\n          font-style: normal;\n          font-weight: normal;\n          line-height: 16px;\n          margin: 3px 0 0 !important;\n          padding: 0 !important;\n        }\n      }\n\n      &[colspan=\"2\"] {\n        background: #fff;\n      }\n\n      label {\n        font-size: 13px;\n        font-weight: 600;\n        line-height: 1.5;\n        padding-top: 4px;\n        word-break: break-word;\n\n        &.light {\n            font-weight: normal;\n            padding: 0;\n        }\n      }\n\n      input,\n      select,\n      textarea {\n        width: 99.5%;\n      }\n\n      input[type=\"radio\"] {\n        margin-top: 0px;\n      }\n\n      input[type=\"radio\"],\n      input[type=\"checkbox\"] {\n        width: 16px;\n        min-width: 16px;\n\n        @media screen and (max-width: 782px) {\n          width: 25px;\n          min-width: 25px;\n        }\n      }\n    }\n  }\n\n  .wp-editor-wrap input {\n    width: auto !important;\n  }\n}\n\n//\n// Vertical layout fixes.\n//\n\n.papi-table tr td:first-child > p,\n.papi-meta-type-term .form-table tr td:first-child > p {\n  padding: 0 0 10px 0 !important;\n}\n\n//\n// Mobile css for first papi table.\n//\n\n@media screen and (max-width: 782px) {\n  .papi-box > .inside {\n    margin-top: -5px !important;\n\n    > .papi-table > tbody > tr {\n      > td {\n        display: block;\n\n        &:first-child {\n          border-bottom: 1px solid #eaeaea;\n          border-right: 0;\n          margin: 10px 0 0;\n          padding: 10px 0;\n          width: 99.6%;\n\n          > label {\n            padding-left: 10px;\n          }\n\n          &[colspan=\"2\"] {\n            margin-bottom: -11px;\n          }\n        }\n\n        + td {\n          border-top: 0;\n          margin-top: 10px;\n          padding: 10px;\n        }\n      }\n\n      &.papi-layout-vertical {\n        > td:first-child {\n          border-bottom: 0;\n        }\n      }\n    }\n  }\n}\n\n//\n// Term table.\n//\n\n.papi-meta-type-term .wrap h1 {\n  margin-bottom: 10px;\n}\n\n.papi-meta-type-term #edittag > .form-table {\n  border-top: 1px #ddd solid;\n}\n\n.papi-meta-type-term #edittag > .papi-table {\n  margin-top: -1px;\n}\n\n.papi-meta-type-term #edittag > .form-table,\n.papi-meta-type-term #edittag > .papi-table {\n  background: white;\n  border-left: 1px #ddd solid;\n  border-right: 1px #ddd solid;\n  border-bottom: 1px #ddd solid;\n}\n"
  },
  {
    "path": "src/assets/scss/modules/_tabs.scss",
    "content": "// Tabs wrapper\n\n.papi-tabs-wrapper {\n\n  float: left;\n  width: 100%;\n\n  .papi-tabs-back {\n    background: #f4f2f2;\n    border-right: 1px solid #dfdfdf;\n    bottom: 0;\n    left: 0;\n    position: absolute;\n    top: 0;\n    width: 139px;\n  }\n\n  .papi-tabs {\n    background: #f4f2f2;\n    border-right: 1px solid #dedede;\n    border-top: 1px solid #eaeaea;\n    float: left;\n    line-height: 22px;\n    margin: -7px 0 -12px -10px;\n    padding: 0 0 10px;\n    position: relative;\n    width: 137px;\n\n    li {\n      display: block;\n      margin: 0;\n      padding: 0;\n\n      a {\n        background: #f4f2f2;\n        border-bottom: 1px solid #eaeaea;\n        border-top: 1px solid transparent;\n        color: #899194;\n        display: block;\n        font-weight: bold;\n        line-height: 18px;\n        margin: 0 0 0 -1px;\n        padding: 10px;\n        text-decoration: none;\n\n        img {\n          float: left;\n          height: 13px;\n          margin-top: 2px;\n          padding: 0 5px;\n          width: 13px;\n        }\n      }\n\n      &.active a {\n        background-color: #f9f9f9;\n        border-color: #dedede;\n        border-top: 1px solid transparent;\n        border-right: 1px #eaeaea solid;\n        color: #333;\n        margin: 0 -1px;\n        position: relative;\n      }\n\n      &.active.white-tab a {\n        background-color: #fff;\n        border-right: 1px #fff solid;\n      }\n\n      &:first-child.active a {\n        border-top: 1px solid transparent;\n      }\n    }\n  }\n\n  .papi-tabs-table-back {\n    background: #f9f9f9;\n    border-right: 1px solid #eaeaea;\n    bottom: 0;\n    left: 0;\n    position: absolute;\n    top: 0;\n    width: 246px;\n\n    &.white-tab {\n      background-color: #fff;\n      border-right: 1px #fff solid;\n    }\n  }\n\n  .papi-tabs-content {\n    margin-left: 131px;\n    padding-left: 10px;\n    position: relative;\n\n    > div {\n      display: none;\n\n      &.active {\n        display: block;\n      }\n\n      .papi-table {\n        margin: -7px 0 -12px -13px;\n        min-height: 100%;\n\n        tr td:first-child {\n          width: 75px;\n\n          label {\n            padding-left: 2px;\n          }\n        }\n      }\n    }\n  }\n}\n"
  },
  {
    "path": "src/assets/scss/modules/_utils.scss",
    "content": "// Utils\n\n.papi-pull-left {\n  float: left;\n}\n\n.papi-pull-right {\n  float: right;\n}\n\n.papi-clear {\n  clear: both;\n}\n\n.papi-hide {\n  display: none !important;\n}\n\n.papi-visually-hidden {\n  clip: rect(0 0 0 0);\n  clip: rect(0, 0, 0, 0);\n  height: 1px;\n  margin: -1px;\n  overflow: hidden;\n  padding: 0;\n  position: absolute;\n  width: 1px;\n}\n"
  },
  {
    "path": "src/assets/scss/properties/_checkbox.scss",
    "content": ".papi-property-checkbox {\n\n  @media screen and (min-width: 782px) {\n    p {\n      margin: 0;\n    }\n  }\n\n  @media screen and (max-width: 782px) {\n    p:first-child {\n      margin-top: 0;\n    }\n  }\n}\n\n//\n// Vertical layout fixes\n//\n\n.papi-layout-vertical .papi-property-checkbox > br {\n  display: none;\n}\n"
  },
  {
    "path": "src/assets/scss/properties/_color.scss",
    "content": ".papi-property-color-picker .wp-picker-input-wrap {\n  margin-top: -1px;\n\n  .button {\n    width: auto !important;\n  }\n}\n"
  },
  {
    "path": "src/assets/scss/properties/_divider.scss",
    "content": ".papi-property-divider {\n  h3 {\n    border-bottom: none;\n    font-size: 13px !important;\n    line-height: 1.4;\n    margin: 0;\n    padding: 8px 12px;\n    padding-top: 0 !important;\n    padding-bottom: 2px !important;\n    width: 100%;\n\n    span {\n      margin-left: -12px;\n    }\n  }\n\n  p {\n    color: #899194;\n    font-size: 12px;\n    font-style: normal;\n    line-height: 16px;\n    margin: 2px 0 0;\n  }\n}\n\n//\n// Mobile css.\n//\n\n@media screen and (max-width: 782px) {\n  .papi-property-divider {\n    padding: 0 10px;\n  }\n}\n"
  },
  {
    "path": "src/assets/scss/properties/_dropdown.scss",
    "content": ".attachment-compat .papi-component-select2 {\n  width: 99.2%;\n}\n\n.papi-table tr td .select2-hidden-accessible {\n  width: 1px;\n}\n\n.papi-table tr td .select2-selection--multiple ul li {\n  margin-bottom: 1px;\n}\n"
  },
  {
    "path": "src/assets/scss/properties/_file.scss",
    "content": ".papi-property-file {\n\n  .attachment {\n    cursor: move;\n    margin: 0 5px 0 -8px;\n    max-height: 150px;\n    max-width: 150px;\n\n    &.ui-sortable-placeholder {\n      background: #f9f9f9;\n      border: 1px #dfdfdf solid;\n      margin: 8px 13px 0 0;\n      max-height: 134px;\n      max-width: 134px;\n      visibility: visible !important;\n    }\n\n    .check {\n      background: #32373c;\n      border-radius: 50%;\n      color: #fff;\n      font-size: 15px;\n      height: 21px;\n      padding-top: 2px;\n      text-decoration: none;\n      text-transform: uppercase;\n    }\n\n    .thumbnail {\n      cursor: move;\n\n      :after {\n        -webkit-box-shadow: none;\n        box-shadow: none;\n      }\n\n      img {\n        display: block;\n        max-width: 134px;\n        max-height: 134px;\n        margin: 0 auto;\n      }\n    }\n  }\n}\n\n.papi-property-repeater .papi-property-file .attachment {\n  width: 150px;\n}\n"
  },
  {
    "path": "src/assets/scss/properties/_flexible.scss",
    "content": ".papi-property-flexible {\n\n  .flexible-row-label {\n    background: #f9f9f9;\n    border-top: 1px #eaeaea solid;\n  }\n\n  .flexible-layout-table .flexible-row-label {\n    border-top: 0;\n    border-bottom: 1px #eaeaea solid;\n    margin-bottom: -2px !important;\n    padding: 10px 10px !important;\n    display: flex;\n  }\n\n  .flexible-layout-row .flexible-row-label {\n    padding: 10px 15px!important;\n    display: flex;\n    margin-top: -10px;\n  }\n\n  .papi-table .flexible-column {\n    padding-bottom: 0;\n    padding-left: 0;\n    padding-right: 0;\n\n    &.flexible-layout-row {\n      border-top: 0;\n\n      .papi-table {\n        border-bottom: 0;\n        margin: 0;\n        table-layout: fixed;\n\n        tr td {\n          border-right: 0;\n          max-width: 100%;\n          padding: 20px 15px 20px 18px;\n\n          &:first-child {\n            border-right: 1px solid #eaeaea;\n            padding: 10px 16px 10px 15px;\n            width: 15%;\n\n            &[colspan=\"2\"] {\n              border-right: 0;\n            }\n          }\n        }\n      }\n    }\n\n    .flexible-table {\n      border-spacing: 0;\n      margin: -1px 0;\n      table-layout: fixed;\n      width: 100%;\n\n      .papi-property-repeater {\n        margin-top: -21px;\n      }\n\n      .wp-editor-tools {\n        margin-bottom: -4px !important;\n      }\n\n      > thead > tr > th {\n        background: #f9f9f9;\n        border: 1px solid #eaeaea;\n        border-left: 0;\n        color: #444;\n        padding: 5px 15px;\n\n        &:first-child {\n          border-left: 0;\n          width: auto;\n        }\n\n        &:last-child {\n          border-right: 0;\n        }\n      }\n\n      > tbody > tr > td {\n        border-right: 1px solid #eaeaea;\n        border-top: 0;\n        padding: 20px 15px 20px 18px;\n\n        &:first-child {\n          background: none;\n          border-left: 0;\n          font-weight: normal;\n          width: auto;\n        }\n\n        &:last-child {\n          border-right: 0;\n        }\n      }\n    }\n  }\n\n  .bottom {\n    text-align: right;\n  }\n\n  .flexible-layouts-btn-wrap {\n    display: inline-block;\n    position: relative;\n  }\n\n  .flexible-layouts {\n    background: #23282d;\n    border-radius: 4px;\n    color: #fff;\n    float: right;\n    left: 50%;\n    min-width: 150px;\n    opacity: 1;\n    position: absolute;\n    text-align: left;\n    -moz-transform: translateX(-50%);\n    -webkit-transform: translateX(-50%);\n    transform: translateX(-50%);\n    width: 100%;\n    z-index: 1;\n\n    ul {\n      color: #fff;\n      padding: 0 8px;\n      text-decoration: none;\n\n      li {\n        border-top: 1px #32373c solid;\n        padding: 5px 8px 0;\n\n        &:first-child {\n          border-top: 0;\n          margin-top: -5px;\n        }\n\n        a {\n          color: white;\n          display: block;\n          float: none;\n          text-decoration: none;\n\n          &:hover {\n            color: #00b9eb;\n          }\n        }\n      }\n    }\n\n    &-hidden {\n      position: absolute;\n      visibility: hidden;\n    }\n\n    &-top {\n      bottom: 140%;\n\n      &:after {\n        top: 100%;\n        border: solid transparent;\n        border-color: rgba(213, 0, 0, 0);\n        border-top-color: #23282d;\n        border-width: 5px;\n        content: ' ';\n        height: 0;\n        left: 50%;\n        margin-left: -5px;\n        pointer-events: none;\n        position: absolute;\n        width: 0;\n      }\n    }\n\n    &-bottom {\n      top: 140%;\n\n      &:after {\n        top: -3%;\n        border: solid transparent;\n        border-color: rgba(213, 0, 0, 0);\n        border-bottom-color: #23282d;\n        border-width: 5px;\n        content: ' ';\n        height: 0;\n        left: 50%;\n        margin-left: -5px;\n        pointer-events: none;\n        position: absolute;\n        width: 0;\n      }\n    }\n  }\n\n  &.papi-property-repeater-top {\n    > .papi-table {\n      > .flexible-tbody > tr > .flexible-column {\n        padding-top: 0;\n      }\n    }\n  }\n}\n"
  },
  {
    "path": "src/assets/scss/properties/_group.scss",
    "content": ".papi-property-group {\n  margin-right: -31px;\n\n  > .papi-table {\n    margin: -11px 0 -10px -15px;\n  }\n}\n\n.papi-property-flexible .papi-property-group,\n.papi-property-repeater .papi-property-group {\n  > .papi-table {\n    margin: -21px 0 -19px -18px;\n  }\n}\n\n//\n// Mobile css.\n//\n\n@media screen and (max-width: 782px) {\n  .papi-property-group {\n    margin-right: -19px;\n\n    > .papi-table {\n      margin: -11px 0 -10px -10px;\n\n      td {\n        display: block;\n\n        &:first-child {\n          border-bottom: 1px solid #eaeaea;\n          border-right: 0;\n          margin-top: 10px;\n          padding: 10px 0;\n          width: 100%;\n\n          > label {\n            padding-left: 10px;\n          }\n\n          &[colspan=\"2\"] {\n            margin-bottom: -11px;\n          }\n        }\n\n        + td {\n          border-top: 0;\n          margin-top: 10px;\n        }\n      }\n    }\n  }\n}\n\n"
  },
  {
    "path": "src/assets/scss/properties/_link.scss",
    "content": ".papi-property-link {\n  .link-table {\n    td {\n      &:first-child {\n        font-weight: bold;\n        border-left: 1px #eaeaea solid !important;\n        border-right: 0 !important;\n      }\n      &:last-child {\n        border-right: 1px #eaeaea solid !important;\n      }\n      border-left: 1px #eaeaea solid !important;\n    }\n    tr {\n      border-bottom: 1px #eaeaea solid !important;\n    }\n    border-bottom: 1px #eaeaea solid !important;\n  }\n}\n\n.papi-property-repeater {\n  .link-table {\n    margin: 0 !important;\n    td {\n      &:first-child {\n        padding: 10px 16px 10px 15px !important;\n        width: 15% !important;\n      }\n    }\n  }\n}\n"
  },
  {
    "path": "src/assets/scss/properties/_post.scss",
    "content": ".papi-property-post {\n  &.advanced {\n    margin-right: -30px;\n  }\n\n  > table {\n    margin: -11px 0 -10px -15px;\n\n    td {\n      background: #fff;\n\n      &:first-child {\n        background: #f9f9f9 !important;\n      }\n    }\n  }\n}\n\n/* Repeater style */\n.papi-property-repeater .papi-property-post {\n  &.advanced {\n    margin-right: -33px !important;\n  }\n\n  .papi-table {\n    margin: -21px 0 -20px -18px !important;\n\n    td:first-child {\n      width: auto;\n    }\n  }\n}\n\n/* Repeater style: row layout */\n.papi-property-repeater .repeater-layout-row .papi-property-post > .papi-table,\n.papi-property-flexible .flexible-layout-row .papi-property-post > .papi-table {\n  td:first-child {\n    width: 15% !important;\n  }\n}\n\n/* Flexible style */\n.papi-property-flexible .papi-property-post {\n  &.advanced {\n    margin-right: -33px !important;\n  }\n\n  .papi-table {\n    margin: -20px 0 -19px -18px !important;\n\n    tr {\n      td {\n        border-top: 1px solid #eaeaea !important;\n\n        &:first-child {\n          width: auto;\n        }\n      }\n\n      &:first-child td {\n        border-top: 0 !important;\n      }\n    }\n  }\n}\n"
  },
  {
    "path": "src/assets/scss/properties/_radio.scss",
    "content": ".papi-property-radio {\n\n  @media screen and (min-width: 782px) {\n    p {\n      margin: 0;\n    }\n  }\n\n  @media screen and (max-width: 782px) {\n    p:first-child {\n      margin-top: 0;\n    }\n  }\n}\n\n//\n// Vertical layout fixes\n//\n\n.papi-layout-vertical .papi-property-radio > br {\n  display: none;\n}\n"
  },
  {
    "path": "src/assets/scss/properties/_reference.scss",
    "content": ".papi-property-reference {\n  li {\n    float: left;\n    width: 100%;\n\n    &:first-child {\n      margin-top: -10px;\n    }\n\n    .handlediv {\n      margin-top: -10px;\n    }\n\n    &.closed .handlediv:before {\n      content: '\\f140' !important;\n    }\n\n    h3 {\n      background: #f9f9f9;\n      border: 1px #eaeaea solid;\n      margin-top: 5px !important;\n\n      + .handlediv {\n        margin-top: -38px;\n      }\n    }\n\n    h4 {\n      float: left;\n      margin: 0;\n      margin-right: 10px;\n    }\n  }\n\n  .page-types {\n    border: 1px #eaeaea solid;\n    border-top: 0;\n    margin-top: -6px;\n    padding: 20px 15px 10px;\n\n    .heading-border {\n      border-bottom: 1px #eaeaea solid;\n      padding-bottom: 5px;\n    }\n\n    ul {\n      float: left;\n      width: 100%;\n    }\n  }\n\n  .box {\n    margin: 0 0 15px -5px;\n\n    a {\n      background: #f8f8f8;\n      border: 1px #eaeaea solid;\n      float: left;\n      margin: 5px;\n      padding: 5px 10px;\n      text-decoration: none;\n      width: auto;\n\n      &:hover {\n        background: #2ea2cc;\n        border: 1px #fff solid;\n        color: #fff;\n        text-decoration: none;\n      }\n    }\n  }\n}\n"
  },
  {
    "path": "src/assets/scss/properties/_relationship.scss",
    "content": ".papi-property-relationship {\n  background: #fafafa;\n  border: 1px #eaeaea solid;\n\n  .relationship-inner {\n    padding: 5px;\n\n    .relationship-top-left {\n      float: left;\n      max-width: 49%;\n      padding: 10px 0;\n      width: 49%;\n\n      input[type=\"search\"] {\n        margin-top: 5px;\n        padding: 5px;\n        width: 100%;\n      }\n\n    }\n\n    .relationship-top-right {\n      float: right;\n      max-width: 49%;\n      padding: 10px 0;\n      width: 49%;\n\n      select {\n        margin-top: 5px;\n      }\n    }\n\n    .relationship-left {\n      background: #fff;\n      border: 1px #eaeaea solid;\n      float: left;\n      height: 250px;\n      max-height: 250px;\n      max-width: 49%;\n      min-height: 250px;\n      overflow: scroll;\n      overflow-x: hidden;\n      width: 49%;\n    }\n\n    .relationship-right {\n      background: #fff;\n      border: 1px #eaeaea solid;\n      float: right;\n      height: 250px;\n      max-height: 250px;\n      max-width: 49%;\n      min-height: 250px;\n      overflow: scroll;\n      overflow-x: hidden;\n      width: 49%;\n    }\n\n    ul {\n      margin: 0;\n      padding: 5px 0;\n\n      .ui-state-highlight {\n        border-bottom: 1px #eaeaea solid;\n      }\n\n      li {\n        background: #fff;\n        border-bottom: 1px #eaeaea solid;\n        cursor: pointer;\n        padding: 5px 10px 10px 5px;\n\n        span.icon {\n          background: url('../images/plus-minus.png') 0 0 no-repeat;\n          cursor: pointer;\n          float: right;\n          height: 16px;\n          width: 16px;\n          margin: 0 3px 0 0;\n\n          &.minus {\n            background-position: -16px 0;\n            margin: 0;\n          }\n        }\n\n        a {\n          text-decoration: none;\n        }\n\n        &.sortable {\n          border: 1px #eaeaea solid;\n          span.icon {\n            margin-top: 2px;\n          }\n        }\n\n      }\n\n    }\n\n  }\n\n}\n"
  },
  {
    "path": "src/assets/scss/properties/_repeater.scss",
    "content": ".papi-property-repeater,\n.papi-property-flexible {\n  margin-right: -31px;\n\n  > .papi-table {\n    margin: -11px 0 -13px -15px;\n    table-layout: fixed;\n    border-bottom: 1px solid #eaeaea;\n\n    > thead > tr,\n    > tbody > tr {\n      background: #fff;\n\n      &.closed {\n        .handle {\n          .count {\n            top: 68%;\n          }\n\n          .toggle {\n            margin-top: 0;\n            padding: 0;\n\n            &:before {\n              content: '\\f140';\n            }\n          }\n        }\n\n        .repeater-column {\n          background: #f9f9f9;\n          border-top: 1px solid #eaeaea;\n          text-align: center;\n        }\n\n        .repeater-content-open {\n          display: none;\n        }\n\n        .repeater-content-closed {\n          color: #afafaf;\n          display: block;\n          font-size: 12px;\n          font-weight: 700;\n          line-height: 1.4em;\n          padding-top: 12px;\n          text-align: center;\n          width: 100%;\n        }\n\n        .repeater-layout-row {\n          background: #f9f9f9;\n          border-bottom: 0;\n          border-top: 1px solid #eaeaea;\n          padding: 0;\n          text-align: center;\n        }\n      }\n\n      th {\n        background: #f9f9f9;\n        border: 1px solid #eaeaea;\n        border-bottom: 0;\n        border-left: 0;\n        font-size: 12px;\n        padding: 10px 15px;\n\n        &:first-child,\n        &.last {\n          background: #fff;\n          border-left: 0;\n          color: #afafaf;\n          font-weight: bold;\n          padding-left: 0;\n          padding-right: 0;\n          position: relative;\n          width: 5%;\n\n          > span {\n            overflow: hidden;\n            position: absolute;\n            text-align: center;\n            text-overflow: ellipsis;\n            top: 50%;\n            white-space: nowrap;\n            width: 100%;\n          }\n        }\n\n        &.last {\n          border-right: 1px solid #eaeaea;\n        }\n      }\n\n      > td {\n        border-right: 1px solid #eaeaea;\n        padding: 10px 15px 20px 15px;\n\n        &.handle {\n          cursor: move;\n\n          .toggle {\n            cursor: pointer;\n            border-bottom: 1px #f9f9f9 solid;\n            font: normal 20px/1 'dashicons';\n            margin-top: 2px;\n            padding-bottom: 4px;\n            top: 0%;\n\n            &:before {\n              content: '\\f142';\n              margin-left: -2px;\n            }\n          }\n        }\n\n        &:first-child,\n        &.last {\n          background: #f9f9f9;\n          border-left: 0;\n          font-weight: bold;\n          padding-left: 0;\n          padding-right: 0;\n          position: relative;\n          width: 5%;\n\n          > span {\n            color: #afafaf;\n            margin-top: -10px;\n            overflow: hidden;\n            position: absolute;\n            text-align: center;\n            text-overflow: ellipsis;\n            top: 50%;\n            white-space: nowrap;\n            width: 100%;\n          }\n        }\n\n        .repeater-content-closed {\n          display: none;\n        }\n\n        .repeater-remove-item {\n          color: #afafaf;\n          text-decoration: none;\n        }\n\n        &.repeater-layout-row {\n          padding: 0;\n\n          > .papi-table {\n            margin: -1px 0 -11px 0;\n            table-layout: fixed;\n\n            tr td {\n              border-right: 0;\n              max-width: 100%;\n              padding: 20px 15px 20px 18px;\n\n              &:first-child {\n                border-right: 1px solid #eaeaea;\n                padding: 10px 16px 10px 15px;\n                width: 15%;\n              }\n            }\n          }\n        }\n      }\n    }\n  }\n\n  .bottom {\n    margin-top: 25px;\n    width: 95%;\n\n    button {\n      float: right;\n    }\n  }\n}\n\n//\n// Repeater inside repeater fixes.\n//\n\n.papi-property-repeater .repeater-layout-row > div {\n  margin-top: -1px;\n}\n\n.papi-property-repeater .repeater-layout-row .papi-property-repeater {\n  margin-right: -33px;\n  margin-left: 1px;\n\n  > .papi-table {\n    margin: -11px 0 -13px -16px;\n    border-bottom: 1px solid #eaeaea;\n\n    > .repeater-tbody {\n      > tr > td {\n        border-right: 1px solid #eaeaea !important;\n      }\n    }\n  }\n}\n\n//\n// Repeater inside flexible fixes.\n//\n\n.papi-property-flexible .flexible-column.flexible-layout-row .papi-property-repeater {\n  margin-left: -1px;\n  margin-right: -33px;\n\n  > .papi-table {\n    margin: -21px 0 -13px -17px 1;\n    border-bottom: 1px solid #eaeaea;\n\n    > .repeater-tbody {\n      > tr > td {\n        border-right: 1px solid #eaeaea !important;\n\n        &.handle span {\n          margin-left: -15px;\n        }\n\n        &.last span {\n          margin-left: -18px;\n        }\n      }\n    }\n  }\n}\n\n//\n// Repeater inside flexible fixes.\n//\n\n.papi-property-flexible .flexible-column.flexible-layout-table .papi-property-repeater {\n  margin-left: -3px;\n}\n\n//\n// Repeater inside repeater without sidebar fixes.\n// Repeater inside flexible without sidebar fixes.\n//\n\n.papi-property-flexible .flexible-column.flexible-layout-row td[colspan=\"2\"] .papi-property-repeater,\n.papi-property-repeater td[colspan=\"2\"] .papi-property-repeater {\n  margin-right: -31px;\n\n  > .papi-table {\n    margin: -21px 0 -13px -14px;\n  }\n}\n\n//\n// Mobile css.\n//\n\n@media screen and (max-width: 782px) {\n  .papi-property-repeater, .papi-property-flexible {\n    margin-top: -16px;\n    padding: 6px;\n\n    .papi-property-repeater,\n    .papi-property-repeater {\n      padding: 0;\n    }\n  }\n}\n"
  },
  {
    "path": "src/assets/scss/properties/_shared.scss",
    "content": ".papi-rq {\n  color: #f00;\n}\n\n.metabox-prefs {\n  .papi-rq {\n    display: none !important;\n  }\n}\n\n.papi-box .hndle .papi-rq {\n  color: #000 !important;\n}\n"
  },
  {
    "path": "src/assets/scss/properties/_table.scss",
    "content": ".papi-property-table {\n  margin-left: -15px;\n  margin-top: -11px;\n  margin-bottom: -10px;\n  > table {\n    th {\n      border-right: 1px solid #eaeaea;\n      padding: 10px 16px 10px 15px;\n      text-align: left;\n      vertical-align: top;\n      &:last-child {\n        border-right: 0;\n      }\n    }\n    td:first-child {\n      font-weight: bold;\n    }\n    table td:first-child {\n      background: #fff;\n      font-weight: normal;\n      width: auto;\n    }\n  }\n}\n"
  },
  {
    "path": "src/assets/scss/properties/_term.scss",
    "content": ".papi-property-term {\n  &.advanced {\n    margin-right: -30px;\n  }\n\n  > table {\n    margin: -11px 0 -10px -15px;\n\n    td {\n      background: #fff;\n\n      &:first-child {\n        background: #f9f9f9 !important;\n      }\n    }\n  }\n}\n\n/* Repeater style */\n.papi-property-repeater .papi-property-term {\n  &.advanced {\n    margin-right: -33px !important;\n  }\n\n  .papi-table {\n    margin: -21px 0 -20px -18px !important;\n\n    td:first-child {\n      width: auto;\n    }\n  }\n}\n\n/* Repeater style: row layout */\n.papi-property-repeater .repeater-layout-row .papi-property-term > .papi-table,\n.papi-property-flexible .flexible-layout-row .papi-property-term > .papi-table {\n  td:first-child {\n    width: 15% !important;\n  }\n}\n\n/* Flexible style */\n.papi-property-flexible .papi-property-term {\n  &.advanced {\n    margin-right: -33px !important;\n  }\n\n  .papi-table {\n    margin: -20px 0 -19px -18px !important;\n\n    tr {\n      td {\n        border-top: 1px solid #eaeaea !important;\n\n        &:first-child {\n          width: auto;\n        }\n      }\n\n      &:first-child td {\n        border-top: 0 !important;\n      }\n    }\n  }\n}\n"
  },
  {
    "path": "src/assets/scss/properties/_text.scss",
    "content": ".papi-property-text {\n  height: 250px;\n  width: 100%;\n}\n"
  },
  {
    "path": "src/assets/scss/properties/_url.scss",
    "content": "@media screen and (max-width: 782px) {\n  .papi-url-media-input {\n    width: 95%;\n  }\n\n  .papi-url-media-button {\n    float: left;\n    margin-top: 10px;\n    width: 100%;\n  }\n}\n\n@media screen and (min-width: 782px) {\n  .papi-url-media-button {\n    float: right;\n    margin-top: 10px !important;\n    max-width: 15%;\n  }\n}\n"
  },
  {
    "path": "src/assets/scss/style.scss",
    "content": "@import \"components/pikaday\";\n@import \"components/select2\";\n\n@import \"modules/utils\";\n@import \"modules/admin\";\n@import \"modules/attachment\";\n@import \"modules/base\";\n@import \"modules/box\";\n@import \"modules/iframe\";\n@import \"modules/table\";\n@import \"modules/tabs\";\n@import \"modules/options\";\n\n@import \"properties/shared\";\n@import \"properties/color\";\n@import \"properties/checkbox\";\n@import \"properties/divider\";\n@import \"properties/dropdown\";\n@import \"properties/file\";\n@import \"properties/flexible\";\n@import \"properties/group\";\n@import \"properties/link\";\n@import \"properties/post\";\n@import \"properties/radio\";\n@import \"properties/repeater\";\n@import \"properties/relationship\";\n@import \"properties/reference\";\n@import \"properties/table\";\n@import \"properties/term\";\n@import \"properties/text\";\n@import \"properties/url\";\n\n"
  },
  {
    "path": "src/cli/class-papi-cli-command.php",
    "content": "<?php\n\n/**\n * Base class that must be extended by any Papi sub commands.\n */\nclass Papi_CLI_Command extends WP_CLI_Command {\n\n\t/**\n\t * Get formatter object based on supplied arguments.\n\t *\n\t * @param  array $assoc_args Associative args from CLI to determine formattin\n\t *\n\t * @return \\WP_CLI\\Formatter\n\t */\n\tprotected function get_formatter( $assoc_args ) {\n\t\t$args = $this->get_format_args( $assoc_args );\n\n\t\treturn new \\WP_CLI\\Formatter( $args );\n\t}\n\n\t/**\n\t * Get default fields for formatter.\n\t *\n\t * Class that extends Papi_CLI_Command should override this method.\n\t *\n\t * @return null|string|array\n\t */\n\tprotected function get_default_format_fields() {\n\t\treturn null;\n\t}\n\n\t/**\n\t * Get format args that will be passed into CLI Formatter.\n\t *\n\t * @param  array $assoc_args Associative args from CLI\n\t *\n\t * @return array Formatter args\n\t */\n\tprotected function get_format_args( $assoc_args ) {\n\t\t$format_args = [\n\t\t\t'fields' => $this->get_default_format_fields(),\n\t\t\t'field'  => null,\n\t\t\t'format' => 'table',\n\t\t];\n\n\t\tif ( isset( $assoc_args['fields'] ) ) {\n\t\t\t$format_args['fields'] = $assoc_args['fields'];\n\t\t}\n\n\t\tif ( isset( $assoc_args['field'] ) ) {\n\t\t\t$format_args['field'] = $assoc_args['field'];\n\t\t}\n\n\t\tif ( ! empty( $assoc_args['format'] ) && in_array( $assoc_args['format'], ['count', 'ids', 'table', 'csv', 'json'], true ) ) {\n\t\t\t$format_args['format'] = $assoc_args['format'];\n\t\t}\n\n\t\treturn $format_args;\n\t}\n}\n"
  },
  {
    "path": "src/cli/class-papi-cli-post-command.php",
    "content": "<?php\n\n/**\n * Manage posts.\n */\nclass Papi_CLI_Post_Command extends Papi_CLI_Command {\n\n\t/**\n\t * Get default fields for formatter.\n\t *\n\t * @return array\n\t */\n\tprotected function get_default_format_fields() {\n\t\treturn ['slug', 'type', 'has value', 'box'];\n\t}\n\n\t/**\n\t * Get fields that exists on a post.\n\t *\n\t * ## OPTIONS\n\t *\n\t * <post>\n\t * : Post ID\n\t *\n\t * [--field=<field>]\n\t * : Instead of returning the whole post fields, returns the value of a single fields.\n\t *\n\t * [--fields=<fields>]\n\t * : Get a specific subset of the post's fields.\n\t *\n\t * [--format=<format>]\n\t * : Accepted values: table, json, csv. Default: table.\n\t *\n\t * ## AVAILABLE FIELDS\n\t *\n\t * These fields are available for get command:\n\t *\n\t * * slug\n\t * * type\n\t * * exists\n\t *\n\t * ## EXAMPLES\n\t *\n\t *     wp papi post get 123 --format=json\n\t *\n\t *     wp papi post get 123 --field=slug\n\t *\n\t * @param  array $args\n\t * @param  array $assoc_args\n\t */\n\tpublic function get( $args, $assoc_args ) {\n\t\ttry {\n\t\t\t// Set query string that we need.\n\t\t\t$_GET['post'] = $args[0];\n\n\t\t\t// Get the page type that the post has.\n\t\t\t$entry_type = papi_get_entry_type_by_meta_id( $args[0] );\n\n\t\t\tif ( empty( $entry_type ) || $entry_type instanceof Papi_Page_Type === false ) {\n\t\t\t\tWP_CLI::error( 'No page type exists on the post' );\n\t\t\t}\n\n\t\t\t$properties = [];\n\n\t\t\tforeach ( $entry_type->get_boxes() as $box ) {\n\t\t\t\tforeach ( $box->properties as $property ) {\n\t\t\t\t\t$properties[] = [\n\t\t\t\t\t\t'slug'      => $property->get_slug( true ),\n\t\t\t\t\t\t'type'      => $property->type,\n\t\t\t\t\t\t'has value' => $property->get_value() !== null ? 'true' : 'false',\n\t\t\t\t\t\t'box'       => $box->title\n\t\t\t\t\t];\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// Render types as a table.\n\t\t\t$formatter = $this->get_formatter( $assoc_args );\n\t\t\t$formatter->display_items( $properties );\n\t\t} catch ( WC_CLI_Exception $e ) {\n\t\t\tWP_CLI::error( $e->getMessage() );\n\t\t}\n\t}\n\n\t/**\n\t * Rename meta key for page type.\n\t *\n\t * ## OPTIONS\n\t *\n\t * <page_type>\n\t * : Page type id\n\t *\n\t * <old_key>\n\t * : Old meta key\n\t *\n\t * <new_key>\n\t * : New meta key\n\t *\n\t * ## EXAMPLES\n\t *\n\t *     wp papi post rename about-page-type name title\n\t *\n\t * @param  array $args\n\t * @param  array $assoc_args\n\t */\n\tpublic function rename( $args, $assoc_args ) {\n\t\t$type    = $args[0];\n\t\t$old_key = $args[1];\n\t\t$new_key = $args[2];\n\n\t\t$posts = ( new Papi_Query( [\n\t\t\t'entry_type' => $type,\n\t\t\t'fields'     => 'ids'\n\t\t] ) )->get_result();\n\n\t\tif ( empty( $posts ) ) {\n\t\t\tWP_CLI::error( 'No posts found' );\n\t\t}\n\n\t\tforeach ( $posts as $post ) {\n\t\t\t$meta = get_post_meta( $post, $old_key, true );\n\n\t\t\tif ( papi_is_empty( $meta ) ) {\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tif ( delete_post_meta( $post, $old_key ) === false ) {\n\t\t\t\tWP_CLI::error( 'Could not delete post meta with key: ' . $old_key );\n\n\t\t\t}\n\n\t\t\tif ( update_post_meta( $post, $new_key, $meta ) === false ) {\n\t\t\t\tWP_CLI::error( 'Could not update post meta with key: ' . $new_key );\n\t\t\t}\n\t\t}\n\n\t\tWP_CLI::success( 'Done' );\n\t}\n}\n"
  },
  {
    "path": "src/cli/class-papi-cli-term-command.php",
    "content": "<?php\n\n/**\n * Manage terms.\n */\nclass Papi_CLI_Term_Command extends Papi_CLI_Command {\n\n\t/**\n\t * Get default fields for formatter.\n\t *\n\t * @return array\n\t */\n\tprotected function get_default_format_fields() {\n\t\treturn ['slug', 'type', 'has value', 'box'];\n\t}\n\n\t/**\n\t * Get fields that exists on a term.\n\t *\n\t * ## OPTIONS\n\t *\n\t * <term>\n\t * : Term ID\n\t *\n\t * [--field=<field>]\n\t * : Instead of returning the whole term fields, returns the value of a single fields.\n\t *\n\t * [--fields=<fields>]\n\t * : Get a specific subset of the term's fields.\n\t *\n\t * [--format=<format>]\n\t * : Accepted values: table, json, csv. Default: table.\n\t *\n\t * ## AVAILABLE FIELDS\n\t *\n\t * These fields are available for get command:\n\t *\n\t * * slug\n\t * * type\n\t * * exists\n\t *\n\t * ## EXAMPLES\n\t *\n\t *     wp papi term get 123 --format=json\n\t *\n\t *     wp papi term get 123 --field=slug\n\t *\n\t * @param  array $args\n\t * @param  array $assoc_args\n\t */\n\tpublic function get( $args, $assoc_args ) {\n\t\ttry {\n\t\t\t// Set query string that we need.\n\t\t\t$_GET['meta_type'] = 'term';\n\n\t\t\t// Get the taxonomy type that the term has.\n\t\t\t$entry_type = papi_get_entry_type_by_meta_id( $args[0] );\n\n\t\t\tif ( empty( $entry_type ) || $entry_type instanceof Papi_Taxonomy_Type === false ) {\n\t\t\t\tWP_CLI::error( 'No taxonomy type exists on the term' );\n\t\t\t}\n\n\t\t\t$properties = [];\n\n\t\t\tforeach ( $entry_type->get_boxes() as $box ) {\n\t\t\t\tforeach ( $box->properties as $property ) {\n\t\t\t\t\t$properties[] = [\n\t\t\t\t\t\t'slug'      => $property->get_slug( true ),\n\t\t\t\t\t\t'type'      => $property->type,\n\t\t\t\t\t\t'has value' => $property->get_value() !== null ? 'true' : 'false',\n\t\t\t\t\t\t'box'       => $box->title\n\t\t\t\t\t];\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// Render types as a table.\n\t\t\t$formatter = $this->get_formatter( $assoc_args );\n\t\t\t$formatter->display_items( $properties );\n\t\t} catch ( WC_CLI_Exception $e ) {\n\t\t\tWP_CLI::error( $e->getMessage() );\n\t\t}\n\t}\n\n\t/**\n\t * Rename meta key for taxonomy type.\n\t *\n\t * ## OPTIONS\n\t *\n\t * <taxonomy_type>\n\t * : Taxonomy type id\n\t *\n\t * <old_key>\n\t * : Old meta key\n\t *\n\t * <new_key>\n\t * : New meta key\n\t *\n\t * ## EXAMPLES\n\t *\n\t *     wp papi term rename about-page-type name title\n\t *\n\t * @param  array $args\n\t * @param  array $assoc_args\n\t */\n\tpublic function rename( $args, $assoc_args ) {\n\t\t$type    = $args[0];\n\t\t$old_key = $args[1];\n\t\t$new_key = $args[2];\n\n\t\t$terms = ( new Papi_Query( [\n\t\t\t'entry_type' => $type,\n\t\t\t'fields'     => 'ids'\n\t\t] ) )->get_result();\n\n\t\tif ( empty( $terms ) ) {\n\t\t\tWP_CLI::error( 'No terms found' );\n\t\t}\n\n\t\tforeach ( $terms as $term ) {\n\t\t\t$meta = get_term_meta( $term, $old_key, true );\n\n\t\t\tif ( papi_is_empty( $meta ) ) {\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tif ( delete_term_meta( $term, $old_key ) === false ) {\n\t\t\t\tWP_CLI::error( 'Could not delete term meta with key: ' . $old_key );\n\n\t\t\t}\n\n\t\t\tif ( update_term_meta( $term, $new_key, $meta ) === false ) {\n\t\t\t\tWP_CLI::error( 'Could not update term meta with key: ' . $new_key );\n\t\t\t}\n\t\t}\n\n\t\tWP_CLI::success( 'Done' );\n\t}\n}\n"
  },
  {
    "path": "src/cli/class-papi-cli-type-command.php",
    "content": "<?php\n\n/**\n * Manage types.\n */\nclass Papi_CLI_Type_Command extends Papi_CLI_Command {\n\n\t/**\n\t * Get default fields for formatter.\n\t *\n\t * @return array\n\t */\n\tprotected function get_default_format_fields() {\n\t\treturn ['name', 'id', 'meta type value', 'template', 'db count', 'type'];\n\t}\n\n\t/**\n\t * Get meta type value.\n\t *\n\t * @param  Papi_Entry_Type $entry_type\n\t *\n\t * @return string\n\t */\n\tprotected function get_meta_type_value( $entry_type ) {\n\t\tif ( in_array( $entry_type->get_type(), ['attachment'], true ) ) {\n\t\t\treturn $entry_type->get_type();\n\t\t}\n\n\t\tswitch ( papi_get_meta_type( $entry_type->get_type() ) ) {\n\t\t\tcase 'post':\n\t\t\t\treturn implode( ', ', $entry_type->post_type );\n\t\t\tcase 'term':\n\t\t\t\treturn implode( ', ', $entry_type->taxonomy );\n\t\t\tdefault:\n\t\t\t\treturn 'n/a';\n\t\t}\n\t}\n\n\t/**\n\t * List Papi types.\n\t *\n\t * ## Options\n\t *\n\t * [--<field>=<value>]\n\t * : Filter types based on type property.\n\t *\n\t * [--field=<field>]\n\t * : Prints the value of a single field for each type.\n\t *\n\t * [--fields=<fields>]\n\t * : Limit the output to specific type fields.\n\t *\n\t * [--format=<format>]\n\t * : Acceptec values: table, csv, json, count, ids. Default: table.\n\t *\n\t * ## AVAILABLE FIELDS\n\t *\n\t * These fields will be displayed by default for each type:\n\t *\n\t * * name\n\t * * id\n\t * * post_type\n\t * * template\n\t * * number_of_pages\n\t * * type\n\t *\n\t * Not all fields exists on a Papi type so some fields will have `n/a`\n\t * as value when no value can be displayed.\n\t *\n\t * ## EXAMPLES\n\t *\n\t *     wp papi type list\n\t *\n\t * @subcommand list\n\t */\n\tpublic function list_( $args, $assoc_args ) {\n\t\t// Get all entry types.\n\t\t$entry_types = papi_get_all_entry_types();\n\n\t\tif ( empty( $entry_types ) ) {\n\t\t\tWP_CLI::error( 'No Papi types exists.' );\n\t\t}\n\n\t\t// Create type item with the fields that\n\t\t// will be displayed.\n\t\t$entry_types = array_map( function( $entry_type ) {\n\t\t\treturn [\n\t\t\t\t'id'              => $entry_type->get_id(),\n\t\t\t\t'name'            => $entry_type->name,\n\t\t\t\t'meta type value' => $this->get_meta_type_value( $entry_type ),\n\t\t\t\t'template'        => empty( $entry_type->template ) ? 'n/a' : $entry_type->template,\n\t\t\t\t'type'            => $entry_type->get_type(),\n\t\t\t\t'db count'        => $entry_type->get_type() === 'option' ? 'n/a' : papi_get_entry_type_count( $entry_type )\n\t\t\t];\n\t\t}, $entry_types );\n\n\t\t// Render types as a table.\n\t\t$formatter = $this->get_formatter( $assoc_args );\n\t\t$formatter->display_items( $entry_types );\n\t}\n}\n"
  },
  {
    "path": "src/cli/class-papi-cli.php",
    "content": "<?php\n\n/**\n * Manage Papi from CLI.\n */\nclass Papi_CLI extends WP_CLI_Command {\n}\n\nWP_CLI::add_command( 'papi', 'Papi_CLI' );\nWP_CLI::add_command( 'papi post', 'Papi_CLI_Post_Command' );\nWP_CLI::add_command( 'papi term', 'Papi_CLI_Term_Command' );\nWP_CLI::add_command( 'papi type', 'Papi_CLI_Type_Command' );\n"
  },
  {
    "path": "src/core/class-papi-core-autoload.php",
    "content": "<?php\n\n/**\n * Autoload class that autoload classes.\n */\nfinal class Papi_Core_Autoload {\n\n\t/**\n\t * The Constructor.\n\t *\n\t * @codeCoverageIgnore\n\t */\n\tpublic function __construct() {\n\t\tspl_autoload_register( [$this, 'autoload'] );\n\t}\n\n\t/**\n\t * Autoload Papi classes.\n\t *\n\t * @codeCoverageIgnore\n\t *\n\t * @param string $class\n\t */\n\tpublic function autoload( $class ) {\n\t\t$class = strtolower( $class );\n\t\t$file  = 'class-' . str_replace( '_', '-', strtolower( $class ) ) . '.php';\n\t\t$path  = PAPI_PLUGIN_DIR;\n\n\t\tif ( strpos( $class, 'papi_admin' ) === 0 ) {\n\t\t\t$path .= 'admin/';\n\t\t} else if ( strpos( $class, 'papi_core_' ) === 0 ) {\n\t\t\t$path .= 'core/';\n\t\t} else if ( strpos( $class, 'papi_cli_' ) === 0 ) {\n\t\t\t$path .= 'cli/';\n\t\t} else if ( preg_match( '/^papi\\_\\w+\\_store$/', $class ) ) {\n\t\t\t$path .= 'stores/';\n\t\t} else if ( strpos( $class, 'papi_property' ) === 0 ) {\n\t\t\t$path .= 'properties/';\n\t\t} else if ( strpos( $class, 'papi_query' ) === 0 ) {\n\t\t\t$path .= 'query/';\n\t\t} else if ( preg_match( '/^papi\\_\\w+\\_type/', $class ) ) {\n\t\t\t$path .= 'types/';\n\t\t}\n\n\t\tif ( is_readable( $path . $file ) ) {\n\t\t\trequire_once $path . $file;\n\t\t}\n\t}\n}\n\nnew Papi_Core_Autoload;\n"
  },
  {
    "path": "src/core/class-papi-core-box.php",
    "content": "<?php\n\nclass Papi_Core_Box {\n\n\t/**\n\t * Capabilities list.\n\t *\n\t * @var array\n\t */\n\tpublic $capabilities = [];\n\n\t/**\n\t * Context.\n\t *\n\t * @var string\n\t */\n\tpublic $context = 'normal';\n\n\t/**\n\t * Display box.\n\t *\n\t * @var boolean\n\t */\n\tpublic $display = true;\n\n\t/**\n\t * Display box with frame.\n\t *\n\t * @var bool\n\t */\n\tpublic $frame = true;\n\n\t/**\n\t * The core type identifier.\n\t *\n\t * @var string\n\t */\n\tpublic $id = '';\n\n\t/**\n\t * Box layout that all properties will inherit.\n\t *\n\t * Possible values are:\n\t * - horizontal\n\t * - vertical\n\t *\n\t * @var string\n\t */\n\tpublic $layout = 'horizontal';\n\n\t/**\n\t * Custom box options.\n\t *\n\t * @var array\n\t */\n\tprotected $options = [];\n\n\t/**\n\t * Priority.\n\t *\n\t * @var string\n\t */\n\tpublic $priority = 'default';\n\n\t/**\n\t * Box properties.\n\t *\n\t * @var array\n\t */\n\tpublic $properties = [];\n\n\t/**\n\t * Site id.\n\t *\n\t * @var int\n\t */\n\tpublic $site_id = 0;\n\n\t/**\n\t * The sort order of the core box.\n\t *\n\t * @var int\n\t */\n\tpublic $sort_order = 1000;\n\n\t/**\n\t * The title of the box.\n\t *\n\t * @var string\n\t */\n\tpublic $title = '';\n\n\t/**\n\t * The constructor.\n\t *\n\t * @param array $args\n\t * @param array $properties\n\t */\n\tpublic function __construct( array $args = [], array $properties = [] ) {\n\t\t$this->setup_args( $args );\n\t\t$this->setup_properties( $properties );\n\t}\n\n\t/**\n\t * Get box option.\n\t *\n\t * @param  string $key\n\t *\n\t * @return mixed\n\t */\n\tpublic function get_option( $key ) {\n\t\treturn isset( $this->options[$key] ) ? $this->options[$key] : null;\n\t}\n\n\t/**\n\t * Set box option.\n\t *\n\t * @param string $key\n\t * @param mixed  $value\n\t */\n\tpublic function set_option( $key, $value ) {\n\t\t$this->options[$key] = $value;\n\t}\n\n\t/**\n\t * Setup arguments.\n\t *\n\t * @param  array $args\n\t */\n\tprotected function setup_args( array $args ) {\n\t\t$excluded_keys = ['options', 'properties'];\n\n\t\tforeach ( $args as $key => $value ) {\n\t\t\tif ( isset( $this->$key ) && ! in_array( $key, $excluded_keys, true ) ) {\n\t\t\t\t$this->$key = papi_esc_html( $value );\n\t\t\t}\n\t\t}\n\n\t\tif ( empty( $this->id ) ) {\n\t\t\t$this->id = papi_slugify( strtolower( papi_f( papi_underscorify( papify( $this->title ) ) ) ) );\n\t\t\t$this->id = sanitize_text_field( $this->id );\n\t\t}\n\t}\n\n\t/**\n\t * Setup properties.\n\t *\n\t * @param  array $properties\n\t */\n\tprotected function setup_properties( array $properties ) {\n\t\t$this->properties = papi_populate_properties( $properties );\n\t}\n\n\t/**\n\t * Get a string representation of the object.\n\t *\n\t * @return string\n\t */\n\tpublic function __toString() {\n\t\treturn $this->id;\n\t}\n}\n"
  },
  {
    "path": "src/core/class-papi-core-conditional-rule.php",
    "content": "<?php\n\n/**\n * Core class that implements a conditional rule.\n */\nclass Papi_Core_Conditional_Rule {\n\n\t/**\n\t * The operator.\n\t *\n\t * @var string\n\t */\n\tpublic $operator = '=';\n\n\t/**\n\t * The slug.\n\t *\n\t * @var string\n\t */\n\tpublic $slug;\n\n\t/**\n\t * The source value.\n\t *\n\t * @var mixed\n\t */\n\tpublic $source;\n\n\t/**\n\t * The value.\n\t *\n\t * @var mixed\n\t */\n\tpublic $value;\n\n\t/**\n\t * The constructor.\n\t *\n\t * @param array $rule\n\t */\n\tpublic function __construct( array $rule ) {\n\t\t$this->setup( $rule );\n\t}\n\n\t/**\n\t * Get field slug.\n\t *\n\t * @return string\n\t */\n\tpublic function get_field_slug() {\n\t\tif ( preg_match( '/\\[|\\]/', $this->slug ) ) {\n\t\t\t$slug = preg_replace( '/\\[|\\]/', '.', $this->slug );\n\t\t\t$slug = str_replace( '..', '.', $slug );\n\t\t\treturn substr( $slug, 0, -1 );\n\t\t}\n\n\t\treturn $this->slug;\n\t}\n\n\t/**\n\t * Get the source value.\n\t *\n\t * @return mixed\n\t */\n\tpublic function get_source() {\n\t\tif ( is_callable( $this->source ) ) {\n\t\t\treturn call_user_func_array( $this->source, [$this->slug] );\n\t\t}\n\n\t\tif ( is_string( $this->source ) && strpos( $this->source, '#' ) !== false ) {\n\t\t\t$source = explode( '#', $this->source );\n\n\t\t\tif ( empty( $source[0] ) || empty( $source[1] ) ) {\n\t\t\t\treturn $this->source;\n\t\t\t}\n\n\t\t\t$source[0] = new $source[0]();\n\n\t\t\tif ( method_exists( $source[0], $source[1] ) ) {\n\t\t\t\treturn call_user_func_array( $source, [$this->slug] );\n\t\t\t}\n\n\t\t\treturn;\n\t\t}\n\n\t\treturn $this->source;\n\t}\n\n\t/**\n\t * Setup source callable.\n\t *\n\t * @param  string $value\n\t *\n\t * @return string\n\t */\n\tpublic function setup_source( $value ) {\n\t\tif ( is_array( $value ) && count( $value ) === 2 && is_object( $value[0] ) && is_string( $value[1] ) ) {\n\t\t\treturn sprintf( '%s#%s', get_class( $value[0] ), $value[1] );\n\t\t}\n\n\t\tif ( is_string( $value ) && is_callable( $value ) ) {\n\t\t\treturn $value;\n\t\t}\n\n\t\t// No support for closure.\n\t\tif ( is_object( $value ) && $value instanceof Closure ) {\n\t\t\treturn '';\n\t\t}\n\n\t\treturn $value;\n\t}\n\n\t/**\n\t * Setup the rule and assign properties with values.\n\t *\n\t * @param array  $rule\n\t */\n\tprotected function setup( array $rule ) {\n\t\tforeach ( $rule as $key => $value ) {\n\t\t\tif ( $key === 'operator' ) {\n\t\t\t\t$value = strtoupper( $value );\n\t\t\t\t$value = html_entity_decode( $value );\n\t\t\t} else if ( $key === 'slug' ) {\n\t\t\t\t$value = papify( $value );\n\t\t\t} else if ( $key === 'source' ) {\n\t\t\t\t$value = $this->setup_source( $value );\n\t\t\t}\n\n\t\t\t$this->$key = $value;\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "src/core/class-papi-core-conditional-rules.php",
    "content": "<?php\n\n/**\n * Conditional rules class that contains\n * all rules.\n */\nclass Papi_Core_Conditional_Rules {\n\n\t/**\n\t * The constructor.\n\t */\n\tpublic function __construct() {\n\t\t$this->setup_filters();\n\t}\n\n\t/**\n\t * Convert string bool to bool.\n\t *\n\t * @param  mixed $str\n\t *\n\t * @return mixed\n\t */\n\tprotected function convert_bool( $str ) {\n\t\tif ( ! is_string( $str ) ) {\n\t\t\treturn $str;\n\t\t}\n\n\t\tswitch ( $str ) {\n\t\t\tcase 'false':\n\t\t\t\treturn false;\n\t\t\tcase 'true':\n\t\t\t\treturn true;\n\t\t\tdefault:\n\t\t\t\treturn $str;\n\t\t}\n\t}\n\n\t/**\n\t * Convert value from a property.\n\t *\n\t * @param  mixed $value\n\t * @param  Papi_Core_Conditional_Rule $rule\n\t *\n\t * @return mixed\n\t */\n\tprotected function convert_prop( $value, Papi_Core_Conditional_Rule $rule ) {\n\t\t$meta_id    = papi_get_meta_id();\n\t\t$meta_type  = papi_get_meta_type();\n\t\t$entry_type = papi_get_entry_type_by_meta_id( $meta_id, $meta_type );\n\n\t\tif ( ! papi_is_empty( $value ) && $entry_type instanceof Papi_Entry_Type !== false ) {\n\t\t\t$property = $entry_type->get_property( $rule->slug );\n\n\t\t\tif ( papi_is_property( $property ) ) {\n\t\t\t\t$prop_value = $property->format_value(\n\t\t\t\t\t$value,\n\t\t\t\t\t$property->slug,\n\t\t\t\t\t$meta_id\n\t\t\t\t);\n\n\t\t\t\t$prop_value = papi_filter_format_value(\n\t\t\t\t\t$property->type,\n\t\t\t\t\t$prop_value,\n\t\t\t\t\t$property->slug,\n\t\t\t\t\t$meta_id,\n\t\t\t\t\t$meta_type\n\t\t\t\t);\n\n\t\t\t\t$prop_value = $this->get_deep_value(\n\t\t\t\t\t$rule->slug,\n\t\t\t\t\t$prop_value\n\t\t\t\t);\n\n\t\t\t\tif ( gettype( $prop_value ) === gettype( $rule->value ) ) {\n\t\t\t\t\treturn $prop_value;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\treturn $value;\n\t\t}\n\n\t\treturn $value;\n\t}\n\n\t/**\n\t * Convert string number to int or float.\n\t *\n\t * @param  string $str\n\t *\n\t * @return float|int\n\t */\n\tprotected function convert_number( $str ) {\n\t\treturn papi_cast_string_value( $str );\n\t}\n\n\t/**\n\t * Get converted value.\n\t *\n\t * @param  Papi_Core_Conditional_Rule $rule\n\t *\n\t * @return array\n\t */\n\tprotected function get_converted_value( Papi_Core_Conditional_Rule $rule ) {\n\t\t$value = $this->get_value( $rule );\n\n\t\t// Convert numeric values.\n\t\tif ( is_numeric( $value ) && is_numeric( $rule->value ) ) {\n\t\t\treturn [\n\t\t\t\t$this->convert_number( $value ),\n\t\t\t\t$this->convert_number( $rule->value )\n\t\t\t];\n\t\t}\n\n\t\t// Convert bool value if it a string bool or return value.\n\t\t$value       = $this->convert_bool( $value );\n\t\t$rule->value = $this->convert_bool( $rule->value );\n\n\t\t// Try to convert the property to the same value as the rule value.\n\t\treturn [\n\t\t\t$this->convert_prop( $value, $rule ),\n\t\t\t$rule->value\n\t\t];\n\t}\n\n\t/**\n\t * Get deep value.\n\t *\n\t * @param  string $slug\n\t * @param  mixed $value\n\t *\n\t * @return mixed\n\t */\n\tprotected function get_deep_value( $slug, $value ) {\n\t\t$slugs = explode( '.', $slug );\n\t\tarray_shift( $slugs );\n\t\treturn papi_field_value( $slugs, $value, $value );\n\t}\n\n\t/**\n\t * Get property value.\n\t *\n\t * @param  Papi_Core_Conditional_Rule $rule\n\t *\n\t * @return mixed\n\t */\n\tprotected function get_value( Papi_Core_Conditional_Rule $rule ) {\n\t\tif ( papi_doing_ajax() ) {\n\t\t\t$source     = $rule->get_source();\n\t\t\t$meta_id    = papi_get_meta_id();\n\t\t\t$entry_type = papi_get_entry_type_by_meta_id( $meta_id );\n\n\t\t\tif ( ! papi_is_empty( $source ) && $entry_type instanceof Papi_Entry_Type !== false ) {\n\t\t\t\tif ( papi_is_property( $entry_type->get_property( $rule->slug ) ) ) {\n\t\t\t\t\treturn $this->get_deep_value( $rule->slug, $source );\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tif ( ! papi_is_empty( $rule->get_source() ) ) {\n\t\t\treturn $this->get_deep_value( $rule->slug, $rule->get_source() );\n\t\t}\n\n\t\t$slug  = $rule->get_field_slug();\n\t\t$type  = papi_get_meta_type();\n\t\t$value = papi_get_field( $slug, null, $type );\n\n\t\treturn $this->get_deep_value( $slug, $value );\n\t}\n\n\t/**\n\t * Equal conditional rule.\n\t *\n\t * @param  Papi_Core_Conditional_Rule $rule\n\t *\n\t * @return bool\n\t */\n\tpublic function rule_equal( Papi_Core_Conditional_Rule $rule ) {\n\t\tlist( $value, $rule_value ) = $this->get_converted_value( $rule );\n\t\treturn $value === $rule_value;\n\t}\n\n\t/**\n\t * Not equal conditional rule.\n\t *\n\t * @param  Papi_Core_Conditional_Rule $rule\n\t *\n\t * @return bool\n\t */\n\tpublic function rule_not_equal( Papi_Core_Conditional_Rule $rule ) {\n\t\tlist( $value, $rule_value ) = $this->get_converted_value( $rule );\n\t\treturn $value !== $rule_value;\n\t}\n\n\t/**\n\t * Greater then conditional rule.\n\t *\n\t * @param  Papi_Core_Conditional_Rule $rule\n\t *\n\t * @return bool\n\t */\n\tpublic function rule_greater_then( Papi_Core_Conditional_Rule $rule ) {\n\t\t$value = $this->get_value( $rule );\n\n\t\tif ( is_array( $value ) ) {\n\t\t\t$value = count( $value );\n\t\t}\n\n\t\tif ( ! is_numeric( $value ) || ! is_numeric( $rule->value ) ) {\n\t\t\treturn false;\n\t\t}\n\n\t\treturn $this->convert_number( $value ) >\n\t\t\t$this->convert_number( $rule->value );\n\t}\n\n\t/**\n\t * Greater then or equal conditional rule.\n\t *\n\t * @param  Papi_Core_Conditional_Rule $rule\n\t *\n\t * @return bool\n\t */\n\tpublic function rule_greater_then_or_equal( Papi_Core_Conditional_Rule $rule ) {\n\t\t$value = $this->get_value( $rule );\n\n\t\tif ( is_array( $value ) ) {\n\t\t\t$value = count( $value );\n\t\t}\n\n\t\tif ( ! is_numeric( $value ) || ! is_numeric( $rule->value ) ) {\n\t\t\treturn false;\n\t\t}\n\n\t\treturn $this->convert_number( $value ) >=\n\t\t\t$this->convert_number( $rule->value );\n\t}\n\n\t/**\n\t * Less then conditional rule.\n\t *\n\t * @param  Papi_Core_Conditional_Rule $rule\n\t *\n\t * @return bool\n\t */\n\tpublic function rule_less_then( Papi_Core_Conditional_Rule $rule ) {\n\t\t$value = $this->get_value( $rule );\n\n\t\tif ( is_array( $value ) ) {\n\t\t\t$value = count( $value );\n\t\t}\n\n\t\tif ( ! is_numeric( $value ) || ! is_numeric( $rule->value ) ) {\n\t\t\treturn false;\n\t\t}\n\n\t\treturn $this->convert_number( $value ) <\n\t\t\t$this->convert_number( $rule->value );\n\t}\n\n\t/**\n\t * Less then or equal conditional rule.\n\t *\n\t * @param  Papi_Core_Conditional_Rule $rule\n\t *\n\t * @return bool\n\t */\n\tpublic function rule_less_then_or_equal( Papi_Core_Conditional_Rule $rule ) {\n\t\t$value = $this->get_value( $rule );\n\n\t\tif ( is_array( $value ) ) {\n\t\t\t$value = count( $value );\n\t\t}\n\n\t\tif ( ! is_numeric( $value ) || ! is_numeric( $rule->value ) ) {\n\t\t\treturn false;\n\t\t}\n\n\t\treturn $this->convert_number( $value ) <=\n\t\t\t$this->convert_number( $rule->value );\n\t}\n\n\t/**\n\t * In array conditional rule.\n\t *\n\t * @param  Papi_Core_Conditional_Rule $rule\n\t *\n\t * @return bool\n\t */\n\tpublic function rule_in( Papi_Core_Conditional_Rule $rule ) {\n\t\tlist( $value, $rule_value ) = $this->get_converted_value( $rule );\n\n\t\tif ( ! is_array( $rule_value ) ) {\n\t\t\treturn false;\n\t\t}\n\n\t\treturn in_array( $value, $rule_value, true );\n\t}\n\n\t/**\n\t * Not in array conditional rule.\n\t *\n\t * @param  Papi_Core_Conditional_Rule $rule\n\t *\n\t * @return bool\n\t */\n\tpublic function rule_not_in( Papi_Core_Conditional_Rule $rule ) {\n\t\tlist( $value, $rule_value ) = $this->get_converted_value( $rule );\n\n\t\tif ( ! is_array( $rule_value ) ) {\n\t\t\treturn false;\n\t\t}\n\n\t\treturn ! in_array( $value, $rule_value, true );\n\t}\n\n\t/**\n\t * Like conditional rule.\n\t *\n\t * @param  Papi_Core_Conditional_Rule $rule\n\t *\n\t * @return bool\n\t */\n\tpublic function rule_like( Papi_Core_Conditional_Rule $rule ) {\n\t\t$value = $this->get_value( $rule );\n\n\t\tif ( ! is_string( $value ) ) {\n\t\t\t$value = papi_convert_to_string( $value );\n\t\t}\n\n\t\tif ( papi_is_empty( $value ) ) {\n\t\t\treturn false;\n\t\t}\n\n\t\treturn strpos(\n\t\t\tstrtolower( $value ),\n\t\t\tstrtolower( $rule->value )\n\t\t) !== false;\n\t}\n\n\t/**\n\t * Get between values.\n\t *\n\t * @param  Papi_Core_Conditional_Rule $rule\n\t *\n\t * @return array\n\t */\n\tprotected function get_between_values( Papi_Core_Conditional_Rule $rule ) {\n\t\t$value = $this->get_value( $rule );\n\n\t\tif ( ! is_array( $rule->value ) ) {\n\t\t\treturn [$rule, false];\n\t\t}\n\n\t\tforeach ( $rule->value as $index => $v ) {\n\t\t\t$v = $this->convert_number( $v );\n\n\t\t\tif ( is_numeric( $v ) ) {\n\t\t\t\t$rule->value[$index] = $v;\n\t\t\t} else {\n\t\t\t\tunset( $rule->value[$index] );\n\t\t\t}\n\t\t}\n\n\t\tif ( ! is_numeric( $value ) || count( $rule->value ) !== 2 ) {\n\t\t\treturn [$rule, false];\n\t\t}\n\n\t\treturn [$rule, $this->convert_number( $value )];\n\t}\n\n\t/**\n\t * Between conditional rule.\n\t *\n\t * @param  Papi_Core_Conditional_Rule $rule\n\t *\n\t * @return bool\n\t */\n\tpublic function rule_between( Papi_Core_Conditional_Rule $rule ) {\n\t\tlist( $rule, $value ) = $this->get_between_values( $rule );\n\n\t\tif ( $value === false ) {\n\t\t\treturn false;\n\t\t}\n\n\t\treturn $rule->value[0] <= $value && $value <= $rule->value[1];\n\t}\n\n\t/**\n\t * Not between conditional rule.\n\t *\n\t * @param  Papi_Core_Conditional_Rule $rule\n\t *\n\t * @return bool\n\t */\n\tpublic function rule_not_between( Papi_Core_Conditional_Rule $rule ) {\n\t\tlist( $rule, $value ) = $this->get_between_values( $rule );\n\n\t\tif ( $value === false ) {\n\t\t\treturn false;\n\t\t}\n\n\t\treturn ! ( $rule->value[0] <= $value && $value <= $rule->value[1] );\n\t}\n\n\t/**\n\t * Exists conditional rule.\n\t *\n\t * @param  Papi_Core_Conditional_Rule $rule\n\t *\n\t * @return bool\n\t */\n\tpublic function rule_exists( Papi_Core_Conditional_Rule $rule ) {\n\t\treturn ! in_array( $this->get_value( $rule ), [null, []], true );\n\t}\n\n\t/**\n\t * Not exists conditional rule.\n\t *\n\t * @param  Papi_Core_Conditional_Rule $rule\n\t *\n\t * @return bool\n\t */\n\tpublic function rule_not_exists( Papi_Core_Conditional_Rule $rule ) {\n\t\treturn in_array( $this->get_value( $rule ), [null, []], true );\n\t}\n\n\t/**\n\t * Empty conditional rule.\n\t *\n\t * @param  Papi_Core_Conditional_Rule $rule\n\t *\n\t * @return bool\n\t */\n\tpublic function rule_empty( Papi_Core_Conditional_Rule $rule ) {\n\t\treturn papi_is_empty( $this->get_value( $rule ) );\n\t}\n\n\t/**\n\t * Empty conditional rule.\n\t *\n\t * @param  Papi_Core_Conditional_Rule $rule\n\t *\n\t * @return bool\n\t */\n\tpublic function rule_not_empty( Papi_Core_Conditional_Rule $rule ) {\n\t\treturn ! papi_is_empty( $this->get_value( $rule ) );\n\t}\n\n\t/**\n\t * Setup filters.\n\t */\n\tpublic function setup_filters() {\n\t\tadd_filter( 'papi/conditional/rule/=', [$this, 'rule_equal'] );\n\t\tadd_filter( 'papi/conditional/rule/!=', [$this, 'rule_not_equal'] );\n\t\tadd_filter( 'papi/conditional/rule/>', [$this, 'rule_greater_then'] );\n\t\tadd_filter( 'papi/conditional/rule/>=', [$this, 'rule_greater_then_or_equal'] );\n\t\tadd_filter( 'papi/conditional/rule/<', [$this, 'rule_less_then'] );\n\t\tadd_filter( 'papi/conditional/rule/<=', [$this, 'rule_less_then_or_equal'] );\n\t\tadd_filter( 'papi/conditional/rule/IN', [$this, 'rule_in'] );\n\t\tadd_filter( 'papi/conditional/rule/NOT IN', [$this, 'rule_not_in'] );\n\t\tadd_filter( 'papi/conditional/rule/LIKE', [$this, 'rule_like'] );\n\t\tadd_filter( 'papi/conditional/rule/BETWEEN', [$this, 'rule_between'] );\n\t\tadd_filter( 'papi/conditional/rule/NOT BETWEEN', [$this, 'rule_not_between'] );\n\t\tadd_filter( 'papi/conditional/rule/EXISTS', [$this, 'rule_exists'] );\n\t\tadd_filter( 'papi/conditional/rule/NOT EXISTS', [$this, 'rule_not_exists'] );\n\t\tadd_filter( 'papi/conditional/rule/EMPTY', [$this, 'rule_empty'] );\n\t\tadd_filter( 'papi/conditional/rule/NOT EMPTY', [$this, 'rule_not_empty'] );\n\t}\n}\n\nnew Papi_Core_Conditional_Rules();\n"
  },
  {
    "path": "src/core/class-papi-core-conditional.php",
    "content": "<?php\n\n/**\n * Core class that handle if a conditional rule\n * should display the property or not.\n */\nclass Papi_Core_Conditional {\n\n\t/**\n\t * Available relations.\n\t *\n\t * @var array\n\t */\n\tprotected $relations = [\n\t\t'AND',\n\t\t'OR'\n\t];\n\n\t/**\n\t * Check if the property should be displayed by the rules.\n\t *\n\t * @param  array $rules\n\t * @param  Papi_Core_Property $property\n\t *\n\t * @return bool\n\t */\n\tpublic function display( array $rules, $property = null ) {\n\t\tif ( empty( $rules ) ) {\n\t\t\treturn true;\n\t\t}\n\n\t\t$rules  = $this->prepare_rules( $rules, $property );\n\n\t\tif ( in_array( $rules['relation'], $this->relations, true ) ) {\n\t\t\treturn $this->display_by_relation( $rules );\n\t\t}\n\n\t\treturn true;\n\t}\n\n\t/**\n\t * Get the display by relation.\n\t *\n\t * @param  array $rules\n\t *\n\t * @return bool\n\t */\n\tprotected function display_by_relation( array $rules ) {\n\t\tif ( $rules['relation'] === 'AND' ) {\n\t\t\t$display = true;\n\n\t\t\tforeach ( $rules as $rule ) {\n\t\t\t\tif ( ! $display ) {\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\n\t\t\t\tif ( papi_is_rule( $rule ) ) {\n\t\t\t\t\t/**\n\t\t\t\t\t * Modify rule allowed.\n\t\t\t\t\t *\n\t\t\t\t\t * @param  bool $result\n\t\t\t\t\t * @param  Papi_Core_Conditional_Rule $rule\n\t\t\t\t\t *\n\t\t\t\t\t * @return bool\n\t\t\t\t\t */\n\t\t\t\t\t$display = apply_filters( 'papi/conditional/rule_allowed', papi_filter_conditional_rule_allowed( $rule ), $rule );\n\t\t\t\t}\n\t\t\t}\n\n\t\t\treturn $display;\n\t\t}\n\n\t\t$empty = array_filter( $rules, function ( $rule ) {\n\t\t\treturn papi_is_rule( $rule ) ? true : null;\n\t\t} );\n\n\t\tif ( empty( $empty ) ) {\n\t\t\treturn true;\n\t\t}\n\n\t\t$result = [];\n\n\t\tforeach ( $rules as $rule ) {\n\t\t\tif ( papi_is_rule( $rule ) ) {\n\t\t\t\t/**\n\t\t\t\t * Modify rule allowed.\n\t\t\t\t *\n\t\t\t\t * @param  bool $result\n\t\t\t\t * @param  Papi_Core_Conditional_Rule $rule\n\t\t\t\t *\n\t\t\t\t * @return bool\n\t\t\t\t */\n\t\t\t\t$result[] = apply_filters( 'papi/conditional/rule_allowed', papi_filter_conditional_rule_allowed( $rule ), $rule );\n\t\t\t}\n\t\t}\n\n\t\t$result = array_filter( $result, function ( $res ) {\n\t\t\treturn $res === true ? true : null;\n\t\t} );\n\n\t\treturn ! empty( $result );\n\t}\n\n\t/**\n\t * Get rule slug.\n\t *\n\t * @param  Papi_Core_Conditional_Rule $rule\n\t * @param  Papi_Core_Property $property\n\t *\n\t * @return string\n\t */\n\tprotected function get_rule_slug( $rule, $property ) {\n\t\t$arr_reg = '/\\[\\d+\\](\\[\\w+\\])$/';\n\t\t$slug    = $property->get_slug();\n\n\t\t$page_type = papi_get_entry_type_by_meta_id();\n\n\t\tif ( $page_type instanceof Papi_Page_Type === false ) {\n\t\t\treturn $rule->slug;\n\t\t}\n\n\t\tif ( preg_match( $arr_reg, $slug, $out ) ) {\n\t\t\t$slug     = str_replace( $out[1], '[' . unpapify( $rule->slug ) . ']', $slug );\n\t\t\t$property = $page_type->get_property( $slug );\n\n\t\t\tif ( papi_is_property( $property ) ) {\n\t\t\t\treturn $slug;\n\t\t\t}\n\t\t}\n\n\t\treturn $rule->slug;\n\t}\n\n\t/**\n\t * Prepare rules.\n\t *\n\t * @param  array $rules\n\t * @param  Papi_Core_Property $property\n\t *\n\t * @return array\n\t */\n\tpublic function prepare_rules( array $rules, $property = null ) {\n\t\tif ( ! isset( $rules['relation'] ) ) {\n\t\t\t$rules['relation'] = 'OR';\n\t\t} else {\n\t\t\t$rules['relation'] = strtoupper( $rules['relation'] );\n\t\t}\n\n\t\tforeach ( $rules as $index => $value ) {\n\t\t\tif ( is_string( $index ) ) {\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tif ( is_array( $value ) ) {\n\t\t\t\t$rules[$index] = new Papi_Core_Conditional_Rule( $value );\n\n\t\t\t\tif ( strpos( $rules[$index]->slug, '.' ) === false && papi_is_property( $property ) ) {\n\t\t\t\t\t$rules[$index]->slug = $this->get_rule_slug(\n\t\t\t\t\t\t$rules[$index],\n\t\t\t\t\t\t$property\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\treturn $rules;\n\t}\n}\n"
  },
  {
    "path": "src/core/class-papi-core-container.php",
    "content": "<?php\n\n/**\n * Container class that implements a container.\n */\nclass Papi_Core_Container implements ArrayAccess {\n\n\t/**\n\t * The classes holder.\n\t *\n\t * @var array\n\t */\n\tprotected $classes = [];\n\n\t/**\n\t * The keys holder.\n\t *\n\t * @var array\n\t */\n\tprotected $keys = [];\n\n\t/**\n\t * The values holder.\n\t *\n\t * @var array\n\t */\n\tprotected $values = [];\n\n\t/**\n\t * Set a parameter or an object.\n\t *\n\t * @param string $id\n\t * @param mixed  $value\n\t * @param bool   $singleton\n\t *\n\t * @throws Exception when singleton cannot be rebind.\n\t *\n\t * @return mixed\n\t */\n\tpublic function bind( $id, $value = null, $singleton = false ) {\n\t\tif ( is_string( $id ) && $this->is_singleton( $id ) ) {\n\t\t\tthrow new Exception( sprintf( 'Identifier `%s` is a singleton and cannot be rebind', $id ) );\n\t\t}\n\n\t\tif ( is_object( $id ) && get_class( $id ) !== false ) {\n\t\t\t$value              = $id;\n\t\t\t$id                 = $this->get_class_prefix( get_class( $id ), false );\n\t\t\t$this->classes[$id] = true;\n\t\t}\n\n\t\tif ( $value instanceof Closure ) {\n\t\t\t$closure = $value;\n\t\t} else {\n\t\t\t$closure = $this->get_closure( $value, $singleton );\n\t\t}\n\n\t\t$this->values[$id] = compact( 'closure', 'singleton' );\n\t\t$this->keys[$id]   = true;\n\n\t\treturn $value;\n\t}\n\n\t/**\n\t * Call closure.\n\t *\n\t * @param mixed $closure\n\t * @param array $parameters\n\t *\n\t * @return Closure\n\t */\n\tprotected function call_closure( $closure, array $parameters = [] ) {\n\t\tif ( $closure instanceof Closure ) {\n\t\t\t$rc      = new ReflectionFunction( $closure );\n\t\t\t$args    = $rc->getParameters();\n\t\t\t$params  = $parameters;\n\t\t\t$classes = [\n\t\t\t\t$this->get_class_prefix( get_class( $this ) ),\n\t\t\t\tget_class( $this ),\n\t\t\t\tget_parent_class( $this )\n\t\t\t];\n\n\t\t\tforeach ( $args as $index => $arg ) {\n\t\t\t\tif ( $arg->getClass() === null ) {\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\n\t\t\t\tif ( in_array( $arg->getClass()->name, $classes, true ) ) {\n\t\t\t\t\t$parameters[$index] = $this;\n\t\t\t\t} else if ( $this->exists( $arg->getClass()->name ) ) {\n\t\t\t\t\t$parameters[$index] = $this->make( $arg->getClass()->name );\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif ( ! empty( $args ) && empty( $parameters ) ) {\n\t\t\t\t$parameters[0] = $this;\n\t\t\t}\n\n\t\t\tif ( count( $args ) > count( $parameters ) ) {\n\t\t\t\t$parameters = array_merge( $parameters, $params );\n\t\t\t}\n\n\t\t\treturn $this->call_closure( call_user_func_array( $closure, $parameters ), $parameters );\n\t\t}\n\n\t\treturn $closure;\n\t}\n\n\t/**\n\t * Check if identifier is set or not.\n\t *\n\t * @param string $id\n\t *\n\t * @return bool\n\t */\n\tpublic function exists( $id ) {\n\t\treturn isset( $this->keys[$this->get_class_prefix( $id )] );\n\t}\n\n\t/**\n\t * Get closure function.\n\t *\n\t * @param mixed $value\n\t * @param bool  $singleton\n\t *\n\t * @return Closure\n\t */\n\tprotected function get_closure( $value, $singleton = false ) {\n\t\treturn function () use ( $value, $singleton ) {\n\t\t\treturn $value;\n\t\t};\n\t}\n\n\t/**\n\t * Get class prefix.\n\t *\n\t * @param string $id\n\t * @param bool   $check\n\t *\n\t * @return string\n\t */\n\tprotected function get_class_prefix( $id, $check = true ) {\n\t\tif ( strpos( $id, '\\\\' ) !== false && $id[0] !== '\\\\' ) {\n\t\t\t$class = '\\\\' . $id;\n\n\t\t\tif ( $check ) {\n\t\t\t\treturn isset( $this->classes[$class] ) ? $class : $id;\n\t\t\t}\n\n\t\t\treturn $class;\n\t\t}\n\n\t\treturn $id;\n\t}\n\n\t/**\n\t * Determine if a given type is a singleton or not.\n\t *\n\t * @param string $id\n\t *\n\t * @throws InvalidArgumentException if argument is not string.\n\t *\n\t * @return bool\n\t */\n\tpublic function is_singleton( $id ) {\n\t\tif ( ! is_string( $id ) ) {\n\t\t\tthrow new InvalidArgumentException( 'Invalid argument. Must be string.' );\n\t\t}\n\n\t\tif ( ! $this->exists( $id ) ) {\n\t\t\treturn false;\n\t\t}\n\n\t\t$id = $this->get_class_prefix( $id );\n\n\t\treturn $this->values[$id]['singleton'] === true;\n\t}\n\n\t/**\n\t * Resolve the given type from the container.\n\t *\n\t * @param string $id\n\t * @param array  $parameters\n\t *\n\t * @throws Exception if identifier is not defined.\n\t *\n\t * @return Closure\n\t */\n\tpublic function make( $id, array $parameters = [] ) {\n\t\tif ( ! $this->exists( $id ) ) {\n\t\t\tthrow new Exception( sprintf( 'Identifier `%s` is not defined', $id ) );\n\t\t}\n\n\t\t$id      = $this->get_class_prefix( $id );\n\t\t$value   = $this->values[$id];\n\t\t$closure = $value['closure'];\n\n\t\treturn $this->call_closure( $closure, $parameters );\n\t}\n\n\t/**\n\t * Add key and value to the container once.\n\t *\n\t * @param  string $key\n\t * @param  mixed  $callback\n\t *\n\t * @return mixed\n\t */\n\tpublic function once( $key, $callback ) {\n\t\tif ( ! is_string( $key ) && ! is_callable( $callback ) ) {\n\t\t\treturn;\n\t\t}\n\n\t\tif ( ! $this->exists( $key ) ) {\n\t\t\t$result = $callback();\n\t\t\t$this->singleton( $key, $result );\n\t\t}\n\n\t\treturn $this->make( $key );\n\t}\n\n\t/**\n\t * Unset value by identifier.\n\t *\n\t * @param string $id\n\t */\n\tpublic function remove( $id ) {\n\t\t$id = $this->get_class_prefix( $id );\n\t\tunset( $this->keys[$id], $this->values[$id] );\n\t}\n\n\t/**\n\t * Reset container values.\n\t */\n\tpublic function reset() {\n\t\tunset( $this->keys, $this->values, $this->classes );\n\t}\n\n\t/**\n\t * Set a parameter or an object.\n\t *\n\t * @param string $id\n\t * @param mixed  $value\n\t *\n\t * @return mixed\n\t */\n\tpublic function singleton( $id, $value = null ) {\n\t\treturn $this->bind( $id, $value, true );\n\t}\n\n\t/**\n\t * Check if identifier is set or not.\n\t *\n\t * @codeCoverageIgnore\n\t *\n\t * @param string $id\n\t *\n\t * @return bool\n\t */\n\tpublic function offsetExists( $id ) {\n\t\treturn $this->exists( $id );\n\t}\n\n\t/**\n\t * Get value by identifier.\n\t *\n\t * @codeCoverageIgnore\n\t *\n\t * @param string $id\n\t *\n\t * @return mixed\n\t */\n\tpublic function offsetGet( $id ) {\n\t\treturn $this->make( $id );\n\t}\n\n\t/**\n\t * Set a parameter or an object.\n\t *\n\t * @codeCoverageIgnore\n\t *\n\t * @param string $id\n\t * @param mixed  $value\n\t */\n\tpublic function offsetSet( $id, $value ) {\n\t\t$this->bind( $id, $value );\n\t}\n\n\t/**\n\t * Unset value by identifier.\n\t *\n\t * @codeCoverageIgnore\n\t *\n\t * @param string $id\n\t */\n\tpublic function offsetUnset( $id ) {\n\t\t$this->remove( $id );\n\t}\n}\n"
  },
  {
    "path": "src/core/class-papi-core-data-handler.php",
    "content": "<?php\n\n/**\n * Core class that handle core post data.\n */\nclass Papi_Core_Data_Handler {\n\n\t/**\n\t * The fields that should be overwritten.\n\t *\n\t * @var array\n\t */\n\tprotected $overwrite = [];\n\n\t/**\n\t * The constructor.\n\t */\n\tpublic function __construct() {\n\t\t$this->setup_actions();\n\t}\n\n\t/**\n\t * Decode property.\n\t *\n\t * @param string $key\n\t * @param string $value\n\t *\n\t * @return mixed\n\t */\n\tprotected function decode_property( $key, $value ) {\n\t\tif ( papi_is_property_type_key( $key ) && is_string( $value ) ) {\n\t\t\t$value = base64_decode( $value );\n\t\t\t$value = papi_maybe_json_decode( $value );\n\t\t}\n\n\t\treturn $value;\n\t}\n\n\t/**\n\t * Get post data.\n\t *\n\t * @param  string $pattern\n\t *\n\t * @return array\n\t */\n\tprotected function get_post_data( $pattern = '/^papi\\_.*/' ) {\n\t\t$data = [];\n\t\t$keys = preg_grep( $pattern, array_keys( $_POST ) );\n\n\t\tforeach ( $keys as $key ) {\n\t\t\t// Remove page type keys with suffix. This should not be saved.\n\t\t\tif (\n\t\t\t\tstrpos( $key, papi_get_page_type_key() ) === 0\n\t\t\t\t&&\n\t\t\t\tstrlen( papi_get_page_type_key() ) !== strlen( $key )\n\t\t\t) {\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\t// Fix for input fields that should be true on `on` value.\n\t\t\tif ( $_POST[$key] === 'on' ) {\n\t\t\t\t$data[$key] = true;\n\t\t\t} else {\n\t\t\t\t$value = $this->decode_property( $key, $_POST[$key] );\n\t\t\t\t$data[$key] = $this->prepare_post_data( $value );\n\t\t\t\t$data[$key] = $this->santize_data( $data[$key] );\n\t\t\t}\n\t\t}\n\n\t\t// Don't wont to save meta nonce field.\n\t\tif ( isset( $data['papi_meta_nonce'] ) ) {\n\t\t\tunset( $data['papi_meta_nonce'] );\n\t\t}\n\n\t\treturn $data;\n\t}\n\n\t/**\n\t * Get pre data that should be saved before all properties data.\n\t */\n\tprotected function get_pre_data() {\n\t\treturn $this->get_post_data( '/^\\_papi\\_.*/' );\n\t}\n\n\t/**\n\t * Pre get deep keys and value.\n\t *\n\t * Used for saving pre data when properties are in a flexible or repeater.\n\t *\n\t * @param  array $arr\n\t *\n\t * @return array\n\t */\n\tprotected function get_pre_deep_keys_value( array $arr ) {\n\t\t$keys  = [];\n\t\t$value = null;\n\n\t\tforeach ( $arr as $key => $v ) {\n\t\t\tif ( is_array( $v ) ) {\n\t\t\t\t$keys[] = $key;\n\t\t\t\tlist( $ks, $val ) = $this->get_pre_deep_keys_value( $v );\n\t\t\t\t$keys   = array_merge( $keys, $ks );\n\t\t\t\t$value  = $val;\n\t\t\t} else {\n\t\t\t\t$keys[] = $key;\n\t\t\t\t$value  = $v;\n\t\t\t}\n\t\t}\n\n\t\treturn [$keys, $value];\n\t}\n\n\t/**\n\t * Prepare post data.\n\t * Will decode property options recursive.\n\t *\n\t * @param  mixed $data\n\t *\n\t * @return mixed\n\t */\n\tprotected function prepare_post_data( $data ) {\n\t\tif ( ! is_array( $data ) ) {\n\t\t\treturn $data;\n\t\t}\n\n\t\tforeach ( $data as $key => $value ) {\n\t\t\tif ( is_array( $value ) ) {\n\t\t\t\t$data[$key] = $this->prepare_post_data( $value );\n\t\t\t} else {\n\t\t\t\t$data[$key] = $this->decode_property( $key, $value );\n\t\t\t}\n\t\t}\n\n\t\treturn $data;\n\t}\n\n\t/**\n\t * Prepare properties data for saving.\n\t *\n\t * @param  array $data\n\t * @param  int   $post_id\n\t *\n\t * @return array\n\t */\n\tprotected function prepare_properties_data( array $data = [], $post_id = 0 ) {\n\t\t// Since we are storing witch property it is in the `$data` array\n\t\t// we need to remove that and set the property type to the property\n\t\t// and make a array of the property type and the value.\n\t\tforeach ( $data as $key => $value ) {\n\t\t\tif ( papi_is_property_type_key( $key ) ) {\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\t$property_type_key = papify( papi_get_property_type_key( $key ) );\n\n\t\t\t// Check if value exists.\n\t\t\tif ( ! isset( $data[$key] ) || ! isset( $data[$property_type_key] ) ) {\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\t// Pair property value with property type object.\n\t\t\t$data[$key] = [\n\t\t\t\t'type'  => $data[$property_type_key],\n\t\t\t\t'value' => $value\n\t\t\t];\n\n\t\t\t// Remove property type object since it's not needed anymore.\n\t\t\tunset( $data[$property_type_key] );\n\t\t}\n\n\t\tforeach ( $data as $key => $item ) {\n\t\t\tif ( papi_is_property_type_key( $key ) ) {\n\t\t\t\tif ( isset( $data[$key] ) ) {\n\t\t\t\t\t\tunset( $data[$key] );\n\t\t\t\t}\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tif ( empty( $item['type'] ) ) {\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\t$property = papi_get_property_type( $item['type'] );\n\n\t\t\tunset( $data[ $key ] );\n\n\t\t\tif ( papi_is_property( $property ) ) {\n\t\t\t\t// Run `update_value` method on the property class.\n\t\t\t\t$data[$key] = $property->update_value(\n\t\t\t\t\t$item['value'],\n\t\t\t\t\tunpapify( $key ),\n\t\t\t\t\t$post_id\n\t\t\t\t);\n\n\t\t\t\t// Apply `update_value` filter so this can be changed from\n\t\t\t\t// the theme for specified property type.\n\t\t\t\t$data[$key] = papi_filter_update_value(\n\t\t\t\t\t$item['type']->type,\n\t\t\t\t\t$data[$key],\n\t\t\t\t\tunpapify( $key ),\n\t\t\t\t\t$post_id,\n\t\t\t\t\tpapi_get_meta_type()\n\t\t\t\t);\n\n\t\t\t\tif ( $item['type']->overwrite ) {\n\t\t\t\t\t$slug = unpapify( $key );\n\t\t\t\t\t$this->overwrite[$slug] = $data[$key];\n\t\t\t\t\tunset( $data[$key] );\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\treturn $data;\n\t}\n\n\t/**\n\t * Sanitize data before saving it.\n\t *\n\t * @param  mixed $value\n\t *\n\t * @return mixed\n\t */\n\tprotected function santize_data( $value ) {\n\t\tif ( is_array( $value ) ) {\n\t\t\tforeach ( $value as $k => $v ) {\n\t\t\t\tif ( is_string( $v ) ) {\n\t\t\t\t\t$value[$k] = $this->santize_data( $v );\n\t\t\t\t}\n\t\t\t}\n\t\t} else if ( is_string( $value ) ) {\n\t\t\t$value = wp_unslash( $value );\n\t\t}\n\n\t\treturn $value;\n\t}\n\n\t/**\n\t * Setup actions.\n\t *\n\t * @codeCoverageIgnore\n\t */\n\tprotected function setup_actions() {\n\t}\n}\n"
  },
  {
    "path": "src/core/class-papi-core-data.php",
    "content": "<?php\n\nclass Papi_Core_Data {\n\n\t/**\n\t * Does the meta use id?\n\t *\n\t * @var bool\n\t */\n\tprotected $id = false;\n\n\t/**\n\t * Meta type.\n\t *\n\t * @var\n\t */\n\tprotected $type;\n\n\t/**\n\t * Property data constructor.\n\t *\n\t * @param $type\n\t */\n\tpublic function __construct( $type = 'post' ) {\n\t\t$this->type = is_string( $type ) ? papi_get_meta_type( $type ) : 'post';\n\t\t$this->id   = in_array( $this->type, ['post', 'term'], true );\n\t}\n\n\t/**\n\t * Delete property value.\n\t *\n\t * @param  int    $id\n\t * @param  string $slug\n\t *\n\t * @return bool\n\t */\n\tpublic function delete( $id, $slug ) {\n\t\t$fn = $this->get_function( 'delete' );\n\n\t\t// Check so the function is callable before using it.\n\t\tif ( ! is_callable( $fn ) ) {\n\t\t\treturn false;\n\t\t}\n\n\t\t// Delete cached value.\n\t\tpapi_cache_delete( $slug, $id, $this->type );\n\n\t\tif ( $this->id ) {\n\t\t\treturn call_user_func_array( $fn, [$this->type, $id, unpapify( $slug )] );\n\t\t}\n\n\t\treturn call_user_func_array( $fn, [unpapify( $slug )] );\n\t}\n\n\t/**\n\t * Get right meta function for right type and context.\n\t *\n\t * @param  string $context\n\t *\n\t * @return string\n\t */\n\tpublic function get_function( $context = 'get' ) {\n\t\tswitch ( $this->type ) {\n\t\t\tcase 'option':\n\t\t\t\treturn sprintf( '%s_option', $context );\n\t\t\tcase 'site':\n\t\t\tcase 'network':\n\t\t\t\treturn sprintf( '%s_site_option', $context );\n\t\t\tcase 'post':\n\t\t\tcase 'term':\n\t\t\t\treturn sprintf( '%s_metadata', $context );\n\t\t\tdefault:\n\t\t\t\tbreak;\n\t\t}\n\t}\n\n\t/**\n\t * Geta property value for right meta type.\n\t *\n\t * @param  int    $id\n\t * @param  string $slug\n\t *\n\t * @return mixed\n\t */\n\tpublic function get( $id, $slug ) {\n\t\t$fn = $this->get_function( 'get' );\n\n\t\tif ( ! is_callable( $fn ) ) {\n\t\t\treturn;\n\t\t}\n\n\t\tif ( $this->id ) {\n\t\t\t$value = call_user_func_array( $fn, [$this->type, $id, unpapify( $slug ), true] );\n\t\t} else {\n\t\t\t$value = call_user_func_array( $fn, [unpapify( $slug ), null] );\n\t\t}\n\n\t\tif ( papi_is_empty( $value ) ) {\n\t\t\treturn;\n\t\t}\n\n\t\treturn $value;\n\t}\n\n\t/**\n\t * Update property meta.\n\t *\n\t * @param  int    $id\n\t * @param  string $slug\n\t * @param  mixed  $value\n\t *\n\t * @return bool\n\t */\n\tpublic function update( $id, $slug, $value ) {\n\t\t$save_value = true;\n\n\t\t// Get right update function to use.\n\t\t$fn = $this->get_function( 'update' );\n\n\t\tif ( ! is_callable( $fn ) ) {\n\t\t\treturn false;\n\t\t}\n\n\t\t// Check for string keys in the array if any.\n\t\tforeach ( array_keys( papi_to_array( $value ) ) as $key ) {\n\t\t\tif ( is_string( $key ) ) {\n\t\t\t\t$save_value = false;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\n\t\t// If main value shouldn't be saved it should be array.\n\t\tif ( ! $save_value && is_array( $value ) ) {\n\t\t\t$value = [$value];\n\t\t}\n\n\t\t// Delete saved value if empty.\n\t\tif ( papi_is_empty( $value ) ) {\n\t\t\treturn $this->delete( $id, $slug );\n\t\t}\n\n\t\t$result = true;\n\n\t\tforeach ( papi_to_array( $value ) as $key => $val ) {\n\t\t\t// Delete saved value if value is empty.\n\t\t\tif ( papi_is_empty( $val ) || $val === '[]' || $val === '{}' ) {\n\t\t\t\treturn $this->delete( $id, $slug );\n\t\t\t}\n\n\t\t\t// Delete main value cache.\n\t\t\tpapi_cache_delete( $slug, $id, $this->type );\n\n\t\t\t// If not a array we can save the value.\n\t\t\tif ( ! is_array( $val ) ) {\n\t\t\t\tif ( $save_value ) {\n\t\t\t\t\t$val = $value;\n\t\t\t\t}\n\n\t\t\t\tif ( $this->id ) {\n\t\t\t\t\t$out    = call_user_func_array( $fn, [$this->type, $id, unpapify( $slug ), $val] );\n\t\t\t\t\t$result = $out ? $result : $out;\n\t\t\t\t} else {\n\t\t\t\t\t$out    = call_user_func_array( $fn, [unpapify( $slug ), $val] );\n\t\t\t\t\t$result = $out ? $result : $out;\n\t\t\t\t}\n\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\t// Clear cache for all child values.\n\t\t\t$this->update_clear_cache( $id, $value );\n\n\t\t\t// Update metadata or option value for all child values.\n\t\t\tforeach ( $val as $child_key => $child_value ) {\n\t\t\t\tif ( papi_is_empty( $child_value ) ) {\n\t\t\t\t\t$this->delete( $id, $child_key );\n\t\t\t\t} else {\n\t\t\t\t\tif ( $this->id ) {\n\t\t\t\t\t\tcall_user_func_array( $fn, [$this->type, $id, unpapify( $child_key ), $child_value] );\n\t\t\t\t\t} else {\n\t\t\t\t\t\tcall_user_func_array( $fn, [unpapify( $child_key ), $child_value] );\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\treturn $result;\n\t}\n\n\t/**\n\t * Clear cache values on update property meta.\n\t *\n\t * @param  int   $id\n\t * @param  mixed $value\n\t */\n\tpublic function update_clear_cache( $id, $value ) {\n\t\t$value = is_array( $value ) ? $value : [];\n\n\t\tforeach ( $value as $child_key => $child_value ) {\n\t\t\tpapi_cache_delete( $child_key, $id, $this->type );\n\n\t\t\tif ( is_array( $child_value ) ) {\n\t\t\t\t$this->update_clear_cache( $id, $child_value );\n\t\t\t}\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "src/core/class-papi-core-meta-store.php",
    "content": "<?php\n\n/**\n * Core class that implements a Papi meta store.\n */\nabstract class Papi_Core_Meta_Store {\n\n\t/**\n\t * The WordPress meta id if it exists.\n\t *\n\t * @var int\n\t */\n\tpublic $id;\n\n\t/**\n\t * Current properties.\n\t *\n\t * @var array\n\t */\n\tprotected $properties = [];\n\n\t/**\n\t * Properties meta values.\n\t *\n\t * @var array\n\t */\n\tprotected $meta_values = [];\n\n\t/**\n\t * The meta type.\n\t *\n\t * @var string\n\t */\n\tprotected $type = 'meta';\n\n\t/**\n\t * The type class.\n\t *\n\t * @var Papi_Core_Type\n\t */\n\tprotected $type_class;\n\n\t/**\n\t * Get property meta value.\n\t *\n\t * @param  string $slug\n\t *\n\t * @return mixed\n\t */\n\tpublic function get_property_meta_value( $slug ) {\n\t\t$slug = strtolower( $slug );\n\n\t\tif ( isset( $this->meta_values[$slug] ) ) {\n\t\t\treturn $this->meta_values[$slug];\n\t\t}\n\n\t\treturn papi_data_get( $this->id, $slug, $this->get_type() );\n\t}\n\n\t/**\n\t * Get meta type.\n\t *\n\t * @return string\n\t */\n\tpublic function get_type() {\n\t\treturn $this->type;\n\t}\n\n\t/**\n\t * Get type class.\n\t *\n\t * @return Papi_Core_Type\n\t */\n\tpublic function get_type_class() {\n\t\treturn $this->type_class;\n\t}\n\n\t/**\n\t * Get value.\n\t *\n\t * @param  int    $id\n\t * @param  string $slug\n\t * @param  mixed  $default\n\t * @param  string $type\n\t *\n\t * @return mixed\n\t */\n\tpublic function get_value( $id = null, $slug = null, $default = null, $type = 'post' ) {\n\t\tif ( ! is_numeric( $id ) && is_string( $id ) ) {\n\t\t\t$type    = empty( $default ) ? $type : $default;\n\t\t\t$default = $slug;\n\t\t\t$slug    = $id;\n\t\t\t$id      = null;\n\t\t}\n\n\t\t$slug = strtolower( $slug );\n\n\t\t// Determine if we should use the cache or not.\n\t\t$cache = $this->get_property_option( $slug, 'cache', true );\n\n\t\t// Get the raw value from the cache.\n\t\t$raw_value = $cache ? papi_cache_get( $slug, $id, $type ) : false;\n\n\t\t// Load raw value if not cached.\n\t\tif ( $raw_value === null || $raw_value === false ) {\n\t\t\t$raw_value = $this->load_value( $slug );\n\n\t\t\tif ( papi_is_empty( $raw_value ) ) {\n\t\t\t\treturn $default;\n\t\t\t}\n\n\t\t\tif ( $cache ) {\n\t\t\t\tpapi_cache_set( $slug, $id, $raw_value, $type );\n\t\t\t} else {\n\t\t\t\tpapi_cache_delete( $slug, $id, $type );\n\t\t\t}\n\t\t}\n\n\t\tif ( papi_is_empty( $raw_value ) ) {\n\t\t\treturn $default;\n\t\t}\n\n\t\t// Format raw value.\n\t\t$value = $this->format_value( $slug, $raw_value );\n\n\t\treturn papi_is_empty( $value ) ? $default : $value;\n\t}\n\n\t/**\n\t * Format property value from the property.\n\t *\n\t * @param  string $slug\n\t * @param  mixed  $value\n\t *\n\t * @return mixed\n\t */\n\tpublic function format_value( $slug, $value ) {\n\t\t$slug     = strtolower( unpapify( $slug ) );\n\t\t$property = $this->property( $slug );\n\n\t\t// If no property type is found, just return null.\n\t\tif ( ! papi_is_property( $property ) ) {\n\t\t\treturn;\n\t\t}\n\n\t\t// Format the value from the property class.\n\t\t$value = $property->format_value( $value, $slug, $this->id );\n\n\t\t// Only fired when not in admin.\n\t\tif ( ! papi_is_admin() ) {\n\t\t\t$value = papi_filter_format_value(\n\t\t\t\t$property->type,\n\t\t\t\t$value,\n\t\t\t\t$slug,\n\t\t\t\t$this->id,\n\t\t\t\tpapi_get_meta_type()\n\t\t\t);\n\t\t}\n\n\t\t// Modify value before it's return to the theme via a defined format callback on the property.\n\t\tif ( ! papi_is_admin() && is_callable( $property->format_cb ) ) {\n\t\t\t$value = call_user_func( $property->format_cb, $value );\n\t\t}\n\n\t\t// Remove empty values from arrays.\n\t\tif ( is_array( $value ) ) {\n\t\t\tforeach ( $value as $index => $val ) {\n\t\t\t\tif ( papi_is_empty( $val ) ) {\n\t\t\t\t\tunset( $value[$index] );\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\treturn $value;\n\t}\n\n\t/**\n\t * Get current property.\n\t *\n\t * @param  string $slug\n\t *\n\t * @return Papi_Core_Property\n\t */\n\tprotected function property( $slug = '' ) {\n\t\tif ( isset( $this->properties[$slug] ) && papi_is_property( $this->properties[$slug] ) ) {\n\t\t\t$property = $this->properties[$slug];\n\t\t} else {\n\t\t\t$property = $this->properties[$slug] = $this->get_property( $slug );\n\t\t}\n\n\t\t/**\n\t\t * Modify property.\n\t\t *\n\t\t * @param  Papi_Core_Property $property\n\t\t */\n\t\treturn apply_filters( 'papi/get_property', $property );\n\t}\n\n\t/**\n\t * Load property value from the property.\n\t *\n\t * @param  string $slug\n\t *\n\t * @return mixed\n\t */\n\tpublic function load_value( $slug ) {\n\t\t$slug     = strtolower( unpapify( $slug ) );\n\t\t$property = $this->property( $slug );\n\n\t\t// If no property type is found, just return null.\n\t\tif ( ! papi_is_property( $property ) ) {\n\t\t\treturn;\n\t\t}\n\n\t\t// Get raw property meta value.\n\t\t$value = $this->get_property_meta_value( $slug );\n\n\t\t// Prepare load value, when you have `overwrite => true`\n\t\t// this value will not exist in the database and that's\n\t\t// why we need to prepare (change) the value.\n\t\t$value = $this->prepare_load_value( $property, $value );\n\n\t\t// Bail if value is empty and option value is empty.\n\t\tif ( papi_is_empty( $value ) ) {\n\t\t\tif ( ! papi_is_empty( $property->get_option( 'value' ) ) ) {\n\t\t\t\treturn $property->get_option( 'value' );\n\t\t\t}\n\n\t\t\treturn;\n\t\t}\n\n\t\t// A property need to know about the store.\n\t\t$this->property( $slug )->set_store( $this );\n\n\t\t// Run load value method right after the value has been loaded from the database.\n\t\t$value = $property->load_value( $value, $slug, $this->id );\n\t\t$value = papi_filter_load_value(\n\t\t\t$property->type,\n\t\t\t$value,\n\t\t\t$slug,\n\t\t\t$this->id,\n\t\t\tpapi_get_meta_type()\n\t\t);\n\n\t\t// Remove empty values from arrays.\n\t\tif ( is_array( $value ) ) {\n\t\t\tforeach ( $value as $index => $val ) {\n\t\t\t\tif ( papi_is_empty( $val ) ) {\n\t\t\t\t\tunset( $value[$index] );\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\treturn $value;\n\t}\n\n\t/**\n\t * Get store from factory.\n\t *\n\t * @param  int    $post_id\n\t * @param  string $type\n\t *\n\t * @return mixed\n\t */\n\tpublic static function factory( $post_id, $type = 'post' ) {\n\t\t$type         = papi_get_meta_type( $type );\n\t\t$class_suffix = '_' . ucfirst( $type ) . '_Store';\n\t\t$class_name   = 'Papi' . $class_suffix;\n\n\t\tif ( ! class_exists( $class_name ) ) {\n\t\t\treturn;\n\t\t}\n\n\t\t$post_id = papi_get_post_id( $post_id );\n\t\t$page    = new $class_name( $post_id );\n\n\t\tif ( ! $page->valid() ) {\n\t\t\treturn;\n\t\t}\n\n\t\treturn $page;\n\t}\n\n\t/**\n\t * Get property from entry type.\n\t *\n\t * @param  string $slug\n\t * @param  string $child_slug\n\t *\n\t * @return Papi_Core_Property\n\t */\n\tabstract public function get_property( $slug, $child_slug = '' );\n\n\t/**\n\t * Get property option or default value.\n\t *\n\t * @param  string $slug\n\t * @param  string $option\n\t * @param  mixed  $default\n\t *\n\t * @return bool\n\t */\n\tpublic function get_property_option( $slug, $option, $default = null ) {\n\t\t$slug     = unpapify( $slug );\n\t\t$property = $this->property( $slug );\n\n\t\t// If no property type is found, return default\n\t\t// value since we don't have a property.\n\t\tif ( ! papi_is_property( $property ) ) {\n\t\t\treturn $default;\n\t\t}\n\n\t\t$value = $property->get_option( $option );\n\n\t\tif ( papi_is_empty( $value ) ) {\n\t\t\treturn $default;\n\t\t}\n\n\t\treturn $value;\n\t}\n\n\t/**\n\t * Prepare load value.\n\t *\n\t * @param  Papi_Core_Property $property\n\t * @param  mixed              $value\n\t *\n\t * @return mixed\n\t */\n\tprotected function prepare_load_value( Papi_Core_Property $property, $value ) {\n\t\treturn $value;\n\t}\n\n\t/**\n\t * Prepare property before returning it.\n\t *\n\t * @param  Papi_Core_Property $property\n\t *\n\t * @return Papi_Core_Property\n\t */\n\tprotected function prepare_property( Papi_Core_Property $property ) {\n\t\t$property->set_store( $this );\n\n\t\treturn $property;\n\t}\n\n\t/**\n\t * Set property meta value.\n\t *\n\t * @param string $slug\n\t * @param mixed  $value\n\t */\n\tpublic function set_property_meta_value( $slug, $value ) {\n\t\t$this->meta_values[strtolower( $slug )] = $value;\n\t}\n\n\t/**\n\t * Check if it's a valid store.\n\t *\n\t * @return bool\n\t */\n\tabstract public function valid();\n}\n"
  },
  {
    "path": "src/core/class-papi-core-property.php",
    "content": "<?php\n\n/**\n * Core class that implements a Papi property.\n */\nclass Papi_Core_Property implements JsonSerializable {\n\n\t/**\n\t * The conditional class.\n\t *\n\t * @var Papi_Core_Conditional\n\t */\n\tprotected $conditional;\n\n\t/**\n\t * The convert type.\n\t *\n\t * @var string\n\t */\n\tpublic $convert_type = 'string';\n\n\t/**\n\t * Default options.\n\t *\n\t * @var array\n\t */\n\tprotected $default_options = [\n\t\t'after_class'   => '',\n\t\t'after_html'    => '',\n\t\t'auth_callback' => '__return_true',\n\t\t'before_class'  => '',\n\t\t'before_html'   => '',\n\t\t'cache'         => true,\n\t\t'capabilities'  => [],\n\t\t'default'       => null,\n\t\t'description'   => '',\n\t\t'disabled'      => false,\n\t\t'display'       => true,\n\t\t'format_cb'     => '',\n\t\t'lang'          => false,\n\t\t'layout'        => 'horizontal', // or 'vertical'\n\t\t'overwrite'     => false,\n\t\t'post_type'     => '',\n\t\t'raw'           => false,\n\t\t'required'      => false,\n\t\t'rules'         => [],\n\t\t'settings'      => [],\n\t\t'sidebar'       => true,\n\t\t'site_id'       => 0,\n\t\t'show_in_rest'  => true,\n\t\t'slug'          => '',\n\t\t'sort_order'    => -1,\n\t\t'title'         => '',\n\t\t'type'          => '',\n\t\t'value'         => null\n\t];\n\n\t/**\n\t * Option aliases.\n\t *\n\t * @var array\n\t */\n\tprotected $option_aliases = [\n\t\t'desc' => 'description',\n\t\t'sets' => 'settings',\n\t];\n\n\t/**\n\t * Default value.\n\t *\n\t * @var null\n\t */\n\tpublic $default_value;\n\n\t/**\n\t * Display the property in WordPress admin.\n\t *\n\t * @var bool\n\t */\n\tprotected $display = true;\n\n\t/**\n\t * Current property options object.\n\t *\n\t * @var stdClass\n\t */\n\tprotected $options;\n\n\t/**\n\t * The post id.\n\t *\n\t * @var int\n\t */\n\tprotected $post_id;\n\n\t/**\n\t * Determine if the property require a slug or not.\n\t *\n\t * @var bool\n\t */\n\tprotected $slug_required = true;\n\n\t/**\n\t * The store that the property works with to get data.\n\t *\n\t * @var Papi_Core_Meta_Store\n\t */\n\tprotected $store;\n\n\t/**\n\t * Parent property.\n\t *\n\t * @var Papi_Core_Property\n\t */\n\tprotected $parent_property = null;\n\n\t/**\n\t * The constructor.\n\t */\n\tpublic function __construct() {\n\t\t$this->setup_actions();\n\t\t$this->setup_filters();\n\t\t$this->setup_properties();\n\t}\n\n\t/**\n\t * Get option value dynamic.\n\t *\n\t * @param  string $key\n\t *\n\t * @return mixed\n\t */\n\tpublic function __get( $key ) {\n\t\treturn $this->get_option( $key );\n\t}\n\n\t/**\n\t * Check if options value exists or not.\n\t *\n\t * @param  string $key\n\t *\n\t * @return bool\n\t */\n\tpublic function __isset( $key ) {\n\t\treturn $this->get_option( $key ) !== null;\n\t}\n\n\t/**\n\t * Set options value dynamic.\n\t *\n\t * @param string $key\n\t * @param mixed  $value\n\t */\n\tpublic function __set( $key, $value ) {\n\t\t$this->set_option( $key, $value );\n\t}\n\n\t/**\n\t * Determine if the current user has capabilities rights.\n\t *\n\t * @return bool\n\t */\n\tpublic function current_user_can() {\n\t\treturn papi_current_user_is_allowed( $this->capabilities );\n\t}\n\n\t/**\n\t * Delete value from the database.\n\t *\n\t * @param  string $slug\n\t * @param  int    $post_id\n\t * @param  string $type\n\t *\n\t * @return bool\n\t */\n\tpublic function delete_value( $slug, $post_id, $type ) {\n\t\treturn papi_data_delete( $post_id, $slug, $type );\n\t}\n\n\t/**\n\t * Determine if the property is disabled or not.\n\t *\n\t * @return bool\n\t */\n\tpublic function disabled() {\n\t\t// Return true if site id isn't zero and don't match the current site id.\n\t\tif ( $this->site_id !== 0 && $this->site_id !== get_current_blog_id() ) {\n\t\t\treturn true;\n\t\t}\n\n\t\t// If the post type don't match the current one\n\t\t// the property should not be rendered.\n\t\tif ( papi_get_meta_type() === 'post' && ! empty( $this->post_type ) && $this->post_type !== papi_get_post_type() ) {\n\t\t\treturn true;\n\t\t}\n\n\t\treturn $this->disabled;\n\t}\n\n\t/**\n\t * Determine if the property should be displayed.\n\t *\n\t * @return bool\n\t */\n\tpublic function display() {\n\t\t// If the property display is true it can be changed with\n\t\t// the display option.\n\t\treturn $this->display ? $this->options->display : false;\n\t}\n\n\t/**\n\t * Create a new instance of the given type or a\n\t * empty core property if no type is given.\n\t *\n\t * @return null|object\n\t */\n\tpublic static function factory() {\n\t\tif ( count( func_get_args() ) === 0 ) {\n\t\t\treturn new static;\n\t\t} else {\n\t\t\t$type = func_get_arg( 0 );\n\t\t}\n\n\t\tif ( is_array( $type ) ) {\n\t\t\t$type = (object) $type;\n\t\t}\n\n\t\tif ( ! is_string( $type ) && ! is_object( $type ) ) {\n\t\t\treturn;\n\t\t}\n\n\t\tif ( is_object( $type ) && ! isset( $type->type ) ) {\n\t\t\t$property = new static;\n\t\t\t$property->set_options( $type );\n\n\t\t\treturn $property;\n\t\t}\n\n\t\tif ( is_subclass_of( $type, __CLASS__ ) ) {\n\t\t\treturn $type;\n\t\t}\n\n\t\t$options = null;\n\n\t\tif ( is_object( $type ) ) {\n\t\t\tif ( ! isset( $type->type ) || ! is_string( $type->type ) ) {\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\t$options = $type;\n\t\t\t$type    = $type->type;\n\t\t}\n\n\t\t// Old types, 'PropertyString' => 'String'.\n\t\t$type = preg_replace( '/^Property/', '', $type );\n\n\t\tif ( empty( $type ) ) {\n\t\t\treturn;\n\t\t}\n\n\t\t$class_name = papi_get_property_class_name( $type );\n\n\t\tif ( ! class_exists( $class_name ) || ! is_subclass_of( $class_name, __CLASS__ ) ) {\n\t\t\treturn;\n\t\t}\n\n\t\tif ( ! papi()->exists( $class_name ) ) {\n\t\t\tpapi()->bind( $class_name, new $class_name() );\n\t\t}\n\n\t\t$class = papi()->make( $class_name );\n\n\t\t// @codeCoverageIgnoreStart\n\t\tif ( ! is_object( $class ) || $class instanceof Papi_Core_Property === false ) {\n\t\t\t$class = new $class_name();\n\t\t\tpapi()->bind( $class_name, $class );\n\t\t}\n\t\t// @codeCoverageIgnoreEnd\n\n\t\t$property = clone $class;\n\n\t\tif ( is_object( $options ) ) {\n\t\t\t$property->set_options( (array) $options );\n\t\t}\n\n\t\treturn $property;\n\t}\n\n\t/**\n\t * Format the value of the property before it's returned\n\t * to WordPress admin or the site.\n\t *\n\t * @param  mixed  $value\n\t * @param  string $slug\n\t * @param  int    $post_id\n\t *\n\t * @return mixed\n\t */\n\tpublic function format_value( $value, $slug, $post_id ) {\n\t\t$value = maybe_unserialize( $value );\n\n\t\treturn papi_maybe_json_decode( $value, $this->convert_type === 'array' );\n\t}\n\n\t/**\n\t * Get child property.\n\t *\n\t * @param  string $slug\n\t * @param  array  $items\n\t *\n\t * @return Papi_Core_Property|null\n\t */\n\tpublic function get_child_property( $slug, array $items = [] ) {\n\t\t$items = empty( $items ) ? $this->get_child_properties() : $items;\n\n\t\tforeach ( $items as $property ) {\n\t\t\tif ( is_array( $property ) && isset( $property['items'] ) ) {\n\t\t\t\t$property = $this->get_child_property( $slug, $property['items'] );\n\t\t\t}\n\n\t\t\t$property = papi_get_property_type( $property );\n\n\t\t\tif ( papi_is_property( $property ) && $property->match_slug( $slug ) ) {\n\t\t\t\treturn $property;\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * Get child properties from `items` in the settings array.\n\t *\n\t * @return array\n\t */\n\tpublic function get_child_properties() {\n\t\t$items = $this->get_setting( 'items', [] );\n\n\t\treturn papi_to_array( $items );\n\t}\n\n\t/**\n\t * Get convert type.\n\t *\n\t * @return string\n\t */\n\tpublic function get_convert_type() {\n\t\treturn $this->convert_type;\n\t}\n\n\t/**\n\t * Get default settings.\n\t *\n\t * @return array\n\t */\n\tpublic function get_default_settings() {\n\t\treturn [];\n\t}\n\n\t/**\n\t * Get meta type from the store or the default one.\n\t *\n\t * @return string\n\t */\n\tpublic function get_meta_type() {\n\t\t$store = $this->get_store();\n\n\t\treturn $store ? $store->get_type() : papi_get_meta_type();\n\t}\n\n\t/**\n\t * Get option value.\n\t *\n\t * @param  string $key\n\t * @param  mixed  $default\n\t *\n\t * @return mixed\n\t */\n\tpublic function get_option( $key, $default = null ) {\n\t\t$value = $default;\n\n\t\tif ( isset( $this->options->$key ) ) {\n\t\t\t$value = $this->options->$key;\n\t\t}\n\n\t\tif ( papi_is_empty( $value ) && isset( $this->default_options[$key] ) ) {\n\t\t\t$value = $this->default_options[$key];\n\t\t}\n\n\t\tif ( $key === 'settings' && is_array( $value ) ) {\n\t\t\t$value = (object) $value;\n\t\t}\n\n\t\treturn $value;\n\t}\n\n\t/**\n\t * Get the current property options object.\n\t *\n\t * @return stdClass\n\t */\n\tpublic function get_options() {\n\t\tif ( is_null( $this->options ) ) {\n\t\t\t$this->set_options();\n\t\t}\n\n\t\treturn $this->options;\n\t}\n\n\t/**\n\t * Get parent property.\n\t *\n\t * @return Papi_Core_Property\n\t */\n\tpublic function get_parent_property() {\n\t\treturn $this->parent_property;\n\t}\n\n\t/**\n\t * Get post id.\n\t *\n\t * @return int\n\t */\n\tpublic function get_post_id() {\n\t\tif ( ! papi_is_empty( $this->post_id ) ) {\n\t\t\treturn $this->post_id;\n\t\t}\n\n\t\tif ( $this->store instanceof Papi_Core_Meta_Store ) {\n\t\t\treturn $this->store->id;\n\t\t}\n\n\t\treturn papi_get_post_id();\n\t}\n\n\t/**\n\t * Get conditional rules.\n\t *\n\t * @return array\n\t */\n\tpublic function get_rules() {\n\t\treturn $this->rules;\n\t}\n\n\t/**\n\t * Get setting value.\n\t *\n\t * @param  string $key\n\t * @param  mixed  $default\n\t *\n\t * @return stdClass\n\t */\n\tpublic function get_setting( $key, $default = null ) {\n\t\tif ( ! is_string( $key ) ) {\n\t\t\treturn $default;\n\t\t}\n\n\t\t$settings = $this->get_settings();\n\n\t\tif ( isset( $settings->$key ) ) {\n\t\t\treturn $settings->$key;\n\t\t}\n\n\t\treturn $default;\n\t}\n\n\t/**\n\t * Get custom property settings.\n\t *\n\t * @return stdClass\n\t */\n\tpublic function get_settings() {\n\t\treturn (object) array_merge( $this->get_default_settings(), (array) $this->settings );\n\t}\n\n\t/**\n\t * Get property slug.\n\t *\n\t * @param  bool $remove_prefix\n\t *\n\t * @return string\n\t */\n\tpublic function get_slug( $remove_prefix = false ) {\n\t\tif ( $remove_prefix ) {\n\t\t\treturn unpapify( $this->slug );\n\t\t}\n\n\t\treturn $this->slug;\n\t}\n\n\t/**\n\t * Get the store that the property will get data from.\n\t *\n\t * @return Papi_Core_Meta_Store|null\n\t */\n\tpublic function get_store() {\n\t\tif ( $this->store instanceof Papi_Core_Meta_Store ) {\n\t\t\treturn $this->store;\n\t\t}\n\n\t\treturn papi_get_meta_store( $this->get_post_id() );\n\t}\n\n\t/**\n\t * Get value, no database connections here.\n\t *\n\t * @return mixed\n\t */\n\tpublic function get_value() {\n\t\treturn papi_is_empty( $this->value ) ? $this->default : $this->value;\n\t}\n\n\t/**\n\t * Get the html id attribute value.\n\t *\n\t * @param  object|string $suffix\n\t * @param  int           $row\n\t *\n\t * @return string\n\t */\n\tpublic function html_id( $suffix = '', $row = null ) {\n\t\tif ( is_array( $suffix ) || is_object( $suffix ) ) {\n\t\t\treturn papi_f( $this->html_name( $suffix, $row ) );\n\t\t} else {\n\t\t\t$suffix = empty( $suffix ) || ! is_string( $suffix ) ? '' : '_' . $suffix;\n\t\t\t$suffix = papi_underscorify( papi_slugify( $suffix ) );\n\t\t}\n\n\t\t$name = $this->html_name();\n\t\t$len  = strlen( $name );\n\n\t\tif ( isset( $name[$len - 1] ) && $name[$len - 1] === ']' ) {\n\t\t\treturn papi_f( sprintf( '%s%s]', substr( $name, 0, $len - 1 ), $suffix ) );\n\t\t}\n\n\t\treturn papi_f( sprintf( '%s%s', $this->html_name(), $suffix ) );\n\t}\n\n\t/**\n\t * Get html name for property with or without sub property and row number.\n\t *\n\t * @param  array|object $sub_property\n\t * @param  int          $row\n\t *\n\t * @return string\n\t */\n\tpublic function html_name( $sub_property = null, $row = null ) {\n\t\t$base_slug = $this->slug;\n\n\t\tif ( is_null( $sub_property ) ) {\n\t\t\treturn $base_slug;\n\t\t}\n\n\t\tif ( is_numeric( $row ) ) {\n\t\t\t$base_slug = sprintf( '%s[%d]', $base_slug, intval( $row ) );\n\t\t}\n\n\t\tif ( ! papi_is_property( $sub_property ) ) {\n\t\t\tif ( is_array( $sub_property ) || is_object( $sub_property ) ) {\n\t\t\t\t$sub_property = self::factory( $sub_property );\n\t\t\t} else {\n\t\t\t\treturn $base_slug;\n\t\t\t}\n\t\t}\n\n\t\treturn sprintf( '%s[%s]', $base_slug, unpapify( $sub_property->get_slug() ) );\n\t}\n\n\t/**\n\t * Change value after it's loaded from the database.\n\t *\n\t * @param  mixed  $value\n\t * @param  string $slug\n\t * @param  int    $post_id\n\t *\n\t * @return mixed\n\t */\n\tpublic function load_value( $value, $slug, $post_id ) {\n\t\t$value = maybe_unserialize( $value );\n\n\t\treturn papi_maybe_json_decode( $value, $this->convert_type === 'array' );\n\t}\n\n\t/**\n\t * Match property slug with given slug value.\n\t *\n\t * @param  string $slug\n\t *\n\t * @return bool\n\t */\n\tpublic function match_slug( $slug ) {\n\t\tif ( ! is_string( $slug ) ) {\n\t\t\treturn false;\n\t\t}\n\n\t\treturn $this->get_slug( ! preg_match( '/^papi\\_/', $slug ) ) === $slug;\n\t}\n\n\t/**\n\t * Prepare value before database.\n\t *\n\t * @param  mixed $value\n\t *\n\t * @return mixed\n\t */\n\tprotected function prepare_value( $value ) {\n\t\tif ( papi_is_empty( $value ) ) {\n\t\t\treturn;\n\t\t}\n\n\t\t$value = papi_santize_data( $value );\n\n\t\tif ( is_array( $value ) ) {\n\t\t\t$value = array_filter( $value, function ( $val ) {\n\t\t\t\treturn ! papi_is_empty( $val );\n\t\t\t} );\n\n\t\t\tif ( ! count( array_filter( array_keys( $value ), 'is_string' ) ) ) {\n\t\t\t\t$value = array_values( $value );\n\t\t\t}\n\t\t}\n\n\t\treturn $value;\n\t}\n\n\t/**\n\t * Register property with:\n\t *\n\t * - `register_meta` (WP 4.6+)\n\t *\n\t * @param  string $type\n\t *\n\t * @return bool\n\t */\n\tpublic function register( $type = 'post' ) {\n\t\tif ( version_compare( get_bloginfo( 'version' ), '4.6', '<' ) ) {\n\t\t\treturn false;\n\t\t}\n\n\t\t$type = papi_get_meta_type( $type );\n\n\t\t// Register option fields with the new `register_setting` function and only for WordPress 4.7.\n\t\tif ( $type === 'option' && version_compare( get_bloginfo( 'version' ), '4.7', '>=' ) ) {\n\t\t\t// The `type` will be the same for each fields, this is just to get it out\n\t\t\t// to the REST API, the output will be different for different fields and are\n\t\t\t// handled later on.\n\t\t\treturn register_setting( 'papi', $this->get_slug( true ), [\n\t\t\t\t'sanitize_callback' => [$this, 'register_meta_sanitize_callback'],\n\t\t\t\t'show_in_rest'      => $this->get_option( 'show_in_rest' ),\n\t\t\t\t'type'              => 'string'\n\t\t\t] );\n\t\t}\n\n\t\t// Register meta fields with the new `register_meta` function.\n\t\t// The `type` will be the same for each fields, this is just to get it out\n\t\t// to the REST API, the output will be different for different fields and are\n\t\t// handled later on.\n\t\treturn register_meta( $type, $this->get_slug( true ), [\n\t\t\t'auth_callback'     => $this->get_option( 'auth_callback' ),\n\t\t\t'description'       => $this->get_option( 'description' ),\n\t\t\t'sanitize_callback' => [$this, 'register_meta_sanitize_callback'],\n\t\t\t'show_in_rest'      => $this->get_option( 'show_in_rest' ),\n\t\t\t'single'            => $this->get_convert_type() !== 'array',\n\t\t\t'type'              => 'string'\n\t\t] );\n\t}\n\n\t/**\n\t * No need for this in Papi, since this is handle different.\n\t *\n\t * @param  mixed $value\n\t *\n\t * @return mixed\n\t */\n\tpublic function register_meta_sanitize_callback( $value ) {\n\t\treturn $value;\n\t}\n\n\t/**\n\t * Prepare property value for REST API response.\n\t *\n\t * @param  mixed $value\n\t *\n\t * @return mixed\n\t */\n\tpublic function rest_prepare_value( $value ) {\n\t\treturn $value;\n\t}\n\n\t/**\n\t * Render AJAX request.\n\t */\n\tpublic function render_ajax_request() {\n\t\tpapi_render_property( $this );\n\t}\n\n\t/**\n\t * Check if the property is allowed\n\t * to render by the conditional rules.\n\t *\n\t * @param  array $rules\n\t *\n\t * @return bool\n\t */\n\tpublic function render_is_allowed_by_rules( array $rules = [] ) {\n\t\tif ( empty( $rules ) ) {\n\t\t\t$rules = $this->get_rules();\n\t\t}\n\n\t\treturn $this->conditional->display( $rules, $this );\n\t}\n\n\t/**\n\t * Set the store that the property will get data from.\n\t *\n\t * @param Papi_Core_Meta_Store $store\n\t */\n\tpublic function set_store( Papi_Core_Meta_Store $store ) {\n\t\t$this->store = $store;\n\t}\n\n\t/**\n\t * Set post id.\n\t *\n\t * @param int $post_id\n\t */\n\tpublic function set_post_id( $post_id ) {\n\t\tif ( ! is_numeric( $post_id ) ) {\n\t\t\treturn;\n\t\t}\n\n\t\t$this->post_id = (int) $post_id;\n\t}\n\n\t/**\n\t * Set the current property options object.\n\t *\n\t * @param array|object $options\n\t */\n\tpublic function set_options( $options = [] ) {\n\t\t$this->options = $this->setup_options( $options );\n\t}\n\n\t/**\n\t * Set property option value.\n\t *\n\t * @param string $key\n\t * @param mixed  $value\n\t */\n\tpublic function set_option( $key, $value ) {\n\t\tif ( ! is_object( $this->options ) ) {\n\t\t\t$this->options = (object) $this->default_options;\n\t\t}\n\n\t\t$this->options->$key = $value;\n\t}\n\n\t/**\n\t * Set property setting value.\n\t *\n\t * @param string $key\n\t * @param mixed  $value\n\t */\n\tpublic function set_setting( $key, $value ) {\n\t\tif ( isset( $this->options->settings, $this->options->settings->$key ) ) {\n\t\t\t$this->options->settings->$key = $value;\n\t\t}\n\t}\n\n\t/**\n\t * Set parent property.\n\t *\n\t * @param Papi_Core_Property $parent_property\n\t */\n\tpublic function set_parent_property( Papi_Core_Property $parent_property ) {\n\t\t$this->parent_property = $parent_property;\n\t}\n\n\t/**\n\t * Setup actions.\n\t */\n\tprotected function setup_actions() {\n\t}\n\n\t/**\n\t * Setup filters.\n\t */\n\tprotected function setup_filters() {\n\t}\n\n\t/**\n\t * Setup properties.\n\t */\n\tprotected function setup_properties() {\n\t\t$this->conditional = new Papi_Core_Conditional();\n\n\t\tif ( $this->default_options['sort_order'] === -1 ) {\n\t\t\t$this->default_options['sort_order'] = papi_filter_settings_sort_order();\n\t\t}\n\n\t\tif ( papi_is_empty( $this->default_options['post_type'] ) ) {\n\t\t\t$this->default_options['post_type'] = papi_get_post_type();\n\t\t}\n\t}\n\n\t/**\n\t * Setup property options.\n\t *\n\t * @param  mixed $options\n\t *\n\t * @return mixed\n\t */\n\tprotected function setup_options( $options = [] ) {\n\t\t// When a object is sent in, just return it.\n\t\tif ( is_object( $options ) ) {\n\t\t\treturn $options;\n\t\t}\n\n\t\t// Only arrays can be handled.\n\t\tif ( ! is_array( $options ) ) {\n\t\t\t$options = [];\n\t\t}\n\n\t\t// Merge default options with the given options array.\n\t\t$options = array_merge( $this->default_options, $options );\n\t\t$options = (object) $options;\n\n\t\t// Setup aliases.\n\t\t$option_aliases = apply_filters( 'papi/option_aliases', $this->option_aliases );\n\t\t$option_aliases = is_array( $option_aliases ) ? $option_aliases : [];\n\t\t$option_aliases = array_merge( $this->option_aliases, $option_aliases );\n\n\t\tforeach ( $option_aliases as $alias => $option ) {\n\t\t\tif ( isset( $options->$alias ) && ! papi_is_empty( $options->$alias ) ) {\n\t\t\t\t$options->$option = $options->$alias;\n\t\t\t\tunset( $options->$alias );\n\t\t\t}\n\t\t}\n\n\t\t// Capabilities should be a array.\n\t\t$options->capabilities = papi_to_array( $options->capabilities );\n\n\t\t// Setup property slug.\n\t\t$options->slug = strtolower( $this->setup_options_slug( $options ) );\n\n\t\t// Setup property settings.\n\t\t$options->settings = $this->setup_options_settings( $options );\n\n\t\t// Type should always be lowercase.\n\t\t$options->type = strtolower( $options->type );\n\n\t\t// Escape all options except those that are send it as second argument.\n\t\treturn papi_esc_html( $options, ['before_html', 'html', 'after_html'] );\n\t}\n\n\t/**\n\t * Setup options slug.\n\t *\n\t * @param  stdClass $options\n\t *\n\t * @return string\n\t */\n\tprotected function setup_options_slug( $options ) {\n\t\t$slug = $options->slug;\n\n\t\t// When `slug` is false or not required a unique slug should be generated.\n\t\tif ( $slug === false || ( empty( $slug ) && $this->slug_required === false ) ) {\n\t\t\treturn '_' . papi_html_name( md5( uniqid( rand(), true ) ) );\n\t\t}\n\n\t\t// If `slug` is empty, check if `title` is not empty\n\t\t// and generate a slug from the `title` or if empty\n\t\t// use the `type`.\n\t\tif ( empty( $slug ) ) {\n\t\t\tif ( empty( $options->title ) ) {\n\t\t\t\t$slug = papi_slugify( $options->type );\n\t\t\t} else {\n\t\t\t\t$slug = papi_slugify( $options->title );\n\t\t\t}\n\t\t}\n\n\t\t// Create a html friendly name from the `slug`.\n\t\treturn papi_html_name( $slug );\n\t}\n\n\t/**\n\t * Setup options settings.\n\t *\n\t * @param  stdClass $options\n\t *\n\t * @return stdClass\n\t */\n\tprotected function setup_options_settings( $options ) {\n\t\t$property_class = self::factory( $options->type );\n\n\t\tif ( papi_is_property( $property_class ) ) {\n\t\t\t$options->settings = array_merge( (array) $property_class->get_default_settings(), (array) $options->settings );\n\t\t}\n\n\t\treturn (object) $options->settings;\n\t}\n\n\t/**\n\t * Update value before it's saved to the database.\n\t *\n\t * @param  mixed  $value\n\t * @param  string $slug\n\t * @param  int    $post_id\n\t *\n\t * @return mixed\n\t */\n\tpublic function update_value( $value, $slug, $post_id ) {\n\t\tif ( ! ( $value = $this->prepare_value( $value ) ) ) {\n\t\t\treturn;\n\t\t}\n\n\t\treturn papi_maybe_json_encode( $value );\n\t}\n\n\t/**\n\t * Get a string representation of the object.\n\t *\n\t * @return string\n\t */\n\tpublic function __toString() {\n\t\treturn $this->get_slug( true );\n\t}\n\n\t/**\n\t * Serialize property to json.\n\t *\n\t * @return mixed\n\t */\n\tpublic function jsonSerialize() {\n\t\treturn $this->get_options();\n\t}\n}\n"
  },
  {
    "path": "src/core/class-papi-core-tab.php",
    "content": "<?php\n\nclass Papi_Core_Tab {\n\n\t/**\n\t * The background of the tab.\n\t *\n\t * Possible values are: `white` or `grey`.\n\t *\n\t * By default if empty the background will be automatic,\n\t * if first property has no sidebar it'll be white and\n\t * if it has a sidebar it'll be grey.\n\t *\n\t * @var string\n\t */\n\tpublic $background = '';\n\n\t/**\n\t * Capabilities list.\n\t *\n\t * @var array\n\t */\n\tpublic $capabilities = [];\n\n\t/**\n\t * Tab icon.\n\t *\n\t * @var string\n\t */\n\tpublic $icon = '';\n\n\t/**\n\t * The core tab identifier.\n\t *\n\t * @var string\n\t */\n\tpublic $id = '';\n\n\t/**\n\t * Box properties.\n\t *\n\t * @var array\n\t */\n\tpublic $properties = [];\n\n\t/**\n\t * The sort order of the core box.\n\t *\n\t * @var int\n\t */\n\tpublic $sort_order = 1000;\n\n\t/**\n\t * Because of old code for tabs this\n\t * property is required to exists on\n\t * core tab class.\n\t *\n\t * @var bool\n\t */\n\tpublic $tab = true;\n\n\t/**\n\t * The title of the box.\n\t *\n\t * @var string\n\t */\n\tpublic $title = '';\n\n\t/**\n\t * The constructor.\n\t *\n\t * @param array $args\n\t * @param array $properties\n\t */\n\tpublic function __construct( array $args = [], array $properties = [] ) {\n\t\t$this->setup_args( $args );\n\t\t$this->setup_properties( $properties );\n\t}\n\n\t/**\n\t * Setup arguments.\n\t *\n\t * @param  array $args\n\t */\n\tprotected function setup_args( array $args ) {\n\t\tforeach ( $args as $key => $value ) {\n\t\t\tif ( isset( $this->$key ) ) {\n\t\t\t\t$this->$key = papi_esc_html( $value );\n\t\t\t}\n\t\t}\n\n\t\tif ( empty( $this->id ) ) {\n\t\t\t$this->id = strtolower( papi_f( papi_underscorify( papify( $this->title ) ) ) );\n\t\t}\n\t}\n\n\t/**\n\t * Setup properties.\n\t *\n\t * @param  array $properties\n\t */\n\tprotected function setup_properties( array $properties ) {\n\t\t$this->properties = array_merge( $this->properties, papi_populate_properties( $properties ) );\n\t}\n}\n"
  },
  {
    "path": "src/core/class-papi-core-type.php",
    "content": "<?php\n\n/**\n * Core class that implements a Papi type.\n */\nclass Papi_Core_Type {\n\n\t/**\n\t * The page type class name.\n\t *\n\t * @var string\n\t */\n\tprotected $_class_name = '';\n\n\t/**\n\t * The file path of the core type file.\n\t *\n\t * @var string\n\t */\n\tprotected $_file_path = '';\n\n\t/**\n\t * The type id.\n\t *\n\t * @var string\n\t */\n\tprotected $_id = '';\n\n\t/**\n\t * The name of the core type.\n\t *\n\t * @var string\n\t */\n\tpublic $name = '';\n\n\t/**\n\t * The sort order of the core type.\n\t *\n\t * @var int\n\t */\n\tpublic $sort_order = 1000;\n\n\t/**\n\t * The type name. Used for WP CLI.\n\t *\n\t * @var string\n\t */\n\tpublic $type = 'core';\n\n\t/**\n\t * The constructor.\n\t *\n\t * Load a core type by the file.\n\t *\n\t * @param string $file_path\n\t */\n\tpublic function __construct( $file_path = '' ) {\n\t\tif ( is_file( $file_path ) ) {\n\t\t\t$this->setup_file( $file_path );\n\t\t\t$this->setup_meta_data();\n\t\t}\n\t}\n\n\t/**\n\t * Determine if the entry type is allowed.\n\t *\n\t * @return bool\n\t */\n\tpublic function allowed() {\n\t\treturn true;\n\t}\n\n\t/**\n\t * Boot page type.\n\t *\n\t * @codeCoverageIgnore\n\t */\n\tpublic function boot() {\n\t\t$this->setup_actions();\n\t\t$this->setup_filters();\n\t}\n\n\t/**\n\t * Get the page type class name with namespace if exists.\n\t *\n\t * @return string\n\t */\n\tpublic function get_class_name() {\n\t\treturn $this->_class_name;\n\t}\n\n\t/**\n\t * Get the page type file pat.h\n\t *\n\t * @return string\n\t */\n\tpublic function get_file_path() {\n\t\treturn $this->_file_path;\n\t}\n\n\t/**\n\t * Get the page type identifier.\n\t *\n\t * @return string\n\t */\n\tpublic function get_id() {\n\t\tif ( ! empty( $this->_id ) ) {\n\t\t\treturn $this->_id;\n\t\t}\n\n\t\treturn papi_get_core_type_base_path( $this->_file_path );\n\t}\n\n\t/**\n\t * Get meta data from type class and merge\n\t * with the parent meta data.\n\t *\n\t * @return array\n\t */\n\tprotected function get_meta() {\n\t\t$method = 'meta';\n\n\t\tif ( ! method_exists( $this, $method ) ) {\n\t\t\treturn [];\n\t\t}\n\n\t\t$child_meta = call_user_func( [$this, $method] );\n\t\t$child_meta = is_array( $child_meta ) ? $child_meta : [];\n\n\t\t$parent_class  = get_parent_class( $this );\n\t\t$parent_exists = method_exists( $parent_class, $method );\n\t\t$parent_meta   = [];\n\n\t\twhile ( $parent_exists ) {\n\t\t\t$rc = new ReflectionClass( $parent_class );\n\n\t\t\t// Bail if not instantiable.\n\t\t\tif ( ! $rc->isInstantiable() ) {\n\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\t$parent        = $rc->newInstance();\n\t\t\t$output        = call_user_func( [$parent, $method] );\n\t\t\t$output        = is_array( $output ) ? $output : [];\n\t\t\t$parent_meta   = array_merge( $parent_meta, $output );\n\t\t\t$parent_class  = get_parent_class( $parent_class );\n\t\t\t$parent_exists = method_exists( $parent_class, $method );\n\t\t}\n\n\t\treturn array_merge( $parent_meta, $child_meta );\n\t}\n\n\t/**\n\t * Get type name.\n\t *\n\t * @return string\n\t */\n\tpublic function get_type() {\n\t\treturn strtolower( $this->type );\n\t}\n\n\t/**\n\t * Check so we have a name on the page type.\n\t *\n\t * @return bool\n\t */\n\tpublic function has_name() {\n\t\treturn ! empty( $this->name );\n\t}\n\n\t/**\n\t * Check if the the given identifier match the page type identifier.\n\t *\n\t * @param  string $id\n\t *\n\t * @return bool\n\t */\n\tpublic function match_id( $id ) {\n\t\treturn $this->get_id() === $id;\n\t}\n\n\t/**\n\t * Create a new instance of the page type file.\n\t *\n\t * @return null|object\n\t */\n\tpublic function new_class() {\n\t\tif ( empty( $this->_file_path ) ) {\n\t\t\treturn;\n\t\t}\n\n\t\treturn new $this->_class_name;\n\t}\n\n\t/**\n\t * Set custom id.\n\t *\n\t * @param  string $id\n\t */\n\tpublic function set_id( $id ) {\n\t\t$this->_id = $id;\n\t}\n\n\t/**\n\t * Setup actions.\n\t *\n\t * @codeCoverageIgnore\n\t */\n\tprotected function setup_actions() {\n\t}\n\n\t/**\n\t * Load the file and setup file path, file name and class name properties.\n\t *\n\t * @param string $file_path\n\t */\n\tprotected function setup_file( $file_path ) {\n\t\t$this->_file_path  = $file_path;\n\t\t$this->_class_name = papi_get_class_name( $this->_file_path );\n\t}\n\n\t/**\n\t * Setup filters.\n\t *\n\t * @codeCoverageIgnore\n\t */\n\tprotected function setup_filters() {\n\t}\n\n\t/**\n\t * Setup meta data.\n\t */\n\tprotected function setup_meta_data() {\n\t\tforeach ( $this->get_meta() as $key => $value ) {\n\t\t\tif ( substr( $key, 0, 1 ) === '_' ) {\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\t$this->$key = papi_esc_html( $value );\n\t\t}\n\n\t\tif ( $this->sort_order === 1000 ) {\n\t\t\t$this->sort_order = papi_filter_settings_sort_order();\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "src/lib/core/cache.php",
    "content": "<?php\n\n/**\n * Delete value from cache.\n *\n * @param  string $key\n * @param  mixed  $suffix\n * @param  string $type\n *\n * @return bool\n */\nfunction papi_cache_delete( $key, $suffix, $type = 'post' ) {\n\t$key = papi_cache_key( $key, $suffix, $type );\n\t$out = true;\n\n\tif ( papi_is_admin() ) {\n\t\t$out = wp_cache_delete( 'admin_' . $key );\n\t}\n\n\treturn $out ? wp_cache_delete( $key ) : $out;\n}\n\n/**\n * Get value from cache.\n *\n * @param  string $key\n * @param  mixed  $suffix\n * @param  string $type\n *\n * @return bool\n */\nfunction papi_cache_get( $key, $suffix = '', $type = 'post' ) {\n\t$key = papi_cache_key( $key, $suffix, $type );\n\n\tif ( papi_is_admin() ) {\n\t\t$key = 'admin_' . $key;\n\t}\n\n\treturn wp_cache_get( $key );\n}\n\n/**\n * Get Papi cache key.\n *\n * @param  string $key\n * @param  mixed  $suffix\n * @param  string $type\n *\n * @return string\n */\nfunction papi_cache_key( $key, $suffix, $type = 'post' ) {\n\tif ( ! is_string( $key ) ) {\n\t\treturn '';\n\t}\n\n\t$type   = empty( $type ) ? 'post' : $type;\n\t$type   = $type === 'page' ? 'post' : $type;\n\t$key    = unpapify( $key );\n\t$key    = papify( $type . '_' . $key );\n\t$suffix = papi_convert_to_string( $suffix );\n\t$suffix = papi_html_name( $suffix );\n\t$suffix = unpapify( $suffix );\n\n\treturn sprintf( '%s_%s', $key, $suffix );\n}\n\n/**\n * Set value in cache.\n *\n * @param  string $key\n * @param  mixed  $suffix\n * @param  mixed  $value\n * @param  string $type\n *\n * @return bool\n */\nfunction papi_cache_set( $key, $suffix, $value, $type = 'post' ) {\n\t$key = papi_cache_key( $key, $suffix, $type );\n\n\tif ( papi_is_admin() ) {\n\t\t$key = 'admin_' . $key;\n\t}\n\n\treturn wp_cache_set( $key, $value );\n}\n"
  },
  {
    "path": "src/lib/core/conditional.php",
    "content": "<?php\n\n/**\n * Check if the given value is a instance of the rule class.\n *\n * @param  mixed $rule\n *\n * @return bool\n */\nfunction papi_is_rule( $rule ) {\n\treturn $rule instanceof Papi_Core_Conditional_Rule;\n}\n\n/**\n * Get conditional rule.\n *\n * @param  array|Papi_Core_Conditional_Rule $rule\n *\n * @return Papi_Core_Conditional_Rule\n */\nfunction papi_rule( $rule ) {\n\tif ( is_array( $rule ) && ! empty( $rule ) ) {\n\t\treturn new Papi_Core_Conditional_Rule( $rule );\n\t}\n\n\tif ( $rule instanceof Papi_Core_Conditional_Rule ) {\n\t\treturn $rule;\n\t}\n}\n"
  },
  {
    "path": "src/lib/core/data.php",
    "content": "<?php\n\n/**\n * Delete data from database.\n *\n * @param  int    $id\n * @param  string $slug\n * @param  string $type\n *\n * @return mixed\n */\nfunction papi_data_delete( $id, $slug, $type = 'post' ) {\n\treturn papi()->data( $type )->delete( $id, $slug, $type );\n}\n\n/**\n * Get data from database.\n *\n * @param  int    $id\n * @param  string $slug\n * @param  string $type\n */\nfunction papi_data_get( $id, $slug, $type = 'post' ) {\n\treturn papi()->data( $type )->get( $id, $slug );\n}\n\n/**\n * Updata data in database.\n *\n * @param  int    $id\n * @param  string $slug\n * @param  mixed  $value\n * @param  string $type\n *\n * @return bool\n */\nfunction papi_data_update( $id, $slug, $value, $type = 'post' ) {\n\treturn papi()->data( $type )->update( $id, $slug, $value );\n}\n"
  },
  {
    "path": "src/lib/core/deprecated.php",
    "content": "<?php\n\n/**\n * This file is empty for now.\n * It will contain deprecated functions.\n */\n\n/**\n * Get the data page.\n *\n * @param  int    $id\n * @param  string $type\n *\n * @return Papi_Core_Meta_Store\n */\nfunction papi_get_page( $id = 0, $type = 'post' ) {\n\t_deprecated_function( __FUNCTION__, '3.2.0' );\n\n\treturn papi_get_meta_store( $id, $type );\n}\n"
  },
  {
    "path": "src/lib/core/io.php",
    "content": "<?php\n\n/**\n * Register Papi directory.\n *\n * @param  array|string $directory\n *\n * @return bool\n */\nfunction register_papi_directory( $directory ) {\n\t// Bail if not a array or string.\n\tif ( ! is_array( $directory ) && ! is_string( $directory ) ) {\n\t\treturn false;\n\t}\n\n\t// Bail if directory don't exists.\n\tif ( is_string( $directory ) && ( ! file_exists( $directory ) || ! is_dir( $directory ) ) ) {\n\t\treturn false;\n\t}\n\n\t// Add directory to directories filter.\n\treturn add_filter( 'papi/settings/directories', function ( $directories ) use ( $directory ) {\n\t\t$directories = is_string( $directories ) ? [$directories] : $directories;\n\t\t$directories = is_array( $directories ) ? $directories : [];\n\n\t\t// Create a array of directory.\n\t\t$directory = papi_to_array( $directory );\n\n\t\t// Check if directory exists before adding it.\n\t\tforeach ( $directory as $index => $dir ) {\n\t\t\tif ( ! file_exists( $dir ) || ! is_dir( $dir ) ) {\n\t\t\t\tunset( $directory[$index] );\n\t\t\t}\n\t\t}\n\n\t\treturn array_merge( $directories, $directory );\n\t} );\n}\n\n/**\n * Get all files in directory.\n *\n * @param  string $directory\n *\n * @return string\n */\nfunction papi_get_all_files_in_directory( $directory = '' ) {\n\t$result = [];\n\n\tif ( empty( $directory ) || ! is_string( $directory ) ) {\n\t\treturn $result;\n\t}\n\n\tif ( file_exists( $directory ) && $handle = opendir( $directory ) ) {\n\t\twhile ( false !== ( $file = readdir( $handle ) ) ) {\n\t\t\tif ( ! in_array( $file, ['..', '.'], true ) && $file[0] !== '.' ) {\n\t\t\t\tif ( is_dir( $directory . '/' . $file ) ) {\n\t\t\t\t\t$result   = array_merge( $result, papi_get_all_files_in_directory( $directory . '/' . $file ) );\n\t\t\t\t} else {\n\t\t\t\t\t$file     = $directory . '/' . $file;\n\t\t\t\t\t$result[] = preg_replace( '/\\/\\//si', '/', $file );\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tclosedir( $handle );\n\t}\n\n\treturn $result;\n}\n\n/**\n * Get core type file path, this allows classes to be overridden.\n *\n * @param  string $file_path\n *\n * @return string\n */\nfunction papi_get_core_type_file_path( $file_path ) {\n\tif ( empty( $file_path ) ) {\n\t\treturn [];\n\t}\n\n\t$directories = papi_filter_settings_directories();\n\t$result      = [];\n\n\tforeach ( $directories as $directory ) {\n\t\t$directory = rtrim( $directory, '/' ) . '/';\n\t\t$file_path = str_replace( $directory, '', $file_path );\n\t\t$path      = $directory . $file_path;\n\n\t\tif ( file_exists( $path ) ) {\n\t\t\t$result[] = $path;\n\t\t}\n\t}\n\n\treturn array_pop( $result );\n}\n\n/**\n * Get all core type files from the register directories.\n *\n * @return array\n */\nfunction papi_get_all_core_type_files() {\n\treturn papi()->once( __FUNCTION__, function() {\n\t\t$directories = papi_filter_settings_directories();\n\t\t$result      = [];\n\n\t\tforeach ( $directories as $directory ) {\n\t\t\t$result = array_merge( $result, papi_get_all_files_in_directory( $directory ) );\n\t\t}\n\n\t\t// Get the last file path from directories.\n\t\t$result = array_map( 'papi_get_core_type_file_path', $result );\n\n\t\t// Only unique path, no duplicated path is allowed.\n\t\treturn array_unique( $result );\n\t} );\n}\n\n/**\n * Get core type file path from file name.\n *\n * @param  string $file\n *\n * @return null|string\n */\nfunction papi_get_file_path( $file ) {\n\tif ( empty( $file ) || ! is_string( $file ) ) {\n\t\treturn;\n\t}\n\n\t$directories = papi_filter_settings_directories();\n\t$file        = '/' . str_replace( ' ', '-', str_replace( '_', '-', $file ) );\n\n\tforeach ( $directories as $directory ) {\n\t\tif ( file_exists( $directory . $file ) ) {\n\t\t\treturn $directory . $file;\n\t\t}\n\n\t\tif ( file_exists( $directory . $file . '.php' ) ) {\n\t\t\treturn $directory . $file . '.php';\n\t\t}\n\t}\n}\n\n/**\n * Get core type base path.\n *\n * @param  string $file\n *\n * @return string\n */\nfunction papi_get_core_type_base_path( $file ) {\n\tif ( empty( $file ) || ! is_string( $file ) ) {\n\t\treturn '';\n\t}\n\n\t$directories = papi_filter_settings_directories();\n\n\tforeach ( $directories as $directory ) {\n\t\tif ( strpos( $file, $directory ) !== false ) {\n\t\t\t$file = str_replace( $directory . '/', '', $file );\n\t\t}\n\t}\n\n\t$file = explode( '.', $file );\n\n\treturn $file[0];\n}\n"
  },
  {
    "path": "src/lib/core/meta.php",
    "content": "<?php\n\n/**\n * Get meta id for a meta type.\n *\n * @param  string $type\n * @param  int    $id\n *\n * @return int\n */\nfunction papi_get_meta_id( $type = null, $id = null ) {\n\t$type = papi_get_meta_type( $type );\n\n\tif ( function_exists( sprintf( 'papi_get_%s_id', $type ) ) ) {\n\t\treturn call_user_func_array( sprintf( 'papi_get_%s_id', $type ), [$id] );\n\t}\n\n\treturn intval( $id );\n}\n\n/**\n * Get right meta id column for a meta type.\n *\n * @param  string $type\n *\n * @return string|null\n */\nfunction papi_get_meta_id_column( $type = 'post' ) {\n\tif ( $type = papi_get_meta_type( $type ) ) {\n\t\treturn sprintf( '%s_id', $type );\n\t}\n}\n\n/**\n * Get the meta store.\n *\n * @param  int    $post_id\n * @param  string $type\n *\n * @return Papi_Core_Meta_Store|null\n */\nfunction papi_get_meta_store( $post_id = 0, $type = 'post' ) {\n\treturn Papi_Core_Meta_Store::factory( $post_id, $type );\n}\n\n/**\n * Get right meta type value. It will treat option\n * as a meta type even if isn't a real meta type.\n *\n * @param  string $type\n *\n * @return string|null\n */\nfunction papi_get_meta_type( $type = null ) {\n\tif ( $meta_type = papi_get_qs( 'meta_type' ) ) {\n\t\treturn $meta_type;\n\t}\n\n\tswitch ( $type ) {\n\t\tcase 'option':\n\t\t\treturn 'option';\n\t\tcase 'post':\n\t\tcase 'page':\n\t\t\treturn 'post';\n\t\tcase 'taxonomy':\n\t\tcase 'term':\n\t\t\treturn 'term';\n\t\tdefault:\n\t\t\tbreak;\n\t}\n\n\t$request_uri = $_SERVER['REQUEST_URI'];\n\t$parsed_url  = parse_url( $request_uri );\n\n\tif ( ! empty( $parsed_url['query'] ) ) {\n\t\t// Taxonomy page in admin.\n\t\tif ( papi_is_admin() && preg_match( '/taxonomy=/', $parsed_url['query'] ) ) {\n\t\t\treturn 'term';\n\t\t}\n\n\t\t// Option page in admin.\n\t\tif ( papi_is_admin() && preg_match( '/page\\=papi(\\%2F|\\/)option/', $parsed_url['query'] ) ) {\n\t\t\treturn 'option';\n\t\t}\n\t}\n\n\t// When doing ajax we need to check if it's a taxonomy ajax or a post type ajax.\n\tif ( papi_is_admin() && defined( 'DOING_AJAX' ) && DOING_AJAX ) {\n\t\tif ( isset( $_POST['taxonomy'] ) ) {\n\t\t\treturn 'term';\n\t\t}\n\n\t\tif ( isset( $_POST['post_type'] ) ) {\n\t\t\treturn 'post';\n\t\t}\n\t}\n\n\t// On the frontend term should be returned if on a category or tag page.\n\tif ( ! papi_is_admin() && ( is_category() || is_tag() ) ) {\n\t\treturn 'term';\n\t}\n\n\t// Check queried object for right meta type.\n\tif ( $obj = get_queried_object() ) {\n\t\tif ( $obj instanceof WP_Term || isset( $obj->term_id ) ) {\n\t\t\treturn 'term';\n\t\t}\n\n\t\tif ( $obj instanceof WP_Post || isset( $obj->post_id ) ) {\n\t\t\treturn 'post';\n\t\t}\n\t}\n\n\t// Default was has to be set here since we trying to figure out\n\t// which url conform which meta type.\n\tif ( is_null( $type ) ) {\n\t\t$type = 'post';\n\t}\n\n\t// If meta type exists as a filter we can return it.\n\tif ( function_exists( \"get_{$type}_meta\" ) ) {\n\t\treturn $type;\n\t}\n}\n"
  },
  {
    "path": "src/lib/core/post.php",
    "content": "<?php\n\n/**\n * Get post or page id from a object.\n *\n * @param  mixed $post_id\n *\n * @return int\n */\nfunction papi_get_post_id( $post_id = null ) {\n\tif ( is_object( $post_id ) && isset( $post_id->ID ) ) {\n\t\treturn $post_id->ID;\n\t}\n\n\tif ( is_numeric( $post_id ) && is_string( $post_id ) && $post_id !== '0' ) {\n\t\treturn intval( $post_id );\n\t}\n\n\tif ( is_null( $post_id ) || intval( $post_id ) === 0 ) {\n\t\tif ( isset( $_POST['action'] ) ) {\n\t\t\tif ( $_POST['action'] === 'query-attachments' && isset( $_POST['query']['item'] ) ) {\n\t\t\t\treturn intval( $_POST['query']['item'] );\n\t\t\t}\n\t\t}\n\n\t\tif ( get_post() ) {\n\t\t\treturn get_the_ID();\n\t\t}\n\n\t\tif ( $value = papi_get_qs( 'post' ) ) {\n\t\t\treturn is_array( $value ) ? 0 : intval( $value );\n\t\t}\n\n\t\tif ( $value = papi_get_qs( 'page_id' ) ) {\n\t\t\treturn intval( $value );\n\t\t}\n\n\t\treturn intval( $post_id );\n\t}\n\n\treturn intval( $post_id );\n}\n\n/**\n * Get post parent id.\n *\n * @return int\n */\nfunction papi_get_parent_post_id() {\n\treturn intval( papi_get_qs( 'post_parent' ) );\n}\n\n/**\n * Get WordPress post type in various ways.\n *\n * @param  int $post_id\n *\n * @return string\n */\nfunction papi_get_post_type( $post_id = null ) {\n\tif ( $post_type = papi_get_or_post( 'post_type' ) ) {\n\t\treturn $post_type;\n\t}\n\n\t$post_id = papi_get_post_id( $post_id );\n\n\tif ( $post_id !== 0 ) {\n\t\treturn strtolower( get_post_type( $post_id ) );\n\t}\n\n\t$page = papi_get_qs( 'page' );\n\n\tif ( is_string( $page ) && strpos( strtolower( $page ), 'papi-add-new-page,' ) !== false ) {\n\t\t$exploded = explode( ',', $page );\n\n\t\tif ( empty( $exploded[1] ) ) {\n\t\t\treturn '';\n\t\t}\n\n\t\treturn $exploded[1];\n\t}\n\n\t// If only `post-new.php` without any querystrings\n\t// it would be the post post type.\n\t$req_uri  = $_SERVER['REQUEST_URI'];\n\t$exploded = explode( '/', $req_uri );\n\t$last     = end( $exploded );\n\n\tif ( $last === 'post-new.php' ) {\n\t\treturn 'post';\n\t}\n\n\treturn '';\n}\n\n/**\n * Get post type label.\n *\n * @param  string $post_type\n * @param  string $label\n * @param  string $default\n *\n * @return string\n */\nfunction papi_get_post_type_label( $post_type, $label, $default = '' ) {\n\tif ( ! post_type_exists( $post_type ) ) {\n\t\treturn $default;\n\t}\n\n\treturn get_post_type_object( $post_type )->labels->$label;\n}\n"
  },
  {
    "path": "src/lib/core/property.php",
    "content": "<?php\n\n/**\n * Convert array of slugs to array with arrays in.\n *\n * @param  array  $values\n * @param  string $slug\n *\n * @return array\n */\nfunction papi_from_property_array_slugs( array $values, $slug ) {\n\t$results = [];\n\n\tif ( empty( $values ) || ! isset( $values[$slug] ) ) {\n\t\treturn $results;\n\t}\n\n\tfor ( $i = 0; $i < $values[$slug]; $i++ ) {\n\t\t$item      = [];\n\t\t$item_slug = $slug . '_' . $i . '_';\n\t\t$keys      = preg_grep( '/' . preg_quote( $item_slug ) . '/', array_keys( $values ) );\n\n\t\tforeach ( $keys as $key ) {\n\t\t\t$arr_key        = str_replace( $item_slug, '', $key );\n\t\t\t$item[$arr_key] = $values[$key];\n\t\t}\n\n\t\t$results[] = $item;\n\t}\n\n\treturn $results;\n}\n\n/**\n * Check if the given value is a instance of a property or not.\n *\n * @param  mixed $value\n *\n * @return bool\n */\nfunction papi_is_property( $value ) {\n\treturn $value instanceof Papi_Core_Property;\n}\n\n/**\n * Get options and properties.\n *\n * @param  mixed $file_or_options\n * @param  array $properties\n * @param  bool  $is_box\n *\n * @return array\n */\nfunction papi_get_options_and_properties( $file_or_options = [], $properties = [], $is_box = true ) {\n\t$options = [];\n\n\tif ( is_array( $file_or_options ) ) {\n\t\tif ( empty( $properties ) && $is_box ) {\n\t\t\t// Check if we have a title or not.\n\t\t\tif ( isset( $file_or_options['title'] ) ) {\n\t\t\t\t$options['title'] = $file_or_options['title'];\n\t\t\t} else if ( isset( $file_or_options[0]->title ) ) {\n\t\t\t\t$options['title'] = $file_or_options[0]->title;\n\t\t\t} else if ( isset( $file_or_options[0]->options, $file_or_options[0]->options['title'] ) ) {\n\t\t\t\t$options['title'] = $file_or_options[0]->options['title'];\n\t\t\t} else {\n\t\t\t\t$options['title'] = '';\n\t\t\t}\n\n\t\t\t$properties = $file_or_options;\n\n\t\t\tif ( ! empty( $properties['title'] ) ) {\n\t\t\t\t$options = $properties;\n\t\t\t}\n\t\t} else {\n\t\t\t$options = array_merge( $options, $file_or_options );\n\n\t\t\tif ( ! $is_box ) {\n\t\t\t\t// Add all non string keys to the properties array\n\t\t\t\tforeach ( $options as $key => $value ) {\n\t\t\t\t\tif ( ! is_string( $key ) ) {\n\t\t\t\t\t\t$properties[] = $value;\n\t\t\t\t\t\tunset( $options[$key] );\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t} else if ( is_string( $file_or_options ) ) {\n\t\t// If it's a template we need to load it the right way\n\t\t// and add all properties the right way.\n\t\tif ( preg_match( '/\\.php$/', $file_or_options ) === 1 ) {\n\t\t\t$values   = $properties;\n\t\t\t$template = papi_template( $file_or_options, $values );\n\n\t\t\t// Create the property array from existing property array or a new.\n\t\t\t$properties = [];\n\t\t\t$options    = $template;\n\n\t\t\t// Add all non string keys to the properties array\n\t\t\tforeach ( $options as $key => $value ) {\n\t\t\t\tif ( ! is_string( $key ) ) {\n\t\t\t\t\t$properties[] = $value;\n\t\t\t\t\tunset( $options[$key] );\n\t\t\t\t}\n\t\t\t}\n\t\t} else {\n\t\t\t// The first parameter is used as the title.\n\t\t\t$options['title'] = $file_or_options;\n\t\t}\n\t}\n\n\t// Support `properties` or `props` in options array.\n\tforeach ( ['properties', 'props'] as $key ) {\n\t\tif ( is_array( $options ) && isset( $options[$key] ) ) {\n\t\t\t$properties = papi_to_array( $options[$key] );\n\t\t\tunset( $options[$key] );\n\t\t}\n\t}\n\n\treturn [$options, $properties];\n}\n\n/**\n * Get property class name.\n *\n * Will replace 'Property' if it exists in the string\n * since it's a old way to write which type to use.\n *\n * @param  string $type\n *\n * @return string\n */\nfunction papi_get_property_class_name( $type ) {\n\tif ( ! is_string( $type ) || empty( $type ) ) {\n\t\treturn;\n\t}\n\n\t$type = str_replace( '-', '_', $type );\n\t$type = explode( '_', $type );\n\t$type = array_map( 'ucfirst', $type );\n\t$type = implode( '_', $type );\n\n\treturn 'Papi_Property_' . ucfirst( preg_replace( '/^Property/', '', $type ) );\n}\n\n/**\n * Get property type by the given type.\n *\n * @param  mixed $type\n *\n * @return Papi_Core_Property\n */\nfunction papi_get_property_type( $type ) {\n\treturn Papi_Core_Property::factory( $type );\n}\n\n/**\n * Get the right key for a property type.\n *\n * @param  string $str\n *\n * @return string\n */\nfunction papi_get_property_type_key( $str = '' ) {\n\t$suffix = '_property';\n\n\tif ( ! is_string( $str ) || strlen( $str ) === 0 ) {\n\t\treturn $suffix;\n\t}\n\n\t$len = strlen( $str );\n\n\tif ( isset( $str[$len - 1] ) && $str[$len - 1] === ']' ) {\n\t\t$str = substr( $str, 0, $len - 1 );\n\n\t\treturn papi_get_property_type_key( $str ) . ']';\n\t}\n\n\treturn unpapify( $str . $suffix );\n}\n\n/**\n * Get the right key for a property type with a underscore as the first character.\n *\n * @param  string $str\n *\n * @return string\n */\nfunction papi_get_property_type_key_f( $str ) {\n\treturn papi_f( papi_get_property_type_key( $str ) );\n}\n\n/**\n * Check if it's ends with '_property'.\n *\n * @param  string $str\n *\n * @return bool\n */\nfunction papi_is_property_type_key( $str = '' ) {\n\t$pattern = '_property';\n\t$pattern = str_replace( '_', '\\_', $pattern );\n\t$pattern = str_replace( '-', '\\-', $pattern );\n\t$pattern = '/' . $pattern . '$/';\n\n\treturn preg_match( $pattern, $str ) === 1;\n}\n\n/**\n * Populate properties array.\n *\n * @param  array|object $properties\n *\n * @return array\n */\nfunction papi_populate_properties( $properties ) {\n\tif ( ! is_array( $properties ) && ! is_object( $properties ) || empty( $properties ) ) {\n\t\treturn [];\n\t}\n\n\tif ( is_object( $properties ) ) {\n\t\treturn [$properties];\n\t}\n\n\t// Convert all non property objects to property objects.\n\t$properties = array_map( function ( $property ) {\n\t\tif ( is_array( $property ) ) {\n\t\t\treturn papi_property( $property );\n\t\t}\n\n\t\treturn $property;\n\t}, $properties );\n\n\tif ( count( array_filter( array_keys( $properties ), 'is_string' ) ) > 0 ) {\n\t\t$properties = [papi_property( $properties )];\n\t} else {\n\t\t$properties = array_values( $properties );\n\t}\n\n\t// If the first property is a core tab, just return\n\t// the properties array and skip the last check.\n\tif ( empty( $properties ) || $properties[0] instanceof Papi_Core_Tab ) {\n\t\treturn papi_sort_order( $properties );\n\t}\n\n\treturn papi_sort_order( array_filter( array_reverse( $properties ), 'papi_is_property' ) );\n}\n\n/**\n * Create a new property array or rendering a template property file.\n *\n * @param  mixed $file_or_options\n * @param  array $values\n *\n * @return Papi_Core_Property\n */\nfunction papi_property( $file_or_options, array $values = [] ) {\n\tif ( papi_is_empty( $file_or_options ) ) {\n\t\treturn;\n\t}\n\n\tif ( is_array( $file_or_options ) ) {\n\t\tif ( $property = Papi_Core_Property::factory( $file_or_options ) ) {\n\t\t\t$file_or_options = $property->get_options();\n\t\t}\n\t}\n\n\tif ( is_string( $file_or_options ) && is_array( $values ) ) {\n\t\t$file_or_options = papi_template( $file_or_options, $values );\n\n\t\tif ( is_object( $file_or_options ) ) {\n\t\t\t$file_or_options = papi_property( (array) $file_or_options->get_options() );\n\t\t}\n\t}\n\n\tif ( is_object( $file_or_options ) ) {\n\t\treturn papi_get_property_type( $file_or_options );\n\t}\n}\n\n/**\n * Render the given property.\n *\n * @param mixed $property\n */\nfunction papi_render_property( $property ) {\n\t$property = Papi_Core_Property::factory( $property );\n\n\tif ( ! papi_is_property( $property ) ) {\n\t\treturn;\n\t}\n\n\t$property->render();\n}\n\n/**\n * Render properties the right way.\n *\n * @param array $properties\n */\nfunction papi_render_properties( array $properties ) {\n\tif ( empty( $properties ) ) {\n\t\treturn;\n\t}\n\n\t// If it's a tab the tabs class will\n\t// handle the rendering of the properties.\n\tif ( $properties[0] instanceof Papi_Core_Tab ) {\n\t\tnew Papi_Admin_Meta_Box_Tabs( $properties );\n\t} else {\n\t\t?>\n\t\t<table class=\"papi-table\">\n\t\t\t<tbody>\n\t\t\t<?php\n\t\t\tforeach ( $properties as $property ) {\n\t\t\t\tpapi_render_property( $property );\n\t\t\t}\n\t\t\t?>\n\t\t\t</tbody>\n\t\t</table>\n\t\t<?php\n\t}\n}\n\n/**\n * Get require text for property.\n *\n * @param  stdClass $property\n *\n * @return string\n */\nfunction papi_property_require_text( $property ) {\n\tif ( ! papi_is_property( $property ) || ! $property->required ) {\n\t\treturn '';\n\t}\n\n\treturn esc_html__( '(required field)', 'papi' );\n}\n\n/**\n * Get require tag for property.\n *\n * @param  stdClass $property\n * @param  bool     $text\n *\n * @return string\n */\nfunction papi_property_required_html( $property, $text = false ) {\n\tif ( ! papi_is_property( $property ) || ! $property->required ) {\n\t\treturn '';\n\t}\n\n\treturn ' <span class=\"papi-rq\" data-property-name=\"' . $property->title . '\" data-property-id=\"' . $property->slug . '\">' . ( $text ? papi_property_require_text( $property ) : '*' ) . '</span>';\n}\n\n/**\n * Convert array of slugs to array with arrays in.\n *\n * @param  array  $values\n * @param  string $slug\n *\n * @return array\n */\nfunction papi_property_from_array_slugs( array $values, $slug ) {\n\t$results = [];\n\n\tif ( empty( $values ) ) {\n\t\treturn $results;\n\t}\n\n\tfor ( $i = 0; $i < $values[$slug]; $i++ ) {\n\t\t$item      = [];\n\t\t$item_slug = $slug . '_' . $i . '_';\n\t\t$keys      = preg_grep( '/' . preg_quote( $item_slug ) . '/', array_keys( $values ) );\n\n\t\tforeach ( $keys as $key ) {\n\t\t\t$arr_key        = str_replace( $item_slug, '', $key );\n\t\t\t$item[$arr_key] = $values[$key];\n\t\t}\n\n\t\t$results[] = $item;\n\t}\n\n\treturn $results;\n}\n\n/**\n * Convert array of arrays to array of slugs.\n * The given slug will match a key with the number of properties.\n *\n * @param  array  $value\n * @param  string $slug\n *\n * @return array\n */\nfunction papi_property_to_array_slugs( array $value, $slug ) {\n\t$results = [];\n\t$counter = [];\n\n\tforeach ( $value as $index => $arr ) {\n\t\tif ( ! is_array( $arr ) ) {\n\t\t\tcontinue;\n\t\t}\n\n\t\t$counter[] = $arr;\n\n\t\tforeach ( $arr as $key => $val ) {\n\t\t\tif ( ! is_string( $key ) || empty( $key ) ) {\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tif ( $key[0] !== '_' ) {\n\t\t\t\t$key = '_' . $key;\n\t\t\t}\n\n\t\t\t$item_slug = $slug . '_' . $index . $key;\n\n\t\t\tif ( papi_is_property_type_key( $item_slug ) ) {\n\t\t\t\t$item_slug = papi_f( $item_slug );\n\t\t\t}\n\n\t\t\t$results[$item_slug] = $val;\n\t\t}\n\t}\n\n\t$results[$slug] = count( $counter );\n\n\treturn $results;\n}\n"
  },
  {
    "path": "src/lib/core/slug.php",
    "content": "<?php\n\n/**\n * Prefix slug. Handles slugs with papi prefix.\n *\n * @param  string $prefix\n * @param  string $slug\n *\n * @return string\n */\nfunction papi_prefix_slug( $prefix, $slug ) {\n\t$papify = preg_match( '/papi\\_/', $slug );\n\t$slug = unpapify( $slug );\n\n\t$slug = implode( '_', [$prefix, $slug] );\n\n\tif ( $papify ) {\n\t\t$slug = papify( $slug );\n\t}\n\n\treturn $slug;\n}\n\n/**\n * Add `papi_` to the given string ad the start of the string.\n *\n * @param  string $str\n *\n * @return string\n */\nfunction papify( $str = '' ) {\n\tif ( ! is_string( $str ) ) {\n\t\treturn '';\n\t}\n\n\tif ( ! preg_match( '/^\\_\\_papi|^\\_papi|^papi\\_/', $str ) ) {\n\t\treturn str_replace( 'papi__', 'papi_', 'papi_' . $str );\n\t}\n\n\treturn $str;\n}\n\n/**\n * Remove `papi-` or `papi_` from the given string.\n *\n * @param  string $str\n *\n * @return string\n */\nfunction unpapify( $str ) {\n\tif ( ! is_string( $str ) ) {\n\t\treturn '';\n\t}\n\n\treturn str_replace( 'papi-', '', str_replace( 'papi_', '', $str ) );\n}\n"
  },
  {
    "path": "src/lib/core/tabs.php",
    "content": "<?php\n\n/**\n * Setup tabs.\n *\n * @param  array $tabs\n *\n * @return array\n */\nfunction papi_tabs_setup( array $tabs ) {\n\t$_tabs = [];\n\n\tforeach ( $tabs as $tab ) {\n\t\tif ( $tab instanceof Papi_Core_Tab === false ) {\n\t\t\tcontinue;\n\t\t}\n\n\t\tif ( papi_current_user_is_allowed( $tab->capabilities ) ) {\n\t\t\t$_tabs[] = $tab;\n\t\t}\n\t}\n\n\t$tabs = papi_sort_order( $_tabs );\n\n\t// Generate unique names for all tabs.\n\t$len = count( $tabs );\n\tfor ( $i = 0; $i < $len; $i ++ ) {\n\t\t$tabs[$i]->id = papi_html_name( $tabs[$i]->title ) . '_' . $i;\n\t}\n\n\treturn $tabs;\n}\n\n/**\n * Create a new tab array or rendering a template tab file.\n *\n * @param  string|array $file_or_options\n * @param  array $properties\n *\n * @return Papi_Core_Tab\n */\nfunction papi_tab( $file_or_options, $properties = [] ) {\n\tlist( $options, $properties ) = papi_get_options_and_properties( $file_or_options, $properties, false );\n\n\treturn new Papi_Core_Tab( $options, $properties );\n}\n"
  },
  {
    "path": "src/lib/core/taxonomy.php",
    "content": "<?php\n\n/**\n * Get term id from a object.\n *\n * @param  mixed $term_id\n *\n * @return int\n */\nfunction papi_get_term_id( $term_id = null ) {\n\tif ( is_object( $term_id ) && isset( $term_id->term_id ) ) {\n\t\treturn $term_id->term_id;\n\t}\n\n\tif ( is_numeric( $term_id ) && is_string( $term_id ) && $term_id !== '0' ) {\n\t\treturn intval( $term_id );\n\t}\n\n\tif ( is_null( $term_id ) || intval( $term_id ) === 0 ) {\n\t\tif ( ! papi_is_admin() && ( is_category() || is_tag() || is_tax() ) ) {\n\t\t\treturn get_queried_object_id();\n\t\t} else if ( $term_id = papi_get_or_post( 'term_id' ) ) {\n\t\t\treturn intval( $term_id );\n\t\t} else if ( $tag_id = papi_get_or_post( 'tag_ID' ) ) {\n\t\t\treturn intval( $tag_id );\n\t\t}\n\t}\n\n\treturn intval( $term_id );\n}\n\n/**\n * Get WordPress taxonomy in various ways.\n *\n * @param  int $term_id\n *\n * @return string\n */\nfunction papi_get_taxonomy( $term_id = null ) {\n\tif ( $taxonomy = papi_get_or_post( 'taxonomy' ) ) {\n\t\treturn $taxonomy;\n\t}\n\n\t$term_id = papi_get_term_id( $term_id );\n\n\tif ( ! empty( $term_id ) ) {\n\t\t$term = get_term( $term_id, '' );\n\n\t\tif ( is_object( $term ) && ! is_wp_error( $term ) ) {\n\t\t\treturn strtolower( $term->taxonomy );\n\t\t}\n\t}\n\n\treturn '';\n}\n\n/**\n * Get taxonomy label.\n *\n * @param  string $taxonomy\n * @param  string $label\n * @param  string $default\n *\n * @return string\n */\nfunction papi_get_taxonomy_label( $taxonomy, $label, $default = '' ) {\n\tif ( ! taxonomy_exists( $taxonomy ) ) {\n\t\treturn $default;\n\t}\n\n\treturn get_taxonomy( $taxonomy )->labels->$label;\n}\n"
  },
  {
    "path": "src/lib/core/template.php",
    "content": "<?php\n\n/**\n * Add page type class name as a css class on body.\n *\n * @param  array $classes\n *\n * @return array\n */\nfunction papi_body_class( array $classes ) {\n\t$classes = array_merge( $classes, papi_get_entry_type_body_classes() );\n\n\tif ( $class = papi_get_entry_type_css_class() ) {\n\t\t$classes[] = $class;\n\t}\n\n\treturn array_unique( $classes );\n}\nadd_filter( 'body_class', 'papi_body_class' );\n\n/**\n * Get template file name.\n *\n * @param  string $template\n *\n * @return string|null\n */\nfunction papi_get_template_file_name( $template ) {\n\tif ( ! is_string( $template ) ) {\n\t\treturn;\n\t}\n\n\t/**\n\t * Filter default extension.\n\t *\n\t * @param  string $extension\n\t *\n\t * @return string\n\t */\n\t$extension = apply_filters( 'papi/template_extension', '.php' );\n\t$template  = substr( $template, -strlen( $extension ) ) === $extension ? $template : $template . $extension;\n\n\treturn $template === $extension ? null : $template;\n}\n\n/**\n * Include partial view.\n *\n * @param string $file\n * @param array $vars\n */\nfunction papi_include_template( $file, array $vars = [] ) {\n\tif ( ! is_string( $file ) || empty( $file ) ) {\n\t\treturn;\n\t}\n\n\t$path = PAPI_PLUGIN_DIR;\n\t$path = rtrim( $path, '/' ) . '/';\n\n\tif ( file_exists( $path . $file ) ) {\n\t\trequire $path . $file;\n\t}\n}\n\n/**\n * Load a template array file and merge values with it.\n *\n * @param  string $file\n * @param  array  $values\n * @param  bool   $convert_to_object\n *\n * @return array|object\n */\nfunction papi_template( $file, array $values = [], $convert_to_object = false ) {\n\tif ( ! is_string( $file ) || empty( $file ) ) {\n\t\treturn [];\n\t}\n\n\t$filepath = papi_get_file_path( $file );\n\n\tif ( empty( $filepath ) && is_file( $file ) ) {\n\t\t$filepath = $file;\n\t}\n\n\tif ( empty( $filepath ) || ! file_exists( $filepath ) || is_dir( $filepath ) ) {\n\t\treturn [];\n\t}\n\n\t$template = require $filepath;\n\n\tif ( papi_is_property( $template ) ) {\n\t\tforeach ( $values as $key => $value ) {\n\t\t\t$template->set_option( $key, $value );\n\t\t}\n\n\t\t$result = $template;\n\t} else {\n\t\t$result = array_merge( (array) $template, $values );\n\t}\n\n\tif ( $convert_to_object ) {\n\t\treturn (object) $result;\n\t}\n\n\treturn $result;\n}\n\n/**\n * Include template files from Papis custom page template meta field.\n *\n * @param  string $original_template\n *\n * @return string\n */\nfunction papi_template_include( $original_template ) {\n\tglobal $post;\n\n\t// Check so we only change template on single and page posts.\n\tif ( ! is_single() && ! is_page() && ! is_tag() ) {\n\t\treturn $original_template;\n\t}\n\n\t/**\n\t * Modify which template is included before Papi looks for the right template.\n\t *\n\t * @param  string $original_template\n\t *\n\t * @return string\n\t */\n\t$template = apply_filters( 'papi/pre_template_include', $original_template );\n\n\tif ( ! empty( $template ) && $original_template !== $template ) {\n\t\treturn $template;\n\t}\n\n\t// Determine which id to use.\n\t$id = is_tag() ? get_queried_object()->term_id : $post->ID;\n\n\t// Only load a template if it exists.\n\tif ( $page_template = papi_get_entry_type_template( $id ) ) {\n\t\tif ( $template = locate_template( $page_template ) ) {\n\t\t\t/**\n\t\t\t * Change which template that is used by Papi.\n\t\t\t *\n\t\t\t * @param  string $template\n\t\t\t *\n\t\t\t * @return string\n\t\t\t */\n\t\t\treturn apply_filters( 'papi/template_include', $template );\n\t\t}\n\t}\n\n\treturn $original_template;\n}\nadd_filter( 'template_include', 'papi_template_include' );\n"
  },
  {
    "path": "src/lib/core/url.php",
    "content": "<?php\n\n/**\n * Get the url to 'post-new.php' with query string of the page type to load.\n *\n * @param  string $page_type\n * @param  bool   $append_admin_url\n * @param  string $post_type\n * @param  array  $include\n *\n * @return string\n */\nfunction papi_get_page_new_url( $page_type, $append_admin_url = true, $post_type = null, array $include = [] ) {\n\t// Prepare query strings.\n\t$include = empty( $include ) ? array_keys( $_GET ) : $include;\n\t$include = array_diff( $include, ['page', 'page_type', 'post_type'] );\n\n\t// Create new admin url.\n\t$admin_url = $append_admin_url ? get_admin_url() : '';\n\t$admin_url = $admin_url . 'post-new.php?page_type=' . $page_type . papi_include_query_strings( '&', $include );\n\n\treturn papi_append_post_type_query( $admin_url, $post_type );\n}\n\n/**\n * Append post type query string.\n *\n * @param  string $url\n * @param  string $post_type_arg\n *\n * @return string\n */\nfunction papi_append_post_type_query( $url, $post_type_arg = null ) {\n\tif ( strpos( $url, 'post_type=' ) !== false ) {\n\t\treturn preg_replace( '/&%.+/', '', $url );\n\t}\n\n\t$post_type = '';\n\n\t// Only change post type if post type arg isn't the same.\n\tif ( $post_type_arg !== $post_type ) {\n\t\t$post_type = $post_type_arg;\n\t}\n\n\t// Add post type if empty.\n\tif ( empty( $post_type ) ) {\n\t\t$post_id = papi_get_post_id();\n\n\t\tif ( $post_id === 0 ) {\n\t\t\t$post_type = papi_get_or_post( 'post_type' );\n\t\t} else {\n\t\t\t$post_type = get_post_type( $post_id );\n\t\t}\n\n\t\tif ( empty( $post_type ) ) {\n\t\t\t$post_type = $post_type_arg;\n\t\t}\n\n\t\tif ( empty( $post_type ) ) {\n\t\t\t$post_type = 'post';\n\t\t}\n\t}\n\n\t// Add right query string character.\n\tif ( ! empty( $post_type ) ) {\n\t\tif ( substr( $url, - 1, 1 ) !== '&' ) {\n\t\t\t$url .= '&';\n\t\t}\n\n\t\t$url .= 'post_type=' . $post_type;\n\t}\n\n\treturn $url;\n}\n\n/**\n * Append query strings from existing request.\n *\n * @param  string $first_character\n * @param  array  $allowed_keys\n *\n * @return string\n */\nfunction papi_include_query_strings( $first_character = '?', array $allowed_keys = [] ) {\n\tif ( empty( $allowed_keys ) ) {\n\t\treturn '';\n\t}\n\n\t$include = array_intersect_key( $_GET, array_flip( $allowed_keys ) );\n\n\tif ( empty( $include ) ) {\n\t\treturn '';\n\t}\n\n\treturn $first_character . http_build_query( $include );\n}\n"
  },
  {
    "path": "src/lib/core/utilities.php",
    "content": "<?php\n\n/**\n * Convert a value to camel case.\n *\n * @param  string $str\n *\n * @return null|string\n */\nfunction papi_camel_case( $str ) {\n\tif ( ! is_string( $str ) ) {\n\t\treturn;\n\t}\n\n\treturn lcfirst( str_replace( ' ', '', ucwords( str_replace( ['-', '_'], ' ', $str ) ) ) );\n}\n\n/**\n * Cast string value to right value type.\n *\n * @param  string $str\n *\n * @return mixed\n */\nfunction papi_cast_string_value( $str ) {\n\tif ( ! is_string( $str ) ) {\n\t\treturn $str;\n\t}\n\n\tif ( is_numeric( $str ) ) {\n\t\treturn $str == (int) $str ? (int) $str : (float) $str; // loose comparison\n\t}\n\n\tif ( $str === 'true' || $str === 'false' ) {\n\t\treturn $str === 'true';\n\t}\n\n\treturn papi_maybe_json_decode(\n\t\tmaybe_unserialize( $str )\n\t);\n}\n\n/**\n * Try convert to string if is possible else return empty string.\n *\n * @param  mixed $obj\n *\n * @return string\n */\nfunction papi_convert_to_string( $obj ) {\n\tif ( $obj === true ) {\n\t\treturn 'true';\n\t}\n\n\tif ( $obj === false ) {\n\t\treturn 'false';\n\t}\n\n\tif ( ! is_array( $obj ) && ( ( ! is_object( $obj ) && settype( $obj, 'string' ) !== false ) || ( is_object( $obj ) && method_exists( $obj, '__toString' ) ) ) ) {\n\t\treturn (string) $obj;\n\t}\n\n\treturn '';\n}\n\n/**\n * Check if current is allowed the given capabilities.\n *\n * @param  array|string $capabilities\n *\n * @return bool\n */\nfunction papi_current_user_is_allowed( $capabilities = [] ) {\n\tif ( ! is_array( $capabilities ) && ! is_string( $capabilities ) || empty( $capabilities ) ) {\n\t\treturn true;\n\t}\n\n\tforeach ( papi_to_array( $capabilities ) as $capability ) {\n\t\tif ( ! current_user_can( $capability ) ) {\n\t\t\treturn false;\n\t\t}\n\t}\n\n\treturn true;\n}\n\n/**\n * Check if Papi is doing a AJAX request or not.\n *\n * @return bool\n */\nfunction papi_doing_ajax() {\n\treturn defined( 'DOING_PAPI_AJAX' ) && DOING_PAPI_AJAX;\n}\n\n/**\n * Papi escape html.\n *\n * @param  mixed $obj\n * @param  array $keys\n *\n * @return mixed\n */\nfunction papi_esc_html( $obj, $keys = [] ) {\n\t$object = is_object( $obj ) && get_class( $obj ) === 'stdClass';\n\n\tif ( $object ) {\n\t\t$obj = (array) $obj;\n\t}\n\n\tif ( is_array( $obj ) ) {\n\t\tforeach ( $obj as $key => $value ) {\n\t\t\tif ( in_array( $key, $keys, true ) ) {\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tif ( is_string( $key ) ) {\n\t\t\t\tunset( $obj[$key] );\n\t\t\t\t$key = papi_esc_html( $key );\n\t\t\t}\n\n\t\t\tif ( is_string( $value ) || is_object( $value ) || is_array( $obj ) ) {\n\t\t\t\t$obj[$key] = papi_esc_html( $value, $keys );\n\t\t\t}\n\t\t}\n\n\t\tif ( $object ) {\n\t\t\treturn (object) $obj;\n\t\t}\n\n\t\treturn $obj;\n\t}\n\n\tif ( is_string( $obj ) ) {\n\t\treturn esc_html( $obj );\n\t}\n\n\treturn $obj;\n}\n\n/**\n * Add underscores at the start of the string.\n *\n * @param  string $str\n * @param  int    $len\n *\n * @return string\n */\nfunction papi_f( $str = '', $len = 1 ) {\n\tif ( ! is_string( $str ) ) {\n\t\treturn '';\n\t}\n\n\t$prefix = '';\n\n\tfor ( $i = 0; $i < $len; $i++ ) {\n\t\t$prefix .= '_';\n\t}\n\n\tif ( strpos( $str, $prefix ) === 0 ) {\n\t\treturn $str;\n\t}\n\n\treturn $prefix . preg_replace( '/^\\_/', '', $str );\n}\n\n/**\n * Get namespace name and/or class name from page type file.\n *\n * @param  string $file\n *\n * @return string\n */\nfunction papi_get_class_name( $file ) {\n\tif ( ! is_string( $file ) ) {\n\t\treturn '';\n\t}\n\n\tif ( papi()->exists( $file ) ) {\n\t\treturn papi()->make( $file );\n\t}\n\n\tif ( ! file_exists( $file ) ) {\n\t\treturn '';\n\t}\n\n\t$content         = file_get_contents( $file );\n\t$tokens          = token_get_all( $content );\n\t$class_name      = '';\n\t$namespace_name  = '';\n\t$i               = 0;\n\t$len             = count( $tokens );\n\n\tfor ( ; $i < $len; $i++ ) {\n\t\tif ( $tokens[$i][0] === T_NAMESPACE ) {\n\t\t\tfor ( $j = $i + 1; $j < $len; $j++ ) {\n\t\t\t\tif ( $tokens[$j][0] === T_STRING ) {\n\t\t\t\t\t $namespace_name .= '\\\\' . $tokens[$j][1];\n\t\t\t\t} else if ( $tokens[$j] === '{' || $tokens[$j] === ';' ) {\n\t\t\t\t\t break;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tif ( $tokens[$i][0] === T_CLASS ) {\n\t\t\tfor ( $j = $i + 1; $j < $len; $j++ ) {\n\t\t\t\tif ( $tokens[$j] === '{' ) {\n\t\t\t\t\t$class_name = $tokens[$i + 2][1];\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\t$val = $namespace_name . '\\\\' . $class_name;\n\n\tif ( empty( $class_name ) ) {\n\t\t$val = '';\n\t}\n\n\tif ( empty( $namespace_name ) ) {\n\t\t$val = $class_name;\n\t}\n\n\tpapi()->bind( $file, $val );\n\n\treturn $val;\n}\n\n/**\n * Get language slug from query string.\n *\n * @return string\n */\nfunction papi_get_lang() {\n\t$lang = strtolower( papi_get_qs( 'lang' ) );\n\n\t/**\n\t * Modify language slug.\n\t *\n\t * @param  string $lang\n\t *\n\t * @return string\n\t */\n\treturn apply_filters( 'papi/lang', $lang );\n}\n\n/**\n * Get only objects from the value.\n *\n * @param  array $arr\n *\n * @return array\n */\nfunction papi_get_only_objects( array $arr ) {\n\treturn array_filter( $arr, 'is_object' );\n}\n\n/**\n * Get value from $_GET or $_POST with the given key.\n *\n * @param  string $key\n *\n * @return string\n */\nfunction papi_get_or_post( $key ) {\n\tif ( ! is_string( $key ) ) {\n\t\treturn;\n\t}\n\n\tif ( $value = papi_get_qs( $key ) ) {\n\t\treturn $value;\n\t}\n\n\tif ( $value = papi_get_sanitized_post( $key ) ) {\n\t\treturn $value;\n\t}\n}\n\n/**\n * Get query string if it exists and is not empty.\n *\n * @param  array|string $qs\n * @param  bool         $keep_keys\n *\n * @return array|string\n */\nfunction papi_get_qs( $qs, $keep_keys = false ) {\n\tif ( ! is_string( $qs ) && ! is_array( $qs ) ) {\n\t\treturn;\n\t}\n\n\tif ( is_array( $qs ) ) {\n\t\tif ( $keep_keys ) {\n\t\t\t$results = [];\n\n\t\t\tforeach ( $qs as $key ) {\n\t\t\t\t$value = papi_get_qs( $key );\n\n\t\t\t\tif ( ! papi_is_empty( $value ) ) {\n\t\t\t\t\t$results[$key] = $value;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\treturn $results;\n\t\t} else {\n\t\t\treturn array_map( 'papi_get_qs', $qs );\n\t\t}\n\t}\n\n\tif ( isset( $_GET[$qs] ) && ! empty( $_GET[$qs] ) ) {\n\t\t$value = $_GET[$qs];\n\n\t\tif ( is_string( $value ) ) {\n\t\t\t$value = sanitize_text_field( $value );\n\t\t}\n\n\t\tif ( $value === 'false' ) {\n\t\t\t$value = false;\n\t\t}\n\n\t\tif ( $value === 'true' ) {\n\t\t\t$value = true;\n\t\t}\n\n\t\tif ( is_string( $value ) && preg_match( '/papi\\/\\w+\\//', $value ) ) {\n\t\t\t$value = preg_replace( '/papi\\/\\w+\\//', '', $value );\n\t\t}\n\n\t\treturn $value;\n\t}\n}\n\n/**\n * Get sanitized value from global `$_POST`.\n *\n * @param  string $key\n *\n * @return string\n */\nfunction papi_get_sanitized_post( $key ) {\n\tif ( ! isset( $_POST[$key] ) ) {\n\t\treturn;\n\t}\n\n\treturn sanitize_text_field( $_POST[$key] );\n}\n\n/**\n * Get a html friendly name.\n *\n * @param  string $name\n *\n * @return string\n */\nfunction papi_html_name( $name ) {\n\tif ( ! is_string( $name ) ) {\n\t\treturn '';\n\t}\n\n\tif ( ! preg_match( '/^\\_\\_papi|^\\_papi/', $name ) ) {\n\t\t$name = papify( $name );\n\n\t\tif ( ! preg_match( '/\\[.*\\]/', $name ) ) {\n\t\t\t$name = papi_slugify( $name );\n\t\t}\n\n\t\treturn papi_underscorify( $name );\n\t}\n\n\treturn $name;\n}\n\n/**\n * Get html tag from tag name and array of attributes.\n *\n * @param  string $tag\n * @param  array  $attr\n *\n * @return string\n */\nfunction papi_html_tag( $tag, $attr = [] ) {\n\t$attributes = [];\n\t$content    = [];\n\n\tif ( ! is_array( $attr ) ) {\n\t\t$attr = [$attr];\n\t}\n\n\tforeach ( $attr as $key => $value ) {\n\t\tif ( is_numeric( $key ) ) {\n\t\t\tif ( is_array( $value ) ) {\n\t\t\t\t$content[] = implode( ' ', $value );\n\t\t\t} else {\n\t\t\t\t$content[] = $value;\n\t\t\t}\n\n\t\t\tcontinue;\n\t\t}\n\n\t\tswitch ( $key ) {\n\t\t\tcase 'checked':\n\t\t\t\t$value = $value === true ? 'checked' : $value;\n\t\t\t\t$value = is_bool( $value ) ? null : $value;\n\t\t\t\tbreak;\n\t\t\tcase 'selected':\n\t\t\t\t$value = $value === true ? 'selected' : $value;\n\t\t\t\t$value = is_bool( $value ) ? null : $value;\n\t\t\t\tbreak;\n\t\t\tdefault:\n\t\t\t\tbreak;\n\t\t}\n\n\t\tif ( is_array( $value ) || is_object( $value ) ) {\n\t\t\t$value = papi_maybe_json_encode( $value );\n\t\t} else if ( is_bool( $value ) ) {\n\t\t\t$value = $value ? 'true' : 'false';\n\t\t} else if ( is_string( $value ) ) {\n\t\t\t$value = trim( $value );\n\t\t}\n\n\t\tif ( papi_is_empty( $value ) ) {\n\t\t\tcontinue;\n\t\t}\n\n\t\t$attributes[] = sprintf( '%s=\"%s\"', $key, esc_attr( $value ) );\n\t}\n\n\tif ( empty( $content ) ) {\n\t\t$end = '>';\n\t} else {\n\t\t$end = sprintf( '>%s</%s>', implode( ' ', $content ), $tag );\n\t}\n\n\tif ( ! empty( $attributes ) ) {\n\t\t$attributes = ' ' . implode( ' ', $attributes );\n\t} else {\n\t\t$attributes = '';\n\t}\n\n\treturn sprintf( '<%s%s%s', $tag, $attributes, $end );\n}\n\n/**\n * Check if Papi is in admin mode or not and provide\n * a way to modify the return value. Be sure\n *\n * Be sure of what you are doing before modifying the\n * value since it can break stuff.\n *\n * @return bool\n */\nfunction papi_is_admin() {\n\treturn apply_filters( 'papi/is_admin', is_admin() );\n}\n\n/**\n * Check if the given object is empty or not.\n * Values like \"0\", 0 and false should not return true.\n *\n * @param  mixed $obj\n *\n * @return bool\n */\nfunction papi_is_empty( $obj ) {\n\tif ( is_array( $obj ) ) {\n\t\tforeach ( $obj as $val ) {\n\t\t\tif ( ! papi_is_empty( $val ) ) {\n\t\t\t\treturn false;\n\t\t\t}\n\t\t}\n\t\treturn true;\n\t}\n\n\tif ( is_string( $obj ) ) {\n\t\treturn empty( $obj ) && ! is_numeric( $obj );\n\t}\n\n\tif ( is_bool( $obj ) || is_numeric( $obj ) ) {\n\t\treturn false;\n\t}\n\n\treturn empty( $obj );\n}\n\n/**\n * Test if given object is a JSON string or not.\n *\n * @param  mixed  $obj\n *\n * @return bool\n */\nfunction papi_is_json( $obj ) {\n\treturn is_string( $obj )\n\t\t&& is_array( json_decode( $obj, true ) )\n\t\t&& json_last_error() === JSON_ERROR_NONE;\n}\n\n/**\n * Maybe JSON decode the given string.\n *\n * @param  string $str\n * @param  bool   $assoc\n *\n * @return mixed\n */\nfunction papi_maybe_json_decode( $str, $assoc = false ) {\n\treturn papi_is_json( $str ) ? json_decode( $str, $assoc ) : $str;\n}\n\n/**\n * Maybe JSON encode the given object.\n *\n * @param  mixed $obj\n *\n * @return mixed\n */\nfunction papi_maybe_json_encode( $obj ) {\n\tif ( is_array( $obj ) || is_object( $obj ) ) {\n\t\treturn json_encode( $obj, JSON_UNESCAPED_UNICODE );\n\t}\n\n\treturn $obj;\n}\n\n/**\n * Maybe convert value to array if array or return the value.\n *\n * @param  mixed $obj\n *\n * @return mixed\n */\nfunction papi_maybe_convert_to_array( $obj ) {\n\treturn is_object( $obj ) ? (array) $obj : $obj;\n}\n\n/**\n * Maybe convert value to object if array or return the value.\n *\n * @param  mixed $obj\n *\n * @return mixed\n */\nfunction papi_maybe_convert_to_object( $obj ) {\n\treturn is_array( $obj ) ? (object) $obj : $obj;\n}\n\n/**\n * Papi get callable value if is it callable.\n *\n * @param  mixed $callable\n * @param  array $args\n *\n * @return mixed\n */\nfunction papi_maybe_get_callable_value( $callable, $args = [] ) {\n\tif ( is_callable( $callable ) ) {\n\t\t$ob_level = ob_get_level();\n\n\t\tob_start();\n\n\t\ttry {\n\t\t\tcall_user_func_array( $callable, papi_to_array( $args ) );\n\t\t} catch ( Exception $e ) {\n\t\t\twhile ( ob_get_level() > $ob_level ) {\n\t\t\t\tob_end_clean();\n\t\t\t}\n\n\t\t\treturn $callable;\n\t\t}\n\n\t\treturn ltrim( ob_get_clean() );\n\t}\n\n\treturn $callable;\n}\n\n/**\n * Replace '\\n' with '<br />'.\n *\n * @param  string $str\n *\n * @return string\n */\nfunction papi_nl2br( $str ) {\n\treturn str_replace( '\\n', '<br />', nl2br( $str ) );\n}\n\n/**\n * Render html tag from tag name and array of attributes.\n *\n * @param  string $tag\n * @param  array  $attr\n *\n * @return string\n */\nfunction papi_render_html_tag( $tag, $attr = [] ) {\n\techo papi_html_tag( $tag, $attr ); // xss\n}\n\n/**\n * Santize data.\n *\n * @param  mixed $obj\n *\n * @return mixed\n */\nfunction papi_santize_data( $obj ) {\n\tif ( is_array( $obj ) ) {\n\t\tforeach ( $obj as $k => $v ) {\n\t\t\tif ( is_string( $v ) ) {\n\t\t\t\t$obj[$k] = papi_santize_data( $v );\n\t\t\t}\n\t\t}\n\t} else if ( is_string( $obj ) ) {\n\t\t$obj = str_replace( \"\\'\", \"'\", str_replace( '\\\"', '\"', $obj ) );\n\t}\n\n\treturn $obj;\n}\n\n/**\n * Sort array based on given key and numeric value.\n *\n * @param  array  $array\n * @param  string $key\n *\n * @return array\n */\nfunction papi_sort_order( $array, $key = 'sort_order' ) {\n\tif ( empty( $array ) || ! is_array( $array ) && ! is_object( $array ) ) {\n\t\treturn [];\n\t}\n\n\tif ( is_object( $array ) ) {\n\t\t$array = papi_to_array( $array );\n\t}\n\n\t$sorter = [];\n\n\tforeach ( $array as $k => $value ) {\n\t\t$value = is_array( $value ) ? (object) $value : $value;\n\t\tif ( is_object( $value ) && isset( $value->$key ) ) {\n\t\t\t$sorter[$k] = $value->$key;\n\t\t\tcontinue;\n\t\t}\n\t}\n\n\t$i = 0;\n\t$default_sort = papi_filter_settings_sort_order();\n\n\tforeach ( $sorter as $k => $v ) {\n\t\tif ( $default_sort === $v ) {\n\t\t\t$sorter[$k] = $v - $i;\n\t\t\t$i++;\n\t\t}\n\t}\n\n\tasort( $sorter, SORT_NUMERIC );\n\n\t$result = [];\n\n\tforeach ( $sorter as $k => $v ) {\n\t\t$value = $array[$k];\n\t\t$value = is_array( $value ) ? (object) $value : $value;\n\n\t\tif ( is_object( $value ) && isset( $value->$key ) ) {\n\t\t\t$result[$k] = $array[$k];\n\t\t}\n\t}\n\n\treturn array_values( $result );\n}\n\n/**\n * Slugify the given string.\n *\n * @param  string $str\n * @param  array  $replace\n * @param  string $delimiter\n *\n * @return string\n */\nfunction papi_slugify( $str, $replace = [], $delimiter = '-' ) {\n\tif ( ! is_string( $str ) ) {\n\t\treturn '';\n\t}\n\n\tif ( ! empty( $replace ) ) {\n\t\t$str = str_replace( (array) $replace, ' ', $str );\n\t}\n\n\t$search   = ['/[^a-zA-Z0-9 \\.\\&\\/_-]+/', '/[ \\.\\&\\/-]+/'];\n\t$replace  = ['', $delimiter];\n\n\t$str = html_entity_decode( $str, ENT_QUOTES, 'UTF-8' );\n\t$str = remove_accents( $str );\n\t$str = preg_replace( $search, $replace, $str );\n\t$str = trim( $str, $delimiter );\n\t$str = strtolower( $str );\n\n\treturn $str;\n}\n\n/**\n * Get a array of $obj.\n *\n * @param  mixed $obj\n *\n * @return mixed\n */\nfunction papi_to_array( $obj ) {\n\tif ( ! is_array( $obj ) ) {\n\t\t$obj = [$obj];\n\t}\n\n\treturn $obj;\n}\n\n/**\n * Underscorify the given string.\n * Replacing whitespace and dash with a underscore.\n *\n * @param  string $str\n *\n * @return string\n */\nfunction papi_underscorify( $str ) {\n\tif ( ! is_string( $str ) ) {\n\t\treturn '';\n\t}\n\n\treturn str_replace( ' ', '_', str_replace( '-', '_', $str ) );\n}\n"
  },
  {
    "path": "src/lib/fields/option.php",
    "content": "<?php\n\n/**\n * Delete value in the database.\n *\n * @param  string $slug\n *\n * @return bool\n */\nfunction papi_delete_option( $slug ) {\n\treturn papi_delete_field( 0, $slug, 'option' );\n}\n\n/**\n * Get property value for property on a option page.\n *\n * @param  string $slug\n * @param  mixed  $default\n *\n * @return mixed\n */\nfunction papi_get_option( $slug, $default = null ) {\n\treturn papi_get_field( 0, $slug, $default, 'option' );\n}\n\n/**\n * Shortcode for `papi_get_option` function.\n *\n * [papi_option slug=\"property_slug\" default=\"Default value\"][/papi_option]\n *\n * @param  array $atts\n *\n * @return mixed\n */\nfunction papi_option_shortcode( $atts ) {\n\t$default = isset( $atts['default'] ) ? $atts['default'] : '';\n\n\tif ( empty( $atts['slug'] ) ) {\n\t\t$value = $default;\n\t} else {\n\t\t$value = papi_get_option( $atts['slug'], $default );\n\t}\n\n\tif ( is_array( $value ) ) {\n\t\t$value = implode( ', ', $value );\n\t}\n\n\treturn $value;\n}\n\nadd_shortcode( 'papi_option', 'papi_option_shortcode' );\n\n/**\n * Update field with new value. The old value will be deleted.\n *\n * @param  string $slug\n * @param  mixed  $value\n *\n * @return bool\n */\nfunction papi_update_option( $slug, $value = null ) {\n\treturn papi_update_field( 0, $slug, $value, 'option' );\n}\n\n/**\n * Echo the value for property on a option page.\n *\n * @param string $slug\n * @param mixed  $default\n */\nfunction the_papi_option( $slug = null, $default = null ) {\n\t$value = papi_get_option( $slug, $default );\n\n\tif ( is_array( $value ) ) {\n\t\t$value = implode( ', ', $value );\n\t}\n\n\tif ( is_object( $value ) ) {\n\t\t$value = papi_convert_to_string( $value );\n\t}\n\n\techo $value; // phpcodesniffer xss whitelist\n}\n"
  },
  {
    "path": "src/lib/fields/page.php",
    "content": "<?php\n\n/**\n * Delete value in the database.\n *\n * @param  int    $id\n * @param  string $slug\n * @param  string $type\n *\n * @return bool\n */\nfunction papi_delete_field( $id = null, $slug = null, $type = 'post' ) {\n\tif ( ! is_numeric( $id ) && is_string( $id ) ) {\n\t\t$type = empty( $slug ) ? $type : $slug;\n\t\t$slug = $id;\n\t\t$id   = null;\n\t}\n\n\tif ( ! is_string( $slug ) || empty( $slug ) ) {\n\t\treturn false;\n\t}\n\n\t$id    = papi_get_meta_id( $type, $id );\n\t$store = papi_get_meta_store( $id, $type );\n\n\tif ( is_null( $store ) ) {\n\t\treturn false;\n\t}\n\n\t// Replace \"-\" with \"_\".\n\t$slug = papi_underscorify( $slug );\n\n\t$property = $store->get_property( $slug );\n\n\tif ( ! papi_is_property( $property ) ) {\n\t\treturn false;\n\t}\n\n\tpapi_cache_delete( $slug, $id, $type );\n\n\t/**\n\t * Fire action before value is deleted.\n\t *\n\t * @param string $type\n\t * @param string $slug\n\t * @param int    $post_id\n\t */\n\tdo_action( 'papi/delete_value/' . $type, $slug, $id );\n\n\treturn $property->delete_value( $slug, $id, $type );\n}\n\n/**\n * Shortcode for `papi_get_field` function.\n *\n * [papi_field id=1 slug=\"property_slug\" default=\"Default value\"][/papi_field]\n *\n * @param  array $atts\n *\n * @return mixed\n */\nfunction papi_field_shortcode( $atts ) {\n\t$atts['id'] = isset( $atts['id'] ) ? $atts['id'] : 0;\n\t$atts['id'] = papi_get_post_id( $atts['id'] );\n\t$default    = isset( $atts['default'] ) ? $atts['default'] : '';\n\n\tif ( empty( $atts['id'] ) || empty( $atts['slug'] ) ) {\n\t\t$value = $default;\n\t} else {\n\t\t$value = papi_get_field( $atts['id'], $atts['slug'], $default );\n\t}\n\n\tif ( is_array( $value ) ) {\n\t\t$value = implode( ', ', $value );\n\t}\n\n\treturn $value;\n}\n\nadd_shortcode( 'papi_field', 'papi_field_shortcode' );\n\n/**\n * Get field value by keys.\n *\n * Example:\n *\n * \"image.url\" will get the url value in the array.\n *\n * @param  array $slugs\n * @param  mixed $value\n * @param  mixed $default\n *\n * @return mixed\n */\nfunction papi_field_value( $slugs, $value, $default = null ) {\n\tif ( empty( $value ) && is_null( $value ) ) {\n\t\treturn $default;\n\t}\n\n\tif ( ! empty( $slugs ) && ( is_object( $value ) || is_array( $value ) ) ) {\n\n\t\tif ( is_object( $value ) ) {\n\t\t\t$value = (array) $value;\n\t\t}\n\n\t\tforeach ( $slugs as $key ) {\n\t\t\tif ( isset( $value[$key] ) ) {\n\t\t\t\t$value = $value[$key];\n\t\t\t}\n\t\t}\n\t}\n\n\treturn $value;\n}\n\n/**\n * Get value for a property on a page.\n *\n * @param  int    $id\n * @param  string $slug\n * @param  mixed  $default\n * @param  string $type\n *\n * @return mixed\n */\nfunction papi_get_field( $id = null, $slug = null, $default = null, $type = 'post' ) {\n\tif ( ! is_numeric( $id ) && is_string( $id ) ) {\n\t\t$type    = empty( $default ) ? $type : $default;\n\t\t$default = $slug;\n\t\t$slug    = $id;\n\t\t$id      = null;\n\t}\n\n\tif ( ! is_string( $slug ) || empty( $slug ) ) {\n\t\treturn $default;\n\t}\n\n\t// Replace \"-\" with \"_\".\n\t$slug = papi_underscorify( $slug );\n\n\t// Check for \"dot\" notation.\n\t$slugs = explode( '.', $slug );\n\t$slug  = $slugs[0];\n\t$slugs = array_slice( $slugs, 1 );\n\n\t// Get right id for right meta type.\n\t$id = papi_get_meta_id( $type, $id );\n\n\t// Get the right store for right entry type.\n\t$store = papi_get_meta_store( $id, $type );\n\n\t// Return the default value if we don't have a valid store.\n\tif ( is_null( $store ) ) {\n\t\treturn $default;\n\t}\n\n\t// Get value from store.\n\t$value = $store->get_value( $id, $slug, $default, $type );\n\n\t// Get value by dot keys if any.\n\treturn papi_field_value( $slugs, $value, $default );\n}\n\n/**\n * Get all fields for a page type.\n *\n * @param  int    $id\n * @param  string $type\n *\n * @return array\n */\nfunction papi_get_fields( $id = 0, $type = 'post' ) {\n\t$store = papi_get_meta_store( $id, $type );\n\n\tif ( $store instanceof Papi_Core_Meta_Store === false ) {\n\t\treturn [];\n\t}\n\n\t$entry_type = $store->get_type_class();\n\tif ( empty( $entry_type ) ) {\n\t\treturn [];\n\t}\n\n\t$value = [];\n\t$boxes = $entry_type->get_boxes();\n\n\tforeach ( $boxes as $box ) {\n\t\tforeach ( $box->properties as $property ) {\n\t\t\t$slug = $property->get_slug( true );\n\t\t\t$value[$slug] = papi_get_field( $id, $slug, null, $type );\n\t\t}\n\t}\n\n\t$fields = $entry_type->fields( $value );\n\t$type   = $type === 'post' ? '' : '_' . $type;\n\n\t/**\n\t * Modify fields returned.\n\t *\n\t * Different filters for different types:\n\t *\n\t * - Post: papi/get_fields\n\t * - Term: papi/get_term_fields\n\t *\n\t * @param  array  $fields\n\t * @param  string $entry_type\n\t *\n\t * @return array\n\t */\n\treturn apply_filters( sprintf( 'papi/get_%sfields', $type ), $fields, $entry_type->get_id() );\n}\n\n/**\n * Get boxes with properties slug for a page.\n *\n * @param  int    $id\n * @param  bool   $only_slugs\n * @param  string $type\n *\n * @since 3.0.0 the param `$only_slugs` exists, if true\n * will it only return the slugs without boxes title.\n *\n * @since 3.1.0 `$id` param is optional.\n *\n * @return array\n */\nfunction papi_get_slugs( $id = 0, $only_slugs = false, $type = 'post' ) {\n\tif ( is_bool( $id ) ) {\n\t\t$type       = empty( $only_slugs ) ? $type : $only_slugs;\n\t\t$only_slugs = $id;\n\t\t$id         = null;\n\t}\n\n\t$store = papi_get_meta_store( $id, $type );\n\n\tif ( $store instanceof Papi_Core_Meta_Store === false ) {\n\t\treturn [];\n\t}\n\n\t$entry_type = $store->get_type_class();\n\n\tif ( empty( $entry_type ) ) {\n\t\treturn [];\n\t}\n\n\t$value = [];\n\t$boxes = $entry_type->get_boxes();\n\n\tforeach ( $boxes as $box ) {\n\t\tif ( ! $only_slugs ) {\n\t\t\t$title = $box->title;\n\n\t\t\tif ( ! isset( $value[$title] ) ) {\n\t\t\t\t$value[$title] = [];\n\t\t\t}\n\t\t}\n\n\t\tforeach ( $box->properties as $property ) {\n\t\t\t$slug = $property->get_slug( true );\n\n\t\t\tif ( $only_slugs ) {\n\t\t\t\t$value[] = $slug;\n\t\t\t} else {\n\t\t\t\t$value[$title][] = $slug;\n\t\t\t}\n\t\t}\n\t}\n\n\treturn $only_slugs ? array_unique( $value ) : $value;\n}\n\n/**\n * Update field with new value. The old value will be deleted.\n *\n * @param  int    $id\n * @param  string $slug\n * @param  mixed  $value\n * @param  string $type\n *\n * @return bool\n */\nfunction papi_update_field( $id = null, $slug = null, $value = null, $type = 'post' ) {\n\tif ( ! is_numeric( $id ) && is_string( $id ) ) {\n\t\t$type  = empty( $value ) ? $value : $type;\n\t\t$value = $slug;\n\t\t$slug  = $id;\n\t\t$id    = null;\n\t}\n\n\tif ( ! is_string( $slug ) || empty( $slug ) ) {\n\t\treturn false;\n\t}\n\n\tif ( papi_is_empty( $value ) ) {\n\t\treturn papi_delete_field( $id, $slug, $type );\n\t}\n\n\t$id    = papi_get_meta_id( $type, $id );\n\t$store = papi_get_meta_store( $id, $type );\n\n\tif ( is_null( $store ) ) {\n\t\treturn false;\n\t}\n\n\t// Replace \"-\" with \"_\".\n\t$slug = papi_underscorify( $slug );\n\n\t$property = $store->get_property( $slug );\n\n\tif ( ! papi_is_property( $property ) ) {\n\t\treturn false;\n\t}\n\n\tpapi_delete_field( $id, $slug, $type );\n\n\t$value = $property->update_value( $value, $slug, $id );\n\t$value = papi_filter_update_value( $property->get_option( 'type' ), $value, $slug, $id, $type );\n\n\treturn papi_data_update( $id, $slug, $value, $type );\n}\n\n/**\n * Echo the value for property.\n *\n * @param int    $id\n * @param string $slug\n * @param mixed  $default\n */\nfunction the_papi_field( $id = null, $slug = null, $default = null ) {\n\t$value = papi_get_field( $id, $slug, $default );\n\n\tif ( is_array( $value ) ) {\n\t\t$value = implode( ', ', $value );\n\t}\n\n\tif ( is_object( $value ) ) {\n\t\t$value = papi_convert_to_string( $value );\n\t}\n\n\techo $value; // phpcodesniffer xss whitelist\n}\n"
  },
  {
    "path": "src/lib/fields/taxonomy.php",
    "content": "<?php\n\n/**\n * Delete property value in the database.\n *\n * @param  int    $term_id\n * @param  string $slug\n *\n * @return bool\n */\nfunction papi_delete_term_field( $term_id, $slug = '' ) {\n\tif ( ! is_numeric( $term_id ) && is_string( $term_id ) ) {\n\t\t$slug    = $term_id;\n\t\t$term_id = null;\n\t}\n\n\tif ( ! is_string( $slug ) || empty( $slug ) ) {\n\t\treturn false;\n\t}\n\n\treturn papi_delete_field( papi_get_term_id( $term_id ), $slug, 'term' );\n}\n\n/**\n * Get property value from the database.\n *\n * @param  int    $term_id\n * @param  string $slug\n * @param  mixed  $default\n *\n * @return mixed\n */\nfunction papi_get_term_field( $term_id = null, $slug = null, $default = null ) {\n\tif ( ! is_numeric( $term_id ) && is_string( $term_id ) ) {\n\t\t$default = $slug;\n\t\t$slug    = $term_id;\n\t\t$term_id = null;\n\t}\n\n\tif ( ! is_string( $slug ) || empty( $slug ) ) {\n\t\treturn $default;\n\t}\n\n\treturn papi_get_field( papi_get_term_id( $term_id ), $slug, $default, 'term' );\n}\n\n/**\n * Get all fields for a taxonomy type.\n *\n * @param  int    $id\n *\n * @return array\n */\nfunction papi_get_term_fields( $id = 0 ) {\n\treturn papi_get_fields( papi_get_term_id( $id ), 'term' );\n}\n\n/**\n * Get boxes with properties slug for a taxonomy.\n *\n * @param  int $id\n * @param  bool $only_slugs\n *\n * @since 3.1.0 `$id` param is optional.\n *\n * @return array\n */\nfunction papi_get_term_slugs( $id = 0, $only_slugs = false ) {\n\tif ( is_bool( $id ) ) {\n\t\t$only_slugs = $id;\n\t\t$id         = null;\n\t}\n\n\treturn papi_get_slugs( papi_get_term_id( $id ), $only_slugs, 'term' );\n}\n\n/**\n * Shortcode for `papi_get_term_field` function.\n *\n * [papi_taxonomy id=1 slug=\"property_slug\" default=\"Default value\"][/papi_taxonomy]\n *\n * @param  array $atts\n *\n * @return mixed\n */\nfunction papi_taxonomy_shortcode( $atts ) {\n\t$atts['id'] = isset( $atts['id'] ) ? $atts['id'] : 0;\n\t$atts['id'] = papi_get_term_id( $atts['id'] );\n\t$default    = isset( $atts['default'] ) ? $atts['default'] : '';\n\n\tif ( empty( $atts['id'] ) || empty( $atts['slug'] ) ) {\n\t\t$value = $default;\n\t} else {\n\t\t$value = papi_get_term_field( $atts['id'], $atts['slug'], $default );\n\t}\n\n\tif ( is_array( $value ) ) {\n\t\t$value = implode( ', ', $value );\n\t}\n\n\treturn $value;\n}\n\nadd_shortcode( 'papi_taxonomy', 'papi_taxonomy_shortcode' );\n\n/**\n * Update property with new value. The old value will be deleted.\n *\n * @param  int    $term_id\n * @param  string $slug\n * @param  mixed  $value\n *\n * @return bool\n */\nfunction papi_update_term_field( $term_id = null, $slug = null, $value = null ) {\n\tif ( ! is_numeric( $term_id ) && is_string( $term_id ) ) {\n\t\t$value   = $slug;\n\t\t$slug    = $term_id;\n\t\t$term_id = null;\n\t}\n\n\tif ( ! is_string( $slug ) || empty( $slug ) ) {\n\t\treturn false;\n\t}\n\n\tif ( papi_is_empty( $value ) ) {\n\t\treturn papi_delete_term_field( $term_id, $slug );\n\t}\n\n\treturn papi_update_field( papi_get_term_id( $term_id ), $slug, $value, 'term' );\n}\n\n/**\n * Echo the value for property.\n *\n * @param int    $term_id\n * @param string $slug\n * @param mixed  $default\n */\nfunction the_papi_term_field( $term_id = null, $slug = null, $default = null ) {\n\t$value = papi_get_term_field( $term_id, $slug, $default );\n\n\tif ( is_array( $value ) ) {\n\t\t$value = implode( ', ', $value );\n\t}\n\n\tif ( is_object( $value ) ) {\n\t\t$value = papi_convert_to_string( $value );\n\t}\n\n\techo $value; // phpcodesniffer xss whitelist\n}\n"
  },
  {
    "path": "src/lib/hooks/actions.php",
    "content": "<?php\n"
  },
  {
    "path": "src/lib/hooks/filters-page-type.php",
    "content": "<?php\n\n/**\n * Get the only page type that will be used for the given post type.\n *\n * @param  string $post_type\n *\n * @return string\n */\nfunction papi_filter_settings_only_page_type( $post_type ) {\n\t$page_type = apply_filters( 'papi/settings/only_page_type_' . $post_type, '' );\n\n\tif ( ! is_string( $page_type ) ) {\n\t\treturn '';\n\t}\n\n\treturn str_replace( '.php', '', $page_type );\n}\n\n/**\n * Show page type in add new page view for the given post type.\n *\n * @param string        $post_type\n * @param string|object $page_type\n *\n * @return bool\n */\nfunction papi_filter_settings_show_page_type( $post_type, $page_type ) {\n\tif ( is_object( $page_type ) && method_exists( $page_type, 'get_id' ) ) {\n\t\t$page_type = $page_type->get_id();\n\t}\n\n\t$value = apply_filters( 'papi/settings/show_page_type_' . $post_type, $page_type );\n\n\tif ( $value === $page_type ) {\n\t\treturn true;\n\t}\n\n\tif ( ! is_bool( $value ) ) {\n\t\treturn false;\n\t}\n\n\treturn $value;\n}\n\n/**\n * Get standard page description for the given post type.\n *\n * @param  string $post_type\n *\n * @return string\n */\nfunction papi_filter_settings_standard_page_type_description( $post_type ) {\n\t$name = papi_get_post_type_label( $post_type, 'singular_name', 'Page' );\n\n\t// New filter, with `type` in the filter tag.\n\t$tag = 'papi/settings/standard_page_type_description_' . $post_type;\n\t$out = apply_filters( $tag, sprintf( __( '%s with WordPress standard fields', 'papi' ), $name ) );\n\n\t// Old filter, that didn't have `type` in the filter tag.\n\t// Should work until Papi 4.0.0.\n\t$tag = 'papi/settings/standard_page_description_' . $post_type;\n\t$out = apply_filters( $tag, $out );\n\n\treturn $out;\n}\n\n/**\n * Get standard page name for the given post type.\n *\n * @param  string $post_type\n *\n * @return string\n */\nfunction papi_filter_settings_standard_page_type_name( $post_type ) {\n\t$name = papi_get_post_type_label( $post_type, 'singular_name', 'Page' );\n\n\t// New filter, with `type` in the filter tag.\n\t$tag = 'papi/settings/standard_page_type_name_' . $post_type;\n\t$out = apply_filters( $tag, sprintf( __( 'Standard %s', 'papi' ), $name ) );\n\n\t// Old filter, that didn't have `type` in the filter tag.\n\t// Should work until Papi 4.0.0.\n\t$tag = 'papi/settings/standard_page_name_' . $post_type;\n\t$out = apply_filters( $tag, $out );\n\n\treturn $out;\n}\n\n/**\n * Show standard page type on the given post type.\n *\n * @param  string $post_type\n *\n * @return bool\n */\nfunction papi_filter_settings_show_standard_page_type( $post_type ) {\n\treturn ! apply_filters( 'papi/settings/show_standard_page_type_' . $post_type, false ) === false;\n}\n\n/**\n * Show standard page type in filter dropdown on the given post type.\n *\n * @param  string $post_type\n *\n * @return bool\n */\nfunction papi_filter_settings_show_standard_page_type_in_filter( $post_type ) {\n\t$tag = 'papi/settings/show_standard_page_type_in_filter_' . $post_type;\n\n\treturn ! apply_filters( $tag, papi_filter_settings_show_standard_page_type( $post_type ) ) === false;\n}\n\n/**\n * Get standard page thumbnail for the given post type.\n *\n * @param  string $post_type\n *\n * @return string\n */\nfunction papi_filter_settings_standard_page_type_thumbnail( $post_type ) {\n\t// New filter, with `type` in the filter tag.\n\t$tag = 'papi/settings/standard_page_type_thumbnail_' . $post_type;\n\t$out = apply_filters( $tag, '' );\n\n\t// Old filter, that didn't have `type` in the filter tag.\n\t// Should work until Papi 4.0.0.\n\t$tag = 'papi/settings/standard_page_thumbnail_' . $post_type;\n\t$out = apply_filters( $tag, $out );\n\n\treturn $out;\n}\n"
  },
  {
    "path": "src/lib/hooks/filters-taxonomy-type.php",
    "content": "<?php\n\n/**\n * Get the only taxonomy type that will be used for the given taxonomy.\n *\n * @param  string $taxonomy\n *\n * @return string\n */\nfunction papi_filter_settings_only_taxonomy_type( $taxonomy ) {\n\t$taxonomy_type = apply_filters( 'papi/settings/only_taxonomy_type_' . $taxonomy, '' );\n\n\tif ( ! is_string( $taxonomy_type ) ) {\n\t\treturn '';\n\t}\n\n\treturn str_replace( '.php', '', $taxonomy_type );\n}\n\n/**\n * Get standard taxonomy name for the given taxonomy.\n *\n * @param  string $taxonomy\n *\n * @return string\n */\nfunction papi_filter_settings_standard_taxonomy_type_name( $taxonomy ) {\n\t$name = papi_get_taxonomy_label( $taxonomy, 'singular_name', 'Taxonomy' );\n\t$tag = 'papi/settings/standard_taxonomy_name_' . $taxonomy;\n\n\treturn apply_filters( $tag, sprintf( __( 'Standard %s', 'papi' ), $name ) );\n}\n\n/**\n * Show standard taxonomy type on the given taxonomy.\n *\n * @param  string $taxonomy\n *\n * @return bool\n */\nfunction papi_filter_settings_show_standard_taxonomy_type( $taxonomy ) {\n\treturn ! apply_filters( 'papi/settings/show_standard_taxonomy_type_' . $taxonomy, false ) === false;\n}\n"
  },
  {
    "path": "src/lib/hooks/filters.php",
    "content": "<?php\n\n/**\n * Get conditional rule value.\n *\n * @param  array|Papi_Core_Conditional_Rule $rule\n *\n * @return bool\n */\nfunction papi_filter_conditional_rule_allowed( $rule ) {\n\t$rule = papi_rule( $rule );\n\n\tif ( ! papi_is_rule( $rule ) ) {\n\t\treturn false;\n\t}\n\n\t$result = apply_filters( 'papi/conditional/rule/' . $rule->operator, $rule );\n\n\tif ( $result === true || $result === false ) {\n\t\treturn $result;\n\t}\n\n\treturn false;\n}\n\n/**\n * Format the value of the property before it's returned to WordPress admin or the site.\n *\n * @since  3.1.0 `$meta_type` argument was added.\n *\n * @param  string $type\n * @param  mixed  $value\n * @param  string $slug\n * @param  int    $id\n * @param  string $meta_type\n *\n * @return mixed\n */\nfunction papi_filter_format_value( $type, $value, $slug, $id, $meta_type = 'post' ) {\n\treturn apply_filters( 'papi/format_value/' . $type, $value, $slug, $id, $meta_type );\n}\n\n/**\n * This filter is applied after the value is loaded in the database.\n *\n * @since  3.1.0 `$meta_type` argument was added.\n *\n * @param  string $type\n * @param  mixed  $value\n * @param  string $slug\n * @param  int    $id\n * @param  string $meta_type\n *\n * @return mixed\n */\nfunction papi_filter_load_value( $type, $value, $slug, $id, $meta_type = 'post' ) {\n\treturn apply_filters( 'papi/load_value/' . $type, $value, $slug, $id, $meta_type );\n}\n\n/**\n * Get all registered page type directories.\n *\n * @return array\n */\nfunction papi_filter_settings_directories() {\n\t$directories = apply_filters( 'papi/settings/directories', [] );\n\n\tif ( empty( $directories ) ) {\n\t\t$directories = get_template_directory() . '/page-types';\n\t}\n\n\tif ( is_string( $directories ) ) {\n\t\treturn [$directories];\n\t}\n\n\tif ( ! is_array( $directories ) ) {\n\t\treturn [];\n\t}\n\n\treturn array_filter( $directories, function ( $directory ) {\n\t\treturn is_string( $directory );\n\t} );\n}\n\n/**\n * Get the default sort order that is 1000.\n *\n * @return int\n */\nfunction papi_filter_settings_sort_order() {\n\treturn intval( apply_filters( 'papi/settings/sort_order', 1000 ) );\n}\n\n/**\n * This filter is applied before the value is saved in the database.\n *\n * @since  3.1.0 `$meta_type` argument was added.\n *\n * @param  string $type\n * @param  mixed  $value\n * @param  string $slug\n * @param  int    $id\n * @param  string $meta_type\n *\n * @return mixed\n */\nfunction papi_filter_update_value( $type, $value, $slug, $id, $meta_type = 'post' ) {\n\treturn apply_filters( 'papi/update_value/' . $type, $value, $slug, $id, $meta_type );\n}\n"
  },
  {
    "path": "src/lib/types/entry.php",
    "content": "<?php\n\n/**\n * Get entry type body classes.\n *\n * @param  int    $id\n * @param  string $type\n *\n * @return string\n */\nfunction papi_get_entry_type_body_classes( $id = 0, $type = null ) {\n\t$entry_type_id = papi_get_entry_type_id( $id, $type );\n\n\tif ( empty( $entry_type_id ) ) {\n\t\treturn [];\n\t}\n\n\t$entry_type = papi_get_entry_type_by_id( $entry_type_id );\n\n\tif ( $entry_type instanceof Papi_Entry_Type === false ) {\n\t\treturn [];\n\t}\n\n\t$classes = $entry_type->body_classes();\n\t$classes = is_array( $classes ) ? $classes : [];\n\n\treturn $classes;\n}\n\n/**\n * Get entry type css class, it will split the entry type id\n * on slash and take the last part of the id.\n *\n * @param  int    $id\n * @param  string $type\n *\n * @return string\n */\nfunction papi_get_entry_type_css_class( $id = 0, $type = null ) {\n\t$entry_type = papi_get_entry_type_id( $id, $type );\n\n\tif ( empty( $entry_type ) ) {\n\t\treturn '';\n\t}\n\n\t$parts = explode( '/', $entry_type );\n\n\tif ( empty( $parts ) || empty( $parts[0] ) ) {\n\t\treturn '';\n\t}\n\n\treturn array_pop( $parts );\n}\n\n/**\n * Count entry types in the database for the\n * given entry type.\n *\n * @param  string|Papi_Entry_Type $entry_type\n *\n * @return int\n */\nfunction papi_get_entry_type_count( $entry_type ) {\n\tglobal $wpdb;\n\n\tif ( empty( $entry_type ) || ( ! is_string( $entry_type ) && ( ! is_object( $entry_type ) ) ) ) {\n\t\treturn 0;\n\t}\n\n\tif ( is_string( $entry_type ) ) {\n\t\t$entry_type = papi_get_entry_type_by_id( $entry_type );\n\t}\n\n\tif ( $entry_type instanceof Papi_Entry_Type === false ) {\n\t\treturn 0;\n\t}\n\n\t$table = sprintf( '%s%smeta', $wpdb->prefix, papi_get_meta_type( $entry_type->get_type() ) );\n\n\t// @codingStandardsIgnoreStart\n\t$sql = $wpdb->prepare(\n\t\t\"SELECT COUNT(*) FROM `$table` WHERE `meta_key` = '%s' AND `meta_value` = '%s'\",\n\t\tpapi_get_page_type_key(),\n\t\t$entry_type->get_id()\n\t);\n\n\treturn intval( $wpdb->get_var( $sql ) );\n\t// @codingStandardsIgnoreEnd\n}\n\n/**\n * Check if entry type exists.\n *\n * @param  string $id\n *\n * @return bool\n */\nfunction papi_entry_type_exists( $id ) {\n\tif ( papi()->exists( $id ) ) {\n\t\treturn true;\n\t}\n\n\t$entry_types = papi_get_all_entry_types();\n\n\tforeach ( $entry_types as $entry_type ) {\n\t\tif ( $entry_type->match_id( $id ) ) {\n\t\t\treturn true;\n\t\t}\n\t}\n\n\treturn false;\n}\n\n/**\n * Get all entry types that exists.\n *\n * @param  array $args {\n *     @type bool         $all\n *     @type mixed        $args\n *     @type bool         $cache\n *     @type array|string $id\n *     @type string       $mode\n *     @type array|string $types\n * }\n *\n * @return array\n */\nfunction papi_get_all_entry_types( array $args = [] ) {\n\t$default_args = [\n\t\t'all'   => true,\n\t\t'args'  => [],\n\t\t'cache' => true,\n\t\t'mode'  => 'include',\n\t\t'types' => []\n\t];\n\n\t$args = array_merge( $default_args, $args );\n\n\tif ( ! is_array( $args['types'] ) ) {\n\t\t$args['types'] = [$args['types']];\n\t}\n\n\tif ( ! is_array( $args['args'] ) ) {\n\t\t$args['args'] = [$args['args']];\n\t}\n\n\t$args['args'] = array_filter( $args['args'] );\n\n\tif ( ! empty( $args['types'] ) ) {\n\t\t$args['all'] = false;\n\t}\n\n\t$cache_key = papi_cache_key( __FUNCTION__, md5( serialize( $args ) ) );\n\n\tif ( ! $args['cache'] ) {\n\t\tpapi()->remove( 'papi_get_all_core_type_files' );\n\t\tpapi()->remove( $cache_key );\n\t}\n\n\tif ( papi()->exists( $cache_key ) ) {\n\t\tif ( $entry_types = papi()->make( $cache_key ) ) {\n\t\t\treturn $entry_types;\n\t\t}\n\n\t\tpapi()->remove( $cache_key );\n\t\tpapi()->remove( 'papi_get_all_core_type_files' );\n\t}\n\n\t$singletons  = [];\n\t$entry_types = [];\n\t$files       = papi_get_all_core_type_files();\n\n\tforeach ( $files as $file ) {\n\t\t$entry_type = papi_get_entry_type( $file );\n\n\t\tif ( is_null( $entry_type ) ) {\n\t\t\tcontinue;\n\t\t}\n\n\t\t// Only entry types can be loaded.\n\t\t// @codeCoverageIgnoreStart\n\t\tif ( $entry_type instanceof Papi_Entry_Type === false ) {\n\t\t\tcontinue;\n\t\t}\n\t\t// @codeCoverageIgnoreEnd\n\n\t\tif ( $entry_type->singleton() ) {\n\t\t\tif ( isset( $singletons[$entry_type->get_type()] ) ) {\n\t\t\t\tcontinue;\n\t\t\t} else {\n\t\t\t\t$singletons[$entry_type->get_type()] = true;\n\t\t\t}\n\t\t}\n\n\t\t$id         = $entry_type->get_id();\n\t\t$valid_type = in_array( $entry_type->get_type(), $args['types'], true );\n\t\t$valid_type = $args['mode'] === 'include' ? $valid_type : ! $valid_type;\n\n\t\tif ( $args['all'] || ( $valid_type && call_user_func_array( [$entry_type, 'allowed'], $args['args'] ) ) ) {\n\t\t\t$entry_type->boot();\n\t\t\t$entry_types[$id] = $entry_type;\n\t\t}\n\t}\n\n\t$entry_types = array_values( $entry_types );\n\n\tif ( is_array( $entry_types ) ) {\n\t\tusort( $entry_types, function ( $a, $b ) {\n\t\t\treturn strcmp( $a->name, $b->name );\n\t\t} );\n\t}\n\n\t$entry_types = papi_sort_order( array_reverse( $entry_types ) );\n\n\tif ( ! papi()->exists( $cache_key ) ) {\n\t\tpapi()->singleton( $cache_key, $entry_types );\n\t}\n\n\treturn $entry_types;\n}\n\n/**\n * Get a entry type by file path.\n *\n * @param  string $file_path\n *\n * @return Papi_Entry_Type\n */\nfunction papi_get_entry_type( $file_path ) {\n\tif ( ! is_file( $file_path ) || ! is_string( $file_path ) ) {\n\t\treturn;\n\t}\n\n\t$id = papi_get_core_type_base_path( $file_path );\n\n\t// Check if file path is changed.\n\tif ( $id === $file_path ) {\n\t\treturn;\n\t}\n\n\tif ( ! papi()->exists( $id ) ) {\n\t\t$class_name = papi_get_class_name( $file_path );\n\n\t\t// @codeCoverageIgnoreStart\n\t\tif ( ! class_exists( $class_name ) ) {\n\t\t\trequire_once $file_path;\n\t\t}\n\n\t\tif ( ! class_exists( $class_name ) ) {\n\t\t\treturn;\n\t\t}\n\t\t// @codeCoverageIgnoreEnd\n\n\t\t$rc = new ReflectionClass( $class_name );\n\n\t\t// Bail if not instantiable.\n\t\tif ( ! $rc->isInstantiable() ) {\n\t\t\treturn;\n\t\t}\n\n\t\t$entry_type = $rc->newInstanceArgs( [$file_path] );\n\n\t\t// If the entry type don't have a name we can't use it.\n\t\tif ( ! $entry_type->has_name() ) {\n\t\t\treturn;\n\t\t}\n\n\t\tpapi()->singleton( $id, $entry_type );\n\t}\n\n\treturn papi()->make( $id );\n}\n\n/**\n * Get entry type by identifier.\n *\n * @param  string $id\n *\n * @return Papi_Entry_Type\n */\nfunction papi_get_entry_type_by_id( $id ) {\n\tif ( ! is_string( $id ) || empty( $id ) ) {\n\t\treturn;\n\t}\n\n\tif ( papi()->exists( $id ) ) {\n\t\t$value = papi()->make( $id );\n\n\t\tif ( $value instanceof Papi_Entry_Type ) {\n\t\t\treturn $value;\n\t\t}\n\t}\n\n\t$result      = null;\n\t$entry_types = papi_get_all_entry_types();\n\n\tforeach ( $entry_types as $entry_type ) {\n\t\tif ( $entry_type->match_id( $id ) ) {\n\t\t\t$result = $entry_type;\n\t\t\tbreak;\n\t\t}\n\t}\n\n\tif ( is_null( $result ) ) {\n\t\t$path   = papi_get_file_path( $id );\n\t\t$result = papi_get_entry_type( $path );\n\t}\n\n\t// Check for class prefixed id when a old id that don't have the class prefix.\n\tif ( is_null( $result ) && strpos( $id, 'class-' ) === false ) {\n\t\t$parts = explode( '/', $id );\n\t\t$parts[count( $parts ) - 1] = 'class-' . $parts[count( $parts ) - 1];\n\t\t$result = papi_get_entry_type_by_id( implode( '/', $parts ) );\n\t}\n\n\treturn $result;\n}\n\n/**\n * Get entry type from meta id.\n *\n * @param  int    $id\n * @param  string $type\n *\n * @return Papi_Entry_Type\n */\nfunction papi_get_entry_type_by_meta_id( $id = 0, $type = null ) {\n\tif ( ! is_numeric( $id ) ) {\n\t\treturn;\n\t}\n\n\t$type = papi_get_meta_type( $type );\n\t$id   = papi_get_meta_id( $type, $id );\n\n\tif ( $id === 0 ) {\n\t\treturn;\n\t}\n\n\tif ( $entry_type = papi_get_entry_type_id( $id, $type ) ) {\n\t\treturn papi_get_entry_type_by_id( $entry_type );\n\t}\n}\n\n/**\n * Get entry type id.\n *\n * @param  int    $id\n * @param  string $type\n *\n * @return string\n */\nfunction papi_get_entry_type_id( $id = 0, $type = null ) {\n\t$type = papi_get_meta_type( $type );\n\t$id   = papi_get_meta_id( $type, $id );\n\n\tif ( $id > 0 ) {\n\t\tif ( $meta_value = get_metadata( $type, $id, papi_get_page_type_key(), true ) ) {\n\t\t\treturn $meta_value;\n\t\t}\n\t}\n\n\t$entry_type_id = papi_get_qs( 'entry_type' );\n\n\t/**\n\t * Change entry type id.\n\t *\n\t * @param  string $entry_type_id\n\t * @param  string $type\n\t * @param  int    $id\n\t *\n\t * @return string\n\t */\n\treturn apply_filters( 'papi/entry_type_id', $entry_type_id, $type, $id );\n}\n\n/**\n * Get entry template file from meta id.\n *\n * @param  int    $id\n * @param  string $type\n *\n * @return null|string\n */\nfunction papi_get_entry_type_template( $id = 0, $type = null ) {\n\tif ( empty( $id ) && ! is_numeric( $id ) ) {\n\t\treturn;\n\t}\n\n\t$data = papi_get_entry_type_by_meta_id( $id, $type );\n\n\tif ( ! isset( $data, $data->template ) ) {\n\t\treturn;\n\t}\n\n\t$template = $data->template;\n\n\t// Check if template is array.\n\tif ( is_array( $template ) ) {\n\t\tforeach ( $template as $key => $value ) {\n\t\t\t// Convert string template to array.\n\t\t\tif ( is_string( $value ) ) {\n\t\t\t\t$template[$key] = [\n\t\t\t\t\t'template' => $value\n\t\t\t\t];\n\t\t\t}\n\n\t\t\tif ( ! is_array( $value ) ) {\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\t// Bail if no default and template key exists.\n\t\t\tif ( ! isset( $value['default'], $value['template'] ) ) {\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\t// If a template found with default value\n\t\t\t// we should use that and break.\n\t\t\tif ( $value['default'] ) {\n\t\t\t\t$template = $value['template'];\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\n\t\t// If no defeault value is found we should use the\n\t\t// first template in the array.\n\t\tif ( is_array( $template ) && count( $template ) > 0 ) {\n\t\t\tif ( isset( $template[0]['template'] ) ) {\n\t\t\t\t$template = $template[0]['template'];\n\t\t\t}\n\t\t}\n\t}\n\n\treturn papi_get_template_file_name( $template );\n}\n\n/**\n * Check if given string is a entry type.\n *\n * @param  string $str\n *\n * @return bool\n */\nfunction papi_is_entry_type( $str = '' ) {\n\t$id = papi_get_entry_type_id();\n\n\treturn empty( $str ) ? ! empty( $id ) : $str === $id;\n}\n"
  },
  {
    "path": "src/lib/types/page.php",
    "content": "<?php\n\n/**\n * Check if the page type should be displayed or not.\n *\n * @param  string|object $page_type\n *\n * @return bool\n */\nfunction papi_display_page_type( $page_type ) {\n\t$post_type = papi_get_post_type();\n\n\tif ( empty( $post_type ) ) {\n\t\treturn false;\n\t}\n\n\tif ( is_string( $page_type ) ) {\n\t\t$page_type = papi_get_entry_type_by_id( $page_type );\n\t}\n\n\tif ( ! is_object( $page_type ) ) {\n\t\treturn false;\n\t}\n\n\tif ( ! in_array( $post_type, $page_type->post_type, true ) ) {\n\t\treturn false;\n\t}\n\n\t$display = $page_type->display( $post_type );\n\n\tif ( ! is_bool( $display ) || $display === false ) {\n\t\treturn false;\n\t}\n\n\tif ( preg_match( '/papi\\-standard\\-\\w+\\-type/', $page_type->get_id() ) ) {\n\t\treturn true;\n\t}\n\n\t$parent_page_type = papi_get_entry_type_by_meta_id( papi_get_parent_post_id() );\n\n\tif ( $parent_page_type instanceof Papi_Page_Type ) {\n\t\t$child_types = $parent_page_type->get_child_types();\n\n\t\tif ( ! empty( $child_types ) ) {\n\t\t\treturn in_array( $page_type, $parent_page_type->get_child_types(), true );\n\t\t}\n\t}\n\n\t// Run show page type filter.\n\treturn papi_filter_settings_show_page_type( $post_type, $page_type );\n}\n\n/**\n * Get all page types based on a post type.\n *\n * @param  string $post_type\n *\n * @return array\n */\nfunction papi_get_all_page_types( $post_type = '' ) {\n\t$page_types = papi_get_all_entry_types( [\n\t\t'args'  => $post_type,\n\t\t'mode'  => 'include',\n\t\t'types' => ['attachment', 'page']\n\t] );\n\n\tif ( is_array( $page_types ) ) {\n\t\tusort( $page_types, function ( $a, $b ) {\n\t\t\treturn strcmp( $a->name, $b->name );\n\t\t} );\n\t}\n\n\treturn papi_sort_order( array_reverse( $page_types ) );\n}\n\n/**\n * Get page type id by post id.\n *\n * @param  int $post_id\n *\n * @return string\n */\nfunction papi_get_page_type_id( $post_id = 0 ) {\n\treturn papi_get_entry_type_id( $post_id, 'post' );\n}\n\n/**\n * Get the page type key that is used for each post.\n *\n * @param  string $suffix\n *\n * @return string\n */\nfunction papi_get_page_type_key( $suffix = '' ) {\n\t$key    = defined( 'PAPI_PAGE_TYPE_KEY' ) ? PAPI_PAGE_TYPE_KEY : '_papi_page_type';\n\t$suffix = ltrim( $suffix, '_' );\n\n\treturn empty( $suffix ) ? $key : $key . '_' . $suffix;\n}\n\n/**\n * Get the page type name.\n *\n * @param  int $post_id\n *\n * @return string\n */\nfunction papi_get_page_type_name( $post_id = 0 ) {\n\t$post_id = papi_get_post_id( $post_id );\n\n\tif ( empty( $post_id ) ) {\n\t\treturn '';\n\t}\n\n\t$entry_type_id = papi_get_page_type_id( $post_id );\n\n\tif ( empty( $entry_type_id ) ) {\n\t\treturn '';\n\t}\n\n\t$entry_type = papi_get_entry_type_by_id( $entry_type_id );\n\n\tif ( empty( $entry_type ) ) {\n\t\treturn '';\n\t}\n\n\treturn $entry_type->name;\n}\n\n/**\n * Get all post types Papi should work with.\n *\n * @return array\n */\nfunction papi_get_post_types() {\n\t$post_types = [];\n\t$page_types = papi_get_all_entry_types( [\n\t\t'types' => ['attachment', 'page']\n\t] );\n\n\tforeach ( $page_types as $page_type ) {\n\t\t$post_types = array_merge( $post_types, papi_to_array( $page_type->post_type ) );\n\t}\n\n\treturn array_unique( $post_types );\n}\n\n/**\n * Get standard page type since it's not a real page type class.\n *\n * @return null|Papi_Page_Type\n */\nfunction papi_get_standard_page_type( $post_type ) {\n\tif ( ! is_string( $post_type ) ) {\n\t\treturn;\n\t}\n\n\t// Create a new page type and set required fields.\n\t$standard_type              = new Papi_Page_Type();\n\t$standard_type->name        = papi_filter_settings_standard_page_type_name( $post_type );\n\t$standard_type->description = papi_filter_settings_standard_page_type_description( $post_type );\n\t$standard_type->thumbnail   = papi_filter_settings_standard_page_type_thumbnail( $post_type );\n\t$standard_type->post_type   = [$post_type];\n\n\t// Set standard page type id.\n\t$standard_type->set_id( papi_get_standard_page_type_id( $post_type ) );\n\n\treturn $standard_type;\n}\n\n/**\n * Get standard page type id.\n *\n * @param  string $post_type\n *\n * @return string\n */\nfunction papi_get_standard_page_type_id( $post_type ) {\n\t$post_type = is_string( $post_type ) ? $post_type : '';\n\treturn sprintf( 'papi-standard-%s-type', $post_type );\n}\n\n/**\n * Check if given string is a page type.\n *\n * @param  string $str\n *\n * @return bool\n */\nfunction papi_is_page_type( $str = '' ) {\n\treturn papi_is_entry_type( $str );\n}\n\n/**\n * Load the entry type id on a post types.\n *\n * @param  string $entry_type_id\n * @param  string $type\n * @param  int $post_id\n *\n * @return string\n */\nfunction papi_load_page_type_id( $entry_type_id = '', $type = 'post', $post_id = null ) {\n\t$type = papi_get_meta_type( $type );\n\n\tif ( $type !== 'post' ) {\n\t\treturn $entry_type_id;\n\t}\n\n\t$post_id   = papi_get_post_id( $post_id );\n\t$key       = papi_get_page_type_key();\n\t$post_type = papi_get_post_type( $post_id );\n\n\t// Try to load the entry type id from only page type filter.\n\tif ( empty( $entry_type_id ) ) {\n\t\t$entry_type_id = papi_filter_settings_only_page_type( $post_type );\n\t}\n\n\t// If we have a post id we can load the entry type id from the post.\n\tif ( empty( $entry_type_id ) && $post_id > 0 ) {\n\t\t$meta_value    = get_post_meta( $post_id, $key, true );\n\t\t$entry_type_id = empty( $meta_value ) ? '' : $meta_value;\n\t}\n\n\t// Try to fetch the entry type id from `page_type` query string.\n\tif ( empty( $entry_type_id ) ) {\n\t\t$entry_type_id = papi_get_qs( 'page_type' );\n\t}\n\n\t// Load right entry type from the parent post id.\n\tif ( empty( $entry_type_id ) ) {\n\t\t$meta_value = get_post_meta( papi_get_parent_post_id(), $key, true );\n\t\t$entry_type_id = empty( $meta_value ) ? '' : $meta_value;\n\t}\n\n\t// Try to load the entry type from all page types and check\n\t// if only one exists of that post type.\n\t//\n\t// The same as only page type filter but without the filter.\n\tif ( empty( $entry_type_id ) ) {\n\t\t$key = sprintf( 'entry_type_id.post_type.%s', $post_type );\n\n\t\tif ( papi()->exists( $key ) ) {\n\t\t\treturn papi()->make( $key );\n\t\t}\n\n\t\t$entries = papi_get_all_page_types( $post_type );\n\n\t\tif ( count( $entries ) === 1 ) {\n\t\t\t$entry_type_id = $entries[0]->get_id();\n\n\t\t\tpapi()->bind( $key, $entry_type_id );\n\t\t}\n\t}\n\n\t// If standard page is enabled and entry type id is empty it's a standard type.\n\tif ( empty( $entry_type_id ) && papi_filter_settings_show_standard_page_type( $post_type ) ) {\n\t\treturn papi_get_standard_page_type_id( $post_type );\n\t}\n\n\treturn $entry_type_id;\n}\n\nadd_filter( 'papi/entry_type_id', 'papi_load_page_type_id', 10, 3 );\n\n/**\n * Set page type to a post.\n *\n * @param  mixed  $post_id\n * @param  string $page_type\n *\n * @return bool\n */\nfunction papi_set_page_type_id( $post_id, $page_type ) {\n\tif ( papi_entry_type_exists( $page_type ) ) {\n\t\treturn update_post_meta( papi_get_post_id( $post_id ), papi_get_page_type_key(), $page_type );\n\t}\n\n\treturn false;\n}\n\n/**\n * Echo the page type name.\n *\n * @param  int $post_id\n *\n * @return string\n */\nfunction the_papi_page_type_name( $post_id = 0 ) {\n\techo esc_html( papi_get_page_type_name( $post_id ) );\n}\n"
  },
  {
    "path": "src/lib/types/taxonomy.php",
    "content": "<?php\n\n/**\n * Get taxonomy type id by term id.\n *\n * @param  int $term_id\n *\n * @return string\n */\nfunction papi_get_taxonomy_type_id( $term_id = 0 ) {\n\treturn papi_get_entry_type_id( $term_id, 'term' );\n}\n\n/**\n * Get the taxonomy type name.\n *\n * @param  int $term_id\n *\n * @return string\n */\nfunction papi_get_taxonomy_type_name( $term_id = 0 ) {\n\t$term_id = papi_get_term_id( $term_id );\n\n\tif ( empty( $term_id ) ) {\n\t\treturn '';\n\t}\n\n\t$entry_type_id = papi_get_taxonomy_type_id( $term_id );\n\n\tif ( empty( $entry_type_id ) ) {\n\t\treturn '';\n\t}\n\n\t$entry_type = papi_get_entry_type_by_id( $entry_type_id );\n\n\tif ( empty( $entry_type ) ) {\n\t\treturn '';\n\t}\n\n\treturn $entry_type->name;\n}\n\n/**\n * Load the entry type id on a taxonomy.\n *\n * @param  string $entry_type_id\n * @param  string $type\n *\n * @return string\n */\nfunction papi_load_taxonomy_type_id( $entry_type_id = '', $type = 'term' ) {\n\tif ( $type !== 'term' ) {\n\t\treturn $entry_type_id;\n\t}\n\n\t$key      = papi_get_page_type_key();\n\t$term_id  = papi_get_term_id();\n\t$taxonomy = papi_get_taxonomy( $term_id );\n\n\t// Try to load the entry type id from only taxonomy type filter.\n\tif ( empty( $entry_type_id ) ) {\n\t\t$entry_type_id = papi_filter_settings_only_taxonomy_type( $taxonomy );\n\t}\n\n\t// If we have a term id we can load the entry type id from the term.\n\tif ( empty( $entry_type_id ) && $term_id > 0 ) {\n\t\t$meta_value    = get_term_meta( $term_id, $key, true );\n\t\t$entry_type_id = empty( $meta_value ) ? '' : $meta_value;\n\t}\n\n\t// Try to load the entry type from all taxonomy types and check\n\t// if only one exists of that post type.\n\t//\n\t// The same as only taxonomy type filter but without the filter.\n\tif ( empty( $entry_type_id ) ) {\n\t\t$key = sprintf( 'entry_type_id.taxonomy.%s', $taxonomy );\n\n\t\tif ( papi()->exists( $key ) ) {\n\t\t\treturn papi()->make( $key );\n\t\t}\n\n\t\t$entries = papi_get_all_entry_types( [\n\t\t\t'args'  => $taxonomy,\n\t\t\t'mode'  => 'include',\n\t\t\t'types' => ['taxonomy']\n\t\t] );\n\n\t\tif ( is_array( $entries ) ) {\n\t\t\tusort( $entries, function ( $a, $b ) {\n\t\t\t\treturn strcmp( $a->name, $b->name );\n\t\t\t} );\n\t\t}\n\n\t\t$entries = papi_sort_order( array_reverse( $entries ) );\n\n\t\tif ( count( $entries ) === 1 ) {\n\t\t\t$entry_type_id = $entries[0]->get_id();\n\n\t\t\tpapi()->bind( $key, $entry_type_id );\n\t\t}\n\t}\n\n\treturn $entry_type_id;\n}\n\nadd_filter( 'papi/entry_type_id', 'papi_load_taxonomy_type_id', 10, 2 );\n\n/**\n * Get all taxonomies Papi should work with.\n *\n * @return array\n */\nfunction papi_get_taxonomies() {\n\t$taxonomies  = [];\n\t$entry_types = papi_get_all_entry_types( [\n\t\t'types' => 'taxonomy'\n\t] );\n\n\tforeach ( $entry_types as $entry_type ) {\n\t\t$taxonomies = array_merge( $taxonomies, papi_to_array( $entry_type->taxonomy ) );\n\t}\n\n\treturn array_filter( array_unique( $taxonomies ) );\n}\n\n/**\n * Set taxonomy type to a term.\n *\n * @param  mixed  $term_id\n * @param  string $taxonomy_type\n *\n * @return bool\n */\nfunction papi_set_taxonomy_type_id( $term_id, $taxonomy_type ) {\n\tif ( papi_entry_type_exists( $taxonomy_type ) ) {\n\t\treturn update_term_meta( papi_get_term_id( $term_id ), papi_get_page_type_key(), $taxonomy_type );\n\t}\n\n\treturn false;\n}\n\n/**\n * Echo the taxonomy type name.\n *\n * @param  int $term_id\n *\n * @return string\n */\nfunction the_papi_taxonomy_type_name( $term_id = 0 ) {\n\techo esc_html( papi_get_taxonomy_type_name( $term_id ) );\n}\n"
  },
  {
    "path": "src/papi-loader.php",
    "content": "<?php\n\n// Load Papi Container.\nrequire_once __DIR__ . '/core/class-papi-core-container.php';\n\n/**\n * Papi loader class that handle the loading\n * of the plugin.\n */\nfinal class Papi_Loader extends Papi_Core_Container {\n\n\t/**\n\t * The instance of Papi loader class.\n\t *\n\t * @var Papi_Loader\n\t */\n\tprotected static $instance;\n\n\t/**\n\t * The plugin name.\n\t *\n\t * @var string\n\t */\n\tpublic $name = 'Papi';\n\n\t/**\n\t * Papi loader instance.\n\t *\n\t * @return Papi_Loader\n\t */\n\tpublic static function instance() {\n\t\tif ( ! isset( self::$instance ) ) {\n\t\t\tself::$instance = new self;\n\t\t}\n\n\t\treturn self::$instance;\n\t}\n\n\t/**\n\t * Call function in the container.\n\t *\n\t * @param  string $name\n\t * @param  array  $arguments\n\t *\n\t * @return mixed\n\t */\n\tpublic function __call( $name, $arguments ) {\n\t\treturn $this->make( $name, $arguments );\n\t}\n\n\t/**\n\t * The constructor.\n\t */\n\tprotected function __construct() {\n\t\t$this->constants();\n\t\t$this->init();\n\n\t\t// Fires the loaded action.\n\t\t// Deprecated action. `papi/loaded` should be used instead.\n\t\tif ( has_action( 'papi/include' ) ) {\n\t\t\tdid_action( 'papi/include' ) || do_action( 'papi/include' );\n\t\t\t_deprecated_function( 'The papi/include action', '3.0.0', 'papi/loaded' );\n\t\t}\n\n\t\t// Fires the loaded action.\n\t\tdid_action( 'papi/loaded' ) || do_action( 'papi/loaded' );\n\t}\n\n\t/**\n\t * Cloning is forbidden.\n\t *\n\t * @codeCoverageIgnore\n\t */\n\tpublic function __clone() {\n\t\t_doing_it_wrong( __FUNCTION__, esc_html__( 'Cheatin&#8217; huh?', 'papi' ), '3.0' );\n\t}\n\n\t/**\n\t * Unserializing instances of this class is forbidden.\n\t *\n\t * @codeCoverageIgnore\n\t */\n\tpublic function __wakeup() {\n\t\t_doing_it_wrong( __FUNCTION__, esc_html__( 'Cheatin&#8217; huh?', 'papi' ), '3.0' );\n\t}\n\n\t/**\n\t * Bootstrap constants\n\t */\n\tprotected function constants() {\n\t\t// Path to Papi plugin directory\n\t\tif ( ! defined( 'PAPI_PLUGIN_DIR' ) ) {\n\t\t\t$mu_dir = trailingslashit( sprintf( '%s/%s/src', WPMU_PLUGIN_DIR, basename( dirname( __DIR__ ) ) ) );\n\n\t\t\tif ( is_dir( $mu_dir ) ) {\n\t\t\t\tdefine( 'PAPI_PLUGIN_DIR', $mu_dir );\n\t\t\t} else {\n\t\t\t\tdefine( 'PAPI_PLUGIN_DIR', trailingslashit( __DIR__ ) );\n\t\t\t}\n\t\t}\n\n\t\t// URL to Papi plugin directory\n\t\tif ( ! defined( 'PAPI_PLUGIN_URL' ) ) {\n\t\t\t$plugin_url = plugin_dir_url( __FILE__ );\n\n\t\t\tif ( is_ssl() ) {\n\t\t\t\t$plugin_url = str_replace( 'http://', 'https://', $plugin_url );\n\t\t\t}\n\n\t\t\tdefine( 'PAPI_PLUGIN_URL', $plugin_url );\n\t\t}\n\n\t\t// The meta key that page type value is using\n\t\t$this->define( 'PAPI_PAGE_TYPE_KEY', '_papi_page_type' );\n\n\t\t// The plugin basename that is used in actions to match so right plugin is modified.\n\t\t$this->define( 'PAPI_PLUGIN_BASENAME', basename( dirname( __DIR__ ) ) . '/papi-loader.php' );\n\t}\n\n\t/**\n\t * Define constant if not already set.\n\t *\n\t * @param  string      $name\n\t * @param  string|bool $value\n\t */\n\tprotected function define( $name, $value ) {\n\t\tif ( ! defined( $name ) ) {\n\t\t\tdefine( $name, $value );\n\t\t}\n\t}\n\n\t/**\n\t * Initialise Papi.\n\t */\n\tprotected function init() {\n\t\t// Fires the before init action.\n\t\tdid_action( 'papi/before_init' ) || do_action( 'papi/before_init' );\n\n\t\t// Set up localisation.\n\t\t$this->load_textdomain();\n\n\t\t// Load all required files.\n\t\t$this->require_files();\n\n\t\t// Setup the container.\n\t\t$this->setup_container();\n\n\t\t// Fires the init action.\n\t\tdid_action( 'papi/init' ) || do_action( 'papi/init' );\n\t}\n\n\t/**\n\t * Load Localisation files.\n\t *\n\t * Locales found in:\n\t * - WP_LANG_DIR/papi/papi-LOCALE.mo\n\t * - WP_CONTENT_DIR/[mu-]plugins/papi/languages/papi-LOCALE.mo\n\t */\n\tprotected function load_textdomain() {\n\t\t$locale = function_exists( 'get_user_local' ) ? get_user_local() : get_locale();\n\t\t$locale = apply_filters( 'plugin_locale', $locale, 'papi' );\n\n\t\tload_textdomain( 'papi', WP_LANG_DIR . '/papi/papi-' . $locale . '.mo' );\n\t\tload_textdomain( 'papi', PAPI_PLUGIN_DIR . '../languages/papi-' . $locale . '.mo' );\n\t}\n\n\t/**\n\t * Require files.\n\t */\n\tprotected function require_files() {\n\t\t// Require the autoload class.\n\t\trequire_once __DIR__ . '/core/class-papi-core-autoload.php';\n\n\t\t$lib_path     = __DIR__ . '/lib/';\n\t\t$lib_includes = [\n\t\t\t'core/cache.php',\n\t\t\t'core/conditional.php',\n\t\t\t'core/data.php',\n\t\t\t'core/deprecated.php',\n\t\t\t'core/meta.php',\n\t\t\t'core/store.php',\n\t\t\t'core/post.php',\n\t\t\t'core/io.php',\n\t\t\t'core/property.php',\n\t\t\t'core/slug.php',\n\t\t\t'core/tabs.php',\n\t\t\t'core/taxonomy.php',\n\t\t\t'core/template.php',\n\t\t\t'core/url.php',\n\t\t\t'core/utilities.php',\n\t\t\t'hooks/actions.php',\n\t\t\t'hooks/filters.php',\n\t\t\t'hooks/filters-page-type.php',\n\t\t\t'hooks/filters-taxonomy-type.php',\n\t\t\t'fields/page.php',\n\t\t\t'fields/option.php',\n\t\t\t'fields/taxonomy.php',\n\t\t\t'types/entry.php',\n\t\t\t'types/page.php',\n\t\t\t'types/option.php',\n\t\t\t'types/taxonomy.php'\n\t\t];\n\n\t\t// Require function files.\n\t\tforeach ( $lib_includes as $file ) {\n\t\t\tif ( file_exists( $lib_path . $file ) ) {\n\t\t\t\trequire_once $lib_path . $file;\n\t\t\t}\n\t\t}\n\n\t\tunset( $file );\n\n\t\t// Require admin classes that should not be loaded by the autoload.\n\t\trequire_once __DIR__ . '/admin/class-papi-admin.php';\n\t\trequire_once __DIR__ . '/admin/class-papi-admin-ajax.php';\n\t\trequire_once __DIR__ . '/admin/class-papi-admin-assets.php';\n\t\trequire_once __DIR__ . '/admin/class-papi-admin-menu.php';\n\n\t\t// Require conditional rules that should not be loaded by the autoload.\n\t\trequire_once __DIR__ . '/core/class-papi-core-conditional-rules.php';\n\n\t\t// Require REST API classes.\n\t\trequire_once __DIR__ . '/rest-api/class-papi-rest-api.php';\n\n\t\t// Load Papi CLI class if WP CLI is used.\n\t\tif ( defined( 'WP_CLI' ) && WP_CLI ) {\n\t\t\trequire_once __DIR__ . '/cli/class-papi-cli.php';\n\t\t}\n\t}\n\n\t/**\n\t * Deactivate Papi if the WordPress version is lower then 4.6.\n\t */\n\tpublic static function deactivate() {\n\t\t// Remove Papi from plugins_loaded action.\n\t\tremove_action( 'plugins_loaded', 'papi' );\n\n\t\t// Load deactivate_plugins if it don't exists.\n\t\tif ( ! function_exists( 'deactivate_plugins' ) ) {\n\t\t\tinclude_once ABSPATH . 'wp-admin/includes/plugin.php';\n\t\t}\n\n\t\tdeactivate_plugins( PAPI_PLUGIN_BASENAME );\n\n\t\twp_die( esc_html__( 'WordPress 4.6 and higher required to run Papi! The plugin has now disabled itself.', 'papi' ) );\n\n\t\t// Remove instance.\n\t\tself::$instance = null;\n\t}\n\n\t/**\n\t * Reset container.\n\t */\n\tpublic function reset() {\n\t\tparent::reset();\n\n\t\t$this->setup_container();\n\t}\n\n\t/**\n\t * Setup container.\n\t */\n\tprotected function setup_container() {\n\t\t$this->singleton( 'data', function ( $type = 'post' ) {\n\t\t\treturn new Papi_Core_Data( $type );\n\t\t} );\n\t}\n}\n\n/**\n * Return the instance of Papi to everyone.\n *\n * @return Papi_Loader\n */\nfunction papi() {\n\tif ( version_compare( get_bloginfo( 'version' ), '4.6', '<' ) ) {\n\t\tPapi_Loader::deactivate();\n\t}\n\n\treturn Papi_Loader::instance();\n}\n\nadd_action( 'plugins_loaded', 'papi' );\n"
  },
  {
    "path": "src/properties/class-papi-property-bool.php",
    "content": "<?php\n\n/**\n * Bool (true/false) property.\n */\nclass Papi_Property_Bool extends Papi_Property {\n\n\t/**\n\t * The convert type.\n\t *\n\t * @var string\n\t */\n\tpublic $convert_type = 'bool';\n\n\t/**\n\t * The default value.\n\t *\n\t * @var bool\n\t */\n\tpublic $default_value = false;\n\n\t/**\n\t * Format the value of the property before it's returned\n\t * to WordPress admin or the site.\n\t *\n\t * @param  mixed  $value\n\t * @param  string $slug\n\t * @param  int    $post_id\n\t *\n\t * @return bool\n\t */\n\tpublic function format_value( $value, $slug, $post_id ) {\n\t\tif ( is_string( $value ) && $value === 'false' || $value === false ) {\n\t\t\treturn false;\n\t\t}\n\n\t\treturn  is_string( $value ) &&\n\t\t\t( $value === 'true' || $value === 'on' ) ||\n\t\t\t$value === true;\n\t}\n\n\t/**\n\t * Render property html.\n\t */\n\tpublic function html() {\n\t\t$value = $this->get_value();\n\n\t\tpapi_render_html_tag( 'input', [\n\t\t\t'type'  => 'hidden',\n\t\t\t'name'  => esc_attr( $this->html_name() ),\n\t\t\t'value' => false\n\t\t] );\n\n\t\tpapi_render_html_tag( 'input', [\n\t\t\t'checked' => ! empty( $value ),\n\t\t\t'id'      => esc_attr( $this->html_id() ),\n\t\t\t'name'    => esc_attr( $this->html_name() ),\n\t\t\t'type'    => 'checkbox'\n\t\t] );\n\t}\n\n\t/**\n\t * Change value after it's loaded from the database.\n\t *\n\t * @param  mixed  $value\n\t * @param  string $slug\n\t * @param  int    $post_id\n\t *\n\t * @return mixed\n\t */\n\tpublic function load_value( $value, $slug, $post_id ) {\n\t\treturn is_string( $value ) && $value === '1' || $value;\n\t}\n\n\t/**\n\t * Prepare property value.\n\t *\n\t * @param  mixed $value\n\t *\n\t * @return mixed\n\t */\n\tprotected function prepare_value( $value ) {\n\t\tif ( is_string( $value ) && ( $value === 'true' || $value === 'on' ) || $value === true ) {\n\t\t\treturn true;\n\t\t}\n\n\t\treturn null;\n\t}\n\n\t/**\n\t * Fix the database value on update.\n\t *\n\t * @param  mixed  $value\n\t * @param  string $slug\n\t * @param  int    $post_id\n\t *\n\t * @return array\n\t */\n\tpublic function update_value( $value, $slug, $post_id ) {\n\t\treturn $this->prepare_value( $value );\n\t}\n}\n"
  },
  {
    "path": "src/properties/class-papi-property-checkbox.php",
    "content": "<?php\n\n/**\n * HTML checkbox property that can handle one or\n * more checkboxes.\n */\nclass Papi_Property_Checkbox extends Papi_Property {\n\n\t/**\n\t * The convert type.\n\t *\n\t * @var string\n\t */\n\tpublic $convert_type = 'array';\n\n\t/**\n\t * The default value.\n\t *\n\t * @var array\n\t */\n\tpublic $default_value = [];\n\n\t/**\n\t * Format the value of the property before it's returned\n\t * to WordPress admin or the site.\n\t *\n\t * @param  mixed  $value\n\t * @param  string $slug\n\t * @param  int    $post_id\n\t *\n\t * @return array\n\t */\n\tpublic function format_value( $value, $slug, $post_id ) {\n\t\tif ( is_string( $value ) && ! papi_is_empty( $value ) ) {\n\t\t\treturn [papi_cast_string_value( $value )];\n\t\t}\n\n\t\tif ( ! is_array( $value ) ) {\n\t\t\treturn $this->default_value;\n\t\t}\n\n\t\treturn array_map( 'papi_cast_string_value', $value );\n\t}\n\n\t/**\n\t * Get default settings.\n\t *\n\t * @return array\n\t */\n\tpublic function get_default_settings() {\n\t\treturn [\n\t\t\t'items'    => [],\n\t\t\t'selected' => []\n\t\t];\n\t}\n\n\t/**\n\t * Render property html.\n\t */\n\tpublic function html() {\n\t\t$settings = $this->get_settings();\n\t\t$value    = papi_cast_string_value( $this->get_value() );\n\n\t\t// Override selected setting with\n\t\t// database value if not empty.\n\t\tif ( ! papi_is_empty( $value ) ) {\n\t\t\t$settings->selected = $value;\n\t\t}\n\n\t\t$settings->selected = papi_to_array( $settings->selected );\n\n\t\techo '<div class=\"papi-property-checkbox\">';\n\n\t\tforeach ( $settings->items as $key => $value ) {\n\t\t\t$key = is_numeric( $key ) ? $value : $key;\n\n\t\t\tpapi_render_html_tag( 'p', [\n\t\t\t\tpapi_html_tag( 'label', [\n\t\t\t\t\t'class' => 'light',\n\t\t\t\t\t'for'   => esc_attr( $this->html_id( $key ) ),\n\n\t\t\t\t\tpapi_html_tag( 'input', [\n\t\t\t\t\t\t'id'      => esc_attr( $this->html_id( $key ) ),\n\t\t\t\t\t\t'name'    => esc_attr( $this->html_name() ) . '[]',\n\t\t\t\t\t\t'type'    => 'hidden'\n\t\t\t\t\t] ),\n\n\t\t\t\t\tpapi_html_tag( 'input', [\n\t\t\t\t\t\t'checked' => in_array( $value, $settings->selected, true ),\n\t\t\t\t\t\t'id'      => esc_attr( $this->html_id( $key ) ),\n\t\t\t\t\t\t'name'    => esc_attr( $this->html_name() ) . '[]',\n\t\t\t\t\t\t'type'    => 'checkbox',\n\t\t\t\t\t\t'value'   => esc_attr( $value )\n\t\t\t\t\t] ),\n\n\t\t\t\t\tesc_html( papi_convert_to_string( $key ) )\n\t\t\t\t] )\n\t\t\t] );\n\t\t}\n\n\t\techo '</div>';\n\t}\n}\n"
  },
  {
    "path": "src/properties/class-papi-property-color.php",
    "content": "<?php\n\n/**\n * Color property that implements WordPress color\n * picker as a property.\n */\nclass Papi_Property_Color extends Papi_Property {\n\n\t/**\n\t * Get default settings.\n\t *\n\t * @return array\n\t */\n\tpublic function get_default_settings() {\n\t\treturn [\n\t\t\t'change'        => false,\n\t\t\t'clear'         => false,\n\t\t\t'default_color' => false,\n\t\t\t'hide'          => false,\n\t\t\t'mode'          => 'hsv',\n\t\t\t'palettes'      => true,\n\t\t\t'slider'        => 'horizontal',\n\t\t\t'show_input'    => false,\n\t\t\t'type'          => 'full',\n\t\t\t'width'         => 255\n\t\t];\n\t}\n\n\t/**\n\t * Render property html.\n\t */\n\tpublic function html() {\n\t\t$settings = $this->get_settings();\n\t\t$value    = $this->get_value();\n\n\t\tpapi_render_html_tag( 'div', [\n\t\t\t'class' => 'papi-property-color-picker',\n\n\t\t\tpapi_html_tag( 'input', [\n\t\t\t\t'data-settings' => $settings,\n\t\t\t\t'id'            => $this->html_id(),\n\t\t\t\t'name'          => $this->html_name(),\n\t\t\t\t'type'          => $settings->show_input === true ? 'text' : 'hidden',\n\t\t\t\t'value'         => $value,\n\t\t\t] )\n\n\t\t] );\n\t}\n}\n"
  },
  {
    "path": "src/properties/class-papi-property-datetime.php",
    "content": "<?php\n\n/**\n * Datetime property.\n */\nclass Papi_Property_Datetime extends Papi_Property {\n\n\t/**\n\t * Get default settings.\n\t *\n\t * @return array\n\t */\n\tpublic function get_default_settings() {\n\t\treturn [\n\t\t\t'format'           => 'YYYY-MM-DD hh:mm:ss',\n\t\t\t'first_day'        => 0,\n\t\t\t'show_seconds'     => false,\n\t\t\t'show_time'        => true,\n\t\t\t'show_time_first'  => false,\n\t\t\t'show_week_number' => false,\n\t\t\t'use_24_hours'     => get_locale() === 'sv_SE'\n\t\t];\n\t}\n\n\t/**\n\t * Render property html.\n\t */\n\tpublic function html() {\n\t\t$settings = $this->get_settings();\n\t\t$value    = $this->get_value();\n\n\t\t$settings_json = [\n\t\t\t'i18n' => [\n\t\t\t\t'previousMonth' => __( 'Previous Month', 'papi' ),\n\t\t\t\t'nextMonth'     => __( 'Next Month', 'papi' ),\n\t\t\t\t'midnight'      => __( 'Midnight', 'papi' ),\n\t\t\t\t'months'        => [\n\t\t\t\t\t__( 'January', 'papi' ),\n\t\t\t\t\t__( 'February', 'papi' ),\n\t\t\t\t\t__( 'March', 'papi' ),\n\t\t\t\t\t__( 'April', 'papi' ),\n\t\t\t\t\t__( 'May', 'papi' ),\n\t\t\t\t\t__( 'June', 'papi' ),\n\t\t\t\t\t__( 'July', 'papi' ),\n\t\t\t\t\t__( 'August', 'papi' ),\n\t\t\t\t\t__( 'September', 'papi' ),\n\t\t\t\t\t__( 'October', 'papi' ),\n\t\t\t\t\t__( 'November', 'papi' ),\n\t\t\t\t\t__( 'December', 'papi' )\n\t\t\t\t],\n\t\t\t\t'noon'          => __( 'Noon', 'papi' ),\n\t\t\t\t'weekdays'      => [\n\t\t\t\t\t__( 'Sunday', 'papi' ),\n\t\t\t\t\t__( 'Monday', 'papi' ),\n\t\t\t\t\t__( 'Tuesday', 'papi' ),\n\t\t\t\t\t__( 'Wednesday', 'papi' ),\n\t\t\t\t\t__( 'Thursday', 'papi' ),\n\t\t\t\t\t__( 'Friday', 'papi' ),\n\t\t\t\t\t__( 'Saturday', 'papi' )\n\t\t\t\t],\n\t\t\t\t'weekdaysShort' => [\n\t\t\t\t\t__( 'Sun', 'papi' ),\n\t\t\t\t\t__( 'Mon', 'papi' ),\n\t\t\t\t\t__( 'Tue', 'papi' ),\n\t\t\t\t\t__( 'Wed', 'papi' ),\n\t\t\t\t\t__( 'Thu', 'papi' ),\n\t\t\t\t\t__( 'Fri', 'papi' ),\n\t\t\t\t\t__( 'Sat', 'papi' )\n\t\t\t\t]\n\t\t\t]\n\t\t];\n\n\t\t// Remove i18n setting if it exists.\n\t\tif ( isset( $settings->i18n ) ) {\n\t\t\tunset( $settings->i18n );\n\t\t}\n\n\t\t// Remove default time format if show time is false.\n\t\tif ( isset( $settings->show_time ) && ! $settings->show_time && isset( $settings->format ) ) {\n\t\t\t$settings->format = trim( str_replace( 'hh:mm:ss', '', $settings->format ) );\n\t\t}\n\n\t\t// Convert all sneak case key to camel case.\n\t\tforeach ( (array) $settings as $key => $val ) {\n\t\t\tif ( ! is_string( $key ) ) {\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tif ( $key = papi_camel_case( $key ) ) {\n\t\t\t\t$settings_json[$key] = $val;\n\t\t\t}\n\t\t}\n\n\t\t// Papi has `use24Hours` as key and Pikaday has `use24hour`.\n\t\t// This code will fix it.\n\t\tif ( isset( $settings_json['use24Hours'] ) ) {\n\t\t\t$settings_json['use24hour'] = $settings_json['use24Hours'];\n\t\t\tunset( $settings_json['use24Hours'] );\n\t\t}\n\n\t\tpapi_render_html_tag( 'input', [\n\t\t\t'class'         => 'papi-property-datetime',\n\t\t\t'data-settings' => (object) $settings_json,\n\t\t\t'id'            => $this->html_id(),\n\t\t\t'name'          => $this->html_name(),\n\t\t\t'type'          => 'text',\n\t\t\t'value'         => $value\n\t\t] );\n\t}\n}\n"
  },
  {
    "path": "src/properties/class-papi-property-divider.php",
    "content": "<?php\n\n/**\n * Divider property.\n */\nclass Papi_Property_Divider extends Papi_Property {\n\n\t/**\n\t * Determine if the property require a slug or not.\n\t *\n\t * @var bool\n\t */\n\tprotected $slug_required = false;\n\n\t/**\n\t * Render property html.\n\t */\n\tpublic function html() {\n\t\t$options = $this->get_options();\n\t\t$title   = '';\n\t\t$text    = '';\n\n\t\tif ( ! papi_is_empty( $options->title ) ) {\n\t\t\t$title = sprintf( '<h3><span>%s</span></h3>', esc_html( $options->title ) );\n\t\t}\n\n\t\tif ( ! papi_is_empty( $options->description ) ) {\n\t\t\t$text = sprintf( '<p>%s</p>', $options->description );\n\t\t}\n\n\t\tpapi_render_html_tag( 'div', [\n\t\t\t'class'          => 'papi-property-divider',\n\t\t\t'data-papi-rule' => esc_attr( $this->html_name() ),\n\t\t\tsprintf( '%s%s', $title, $text )\n\t\t] );\n\t}\n\n\t/**\n\t * Render the final html that is displayed in the table.\n\t */\n\tprotected function render_row_html() {\n\t\tif ( $this->get_option( 'raw' ) ) {\n\t\t\tparent::render_row_html();\n\t\t} else {\n\t\t\t?>\n\t\t\t<tr class=\"<?php echo $this->display ? '' : 'papi-hide'; ?>\">\n\t\t\t\t<td colspan=\"2\">\n\t\t\t\t\t<?php $this->render_property_html(); ?>\n\t\t\t\t</td>\n\t\t\t</tr>\n\t\t\t<?php\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "src/properties/class-papi-property-dropdown.php",
    "content": "<?php\n\n/**\n * Dropdown property.\n */\nclass Papi_Property_Dropdown extends Papi_Property {\n\n\t/**\n\t * The convert type.\n\t *\n\t * @var string\n\t */\n\tpublic $convert_type = 'mixed';\n\n\t/**\n\t * Format the value of the property before it's returned\n\t * to WordPress admin or the site.\n\t *\n\t * @param  mixed  $value\n\t * @param  string $slug\n\t * @param  int    $post_id\n\t *\n\t * @return mixed\n\t */\n\tpublic function format_value( $value, $slug, $post_id ) {\n\t\tif ( ! $this->get_setting( 'multiple' ) ) {\n\t\t\treturn $this->is_string_items() ? $value : papi_cast_string_value( $value );\n\t\t}\n\n\t\t$value = is_array( $value ) ? $value : [];\n\n\t\tif ( ! $this->is_string_items() ) {\n\t\t\t$value = array_map( 'papi_cast_string_value', $value );\n\t\t}\n\n\t\treturn $value;\n\t}\n\n\t/**\n\t * Determine if items are strings or not.\n\t *\n\t * @return bool\n\t */\n\tpublic function is_string_items() {\n\t\t$items = $this->get_items();\n\n\t\tif ( empty( $items ) ) {\n\t\t\treturn false;\n\t\t}\n\n\t\t$items = array_values( $items );\n\n\t\treturn is_string( $items[0] );\n\t}\n\n\t/**\n\t * Get default settings.\n\t *\n\t * @return array\n\t */\n\tpublic function get_default_settings() {\n\t\treturn [\n\t\t\t'allow_clear' => true,\n\t\t\t'placeholder' => null,\n\t\t\t'items'       => [],\n\t\t\t'multiple'    => false,\n\t\t\t'selected'    => [],\n\t\t\t'select2'     => true\n\t\t];\n\t}\n\n\t/**\n\t * Get dropdown items.\n\t *\n\t * @return array\n\t */\n\tprotected function get_items() {\n\t\treturn papi_to_array( $this->get_setting( 'items', [] ) );\n\t}\n\n\t/**\n\t * Render property html.\n\t */\n\tpublic function html() {\n\t\t// Setup variables needed.\n\t\t$settings     = $this->get_settings();\n\t\t$value        = $this->get_value();\n\t\t$options_html = [];\n\n\t\t// Properties that extends dropdown property\n\t\t// maybe don't have this setting.\n\t\tif ( ! isset( $settings->selected ) ) {\n\t\t\t$settings->selected = [];\n\t\t}\n\n\t\t// Properties that extends dropdown property\n\t\t// maybe don't have this setting.\n\t\tif ( ! isset( $settings->multiple ) ) {\n\t\t\t$settings->multiple = false;\n\t\t}\n\n\t\t// Override selected setting with\n\t\t// database value if not empty.\n\t\tif ( ! papi_is_empty( $value ) ) {\n\t\t\t$settings->selected = $value;\n\t\t}\n\n\t\t$placeholder = ! is_null( $settings->placeholder ) ? $settings->placeholder : '';\n\t\t$placeholder = papi_is_empty( $placeholder ) ? '&nbsp;' : $placeholder;\n\n\t\t// Add placeholder if any.\n\t\tif ( ! is_null( $settings->placeholder ) ) {\n\t\t\t$options_html[] = sprintf( '<option value=\"%s\">%s</option>', esc_attr( $this->get_option( 'default', '' ) ), esc_html( $placeholder ) );\n\t\t}\n\n\t\t$classes = 'papi-fullwidth';\n\n\t\tif ( $settings->select2 ) {\n\t\t\t$classes .= ' papi-component-select2';\n\t\t}\n\n\t\t// Create option html tags for all items.\n\t\tforeach ( $this->get_items() as $key => $value ) {\n\t\t\t$key = is_numeric( $key ) ? $value : $key;\n\n\t\t\tif ( papi_is_empty( $key ) ) {\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\t// When a multiple we need to check with\n\t\t\t// `in_array` since the value is a array.\n\t\t\tif ( $settings->multiple ) {\n\t\t\t\t$selected = in_array( $value, $settings->selected, true );\n\t\t\t} else {\n\t\t\t\t$selected = papi_convert_to_string( $value ) === papi_convert_to_string( $settings->selected );\n\t\t\t}\n\n\t\t\t$options_html[] = papi_html_tag( 'option', [\n\t\t\t\t'data-edit-url' => false,\n\t\t\t\t'data-new-url'  => false,\n\t\t\t\t'selected'      => $selected ? 'selected' : null,\n\t\t\t\t'value'         => $value,\n\t\t\t\tesc_html( papi_convert_to_string( $key ) )\n\t\t\t] );\n\t\t}\n\n\t\t// When selectize is empty this will be used.\n\t\tpapi_render_html_tag( 'input', [\n\t\t\t'name' => $this->html_name() . ( $settings->multiple ? '[]' : '' ),\n\t\t\t'type' => 'hidden'\n\t\t] );\n\n\t\tpapi_render_html_tag( 'select', [\n\t\t\t'class'            => $classes,\n\t\t\t'data-allow-clear' => $settings->allow_clear,\n\t\t\t'data-placeholder' => $placeholder,\n\t\t\t'data-width'       => '100%',\n\t\t\t'id'               => $this->html_id() . ( $settings->multiple ? '[]' : '' ),\n\t\t\t'multiple'         => $settings->multiple ? 'multiple' : null,\n\t\t\t'name'             => $this->html_name() . ( $settings->multiple ? '[]' : '' ),\n\t\t\t$options_html\n\t\t] );\n\t}\n\n\t/**\n\t * Change value after it's loaded from the database.\n\t *\n\t * @param  mixed  $value\n\t * @param  string $slug\n\t * @param  int    $post_id\n\t *\n\t * @return mixed\n\t */\n\tpublic function load_value( $value, $slug, $post_id ) {\n\t\t$value = maybe_unserialize( $value );\n\n\t\treturn papi_maybe_json_decode( $value, $this->get_setting( 'multiple' ) );\n\t}\n\n\t/**\n\t * Update value before it's saved to the database.\n\t *\n\t * @param  mixed  $value\n\t * @param  string $slug\n\t * @param  int    $post_id\n\t *\n\t * @return mixed\n\t */\n\tpublic function update_value( $value, $slug, $post_id ) {\n\t\tif ( ! ( $value = $this->prepare_value( $value ) ) ) {\n\t\t\treturn;\n\t\t}\n\n\t\treturn papi_maybe_json_encode( $value );\n\t}\n}\n"
  },
  {
    "path": "src/properties/class-papi-property-editor.php",
    "content": "<?php\n\n/**\n * WordPress editor property.\n */\nclass Papi_Property_Editor extends Papi_Property {\n\n\t/**\n\t * Add filters to filter TinyMCE buttons.\n\t */\n\tprotected function add_mce_buttons() {\n\t\tfor ( $i = 0; $i < 4; $i++ ) {\n\t\t\t$num = $i === 0 ? '' : '_' . ( $i + 1 );\n\t\t\tadd_filter( 'mce_buttons' . $num, [$this, 'mce_buttons'] );\n\t\t}\n\t}\n\n\t/**\n\t * Format the value of the property before it's returned\n\t * to WordPress admin or the site.\n\t *\n\t * @param  mixed  $value\n\t * @param  string $slug\n\t * @param  int    $post_id\n\t *\n\t * @return array\n\t */\n\tpublic function format_value( $value, $slug, $post_id ) {\n\t\treturn papi_is_admin() ? $value : apply_filters( 'the_content', $value );\n\t}\n\n\t/**\n\t * Get default settings.\n\t *\n\t * @return array\n\t */\n\tpublic function get_default_settings() {\n\t\treturn [\n\t\t\t'mce_buttons'      => [],\n\t\t\t'mce_buttons_2'    => [],\n\t\t\t'mce_buttons_3'    => [],\n\t\t\t'mce_buttons_4'    => [],\n\t\t\t'media_buttons'    => true,\n\t\t\t'teeny'            => false,\n\t\t\t'drag_drop_upload' => true\n\t\t];\n\t}\n\n\t/**\n\t * Filter TinyMCE buttons (Visual tab).\n\t *\n\t * @param  array $buttons\n\t *\n\t * @return array\n\t */\n\tpublic function mce_buttons( array $buttons = [] ) {\n\t\tif ( $new = papi_to_array( $this->get_setting( current_filter(), [] ) ) ) {\n\t\t\treturn $new;\n\t\t}\n\n\t\treturn $buttons;\n\t}\n\n\t/**\n\t * Render property html.\n\t */\n\tpublic function html() {\n\t\t$value = $this->get_value();\n\t\t$value = html_entity_decode( $value );\n\t\t$id    = str_replace(\n\t\t\t'[',\n\t\t\t'',\n\t\t\tstr_replace( ']', '', $this->html_name() )\n\t\t) . '-' . uniqid();\n\n\t\t// Add `mce_buttons` filters.\n\t\t$this->add_mce_buttons();\n\n\t\twp_editor( $value, $id, [\n\t\t\t'textarea_name'    => esc_attr( $this->html_name() ),\n\t\t\t'media_buttons'    => $this->get_setting( 'media_buttons', true ),\n\t\t\t'teeny'            => $this->get_setting( 'teeny', false ),\n\t\t\t'drag_drop_upload' => $this->get_setting( 'drag_drop_upload', true ),\n\t\t] );\n\n\t\t// Remove `mce_buttons` filters.\n\t\t$this->reove_mce_buttons();\n\n\t\tif ( defined( 'DOING_AJAX' ) && DOING_AJAX ) {\n\t\t\tadd_filter( 'mce_external_plugins', '__return_empty_array' );\n\t\t}\n\t}\n\n\t/**\n\t * Remove filters that filter TinyMCE buttons.\n\t */\n\tprotected function reove_mce_buttons() {\n\t\tfor ( $i = 0; $i < 4; $i++ ) {\n\t\t\t$num = $i === 0 ? '' : '_' . ( $i + 1 );\n\t\t\tremove_filter( 'mce_buttons' . $num, [$this, 'mce_buttons'] );\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "src/properties/class-papi-property-email.php",
    "content": "<?php\n\n/**\n * HTML5 email input property.\n */\nclass Papi_Property_Email extends Papi_Property_String {\n\n\t/**\n\t * The input type to use.\n\t *\n\t * @var string\n\t */\n\tpublic $input_type = 'email';\n\n}\n"
  },
  {
    "path": "src/properties/class-papi-property-file.php",
    "content": "<?php\n\n/**\n * WordPress media file property.\n */\nclass Papi_Property_File extends Papi_Property {\n\n\t/**\n\t * The convert type.\n\t *\n\t * @var string\n\t */\n\tpublic $convert_type = 'object';\n\n\t/**\n\t * The default value.\n\t *\n\t * @var array\n\t */\n\tpublic $default_value = [];\n\n\t/**\n\t * File type.\n\t *\n\t * @var string\n\t */\n\tprotected $file_type = 'file';\n\n\t/**\n\t * Format the value of the property before it's returned\n\t * to WordPress admin or the site.\n\t *\n\t * @param mixed  $value\n\t * @param string $slug\n\t * @param int    $post_id\n\t *\n\t * @return mixed\n\t */\n\tpublic function format_value( $value, $slug, $post_id ) {\n\t\tif ( ! is_array( $value ) ) {\n\t\t\t$value = $this->get_file( $value );\n\t\t}\n\n\t\t// Single file.\n\t\tif ( is_numeric( $value ) ) {\n\t\t\t$meta = wp_get_attachment_metadata( $value );\n\n\t\t\tif ( $meta === false ) {\n\t\t\t\t$meta = [];\n\t\t\t}\n\n\t\t\t$alt = get_post_meta( $value, '_wp_attachment_image_alt', true );\n\n\t\t\t$att  = get_post( $value );\n\n\t\t\tif ( is_null( $att ) ) {\n\t\t\t\treturn (int) $value;\n\t\t\t}\n\n\t\t\t$mine = [\n\t\t\t\t'alt'         => trim( wp_strip_all_tags( $alt ? $alt : '' ) ),\n\t\t\t\t'caption'     => trim( wp_strip_all_tags( isset( $att->post_excerpt ) ? $att->post_excerpt : '' ) ),\n\t\t\t\t'description' => trim( wp_strip_all_tags( isset( $att->post_content ) ? $att->post_content : '' ) ),\n\t\t\t\t'id'          => intval( $value ),\n\t\t\t\t'is_image'    => (bool) wp_attachment_is_image( $value ),\n\t\t\t\t'title'       => esc_html( isset( $att->post_title ) ? $att->post_title : '' ),\n\t\t\t\t'url'         => wp_get_attachment_url( $value ),\n\t\t\t];\n\n\t\t\tif ( isset( $meta['sizes'] ) ) {\n\t\t\t\tforeach ( array_keys( $meta['sizes'] ) as $size ) {\n\t\t\t\t\t$src = wp_get_attachment_image_src( $mine['id'], $size );\n\t\t\t\t\tif ( $src ) {\n\t\t\t\t\t\t$meta['sizes'][ $size ]['url'] = $src[0];\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t$value = (object) array_merge( $meta, $mine );\n\n\t\t\tif ( ! papi_is_admin() && $this->get_setting( 'fields' ) === 'ids' ) {\n\t\t\t\treturn $att->ID;\n\t\t\t}\n\n\t\t\treturn $value;\n\t\t}\n\n\t\t// Multiple files.\n\t\tif ( is_array( $value ) ) {\n\t\t\tforeach ( $value as $k => $v ) {\n\t\t\t\t$value[ $k ] = $this->format_value( $v, $slug, $post_id );\n\t\t\t}\n\n\t\t\treturn $value;\n\t\t}\n\n\t\tif ( is_object( $value ) && ! isset( $value->url ) ) {\n\t\t\treturn;\n\t\t}\n\n\t\treturn $value;\n\t}\n\n\t/**\n\t * Get file by id or meta value.\n\t *\n\t * @param mixed $value\n\t *\n\t * @return int\n\t */\n\tpublic function get_file( $value ) {\n\t\t$meta_key = $this->get_setting( 'meta_key' );\n\n\t\tif ( empty( $meta_key ) ) {\n\t\t\tif ( is_numeric( $value ) && intval( $value ) !== 0 ) {\n\t\t\t\t$post = get_post( $value );\n\t\t\t}\n\t\t} else if ( ! empty( $value ) ) {\n\t\t\t$args = [\n\t\t\t\t'fields'         => 'ids',\n\t\t\t\t'meta_key'       => $meta_key,\n\t\t\t\t'meta_value'     => $value,\n\t\t\t\t'posts_per_page' => 1,\n\t\t\t\t'post_type'      => 'attachment',\n\t\t\t\t'post_status'    => 'any',\n\t\t\t];\n\n\t\t\t$query = new WP_Query( $args );\n\n\t\t\tif ( ! empty( $query->posts ) ) {\n\t\t\t\t$post = get_post( $query->posts[0] );\n\t\t\t}\n\t\t}\n\n\t\tif ( empty( $post ) ) {\n\t\t\treturn $value;\n\t\t}\n\n\t\treturn $post->ID;\n\t}\n\n\t/**\n\t * Get matching value based on key from a file.\n\t *\n\t * @param mixed $value\n\t *\n\t * @return mixed\n\t */\n\tprotected function get_file_value( $value ) {\n\t\t$meta_key = $this->get_setting( 'meta_key' );\n\n\t\tif ( ! is_object( $value ) ) {\n\t\t\treturn 0;\n\t\t}\n\n\t\tif ( empty( $meta_key ) ) {\n\t\t\treturn $value->id;\n\t\t}\n\n\t\tif ( $value = get_post_meta( $value->id, $meta_key, true ) ) {\n\t\t\treturn $value;\n\t\t}\n\n\t\treturn 0;\n\t}\n\n\t/**\n\t * Get default settings.\n\t *\n\t * @return array\n\t */\n\tpublic function get_default_settings() {\n\t\treturn [\n\t\t\t'multiple' => false,\n\t\t\t'meta_key' => '',\n\t\t];\n\t}\n\n\t/**\n\t * Render property html.\n\t */\n\tpublic function html() {\n\t\t$css_classes = '';\n\t\t$labels      = $this->get_labels();\n\t\t$settings    = $this->get_settings();\n\t\t$slug        = $this->html_name();\n\t\t$value       = papi_to_array( $this->get_value() );\n\n\t\t// Keep only valid objects.\n\t\t$value = array_filter( $value, function ( $item ) {\n\t\t\treturn is_object( $item ) && isset( $item->id ) && ! empty( $item->id );\n\t\t} );\n\n\t\t$show_button = empty( $value );\n\n\t\tif ( $settings->multiple ) {\n\t\t\t$css_classes .= ' multiple ';\n\t\t\t$slug        .= '[]';\n\t\t\t$show_button = true;\n\t\t}\n\t\t?>\n\n\t\t<div class=\"papi-property-file <?php echo esc_attr( $css_classes ); ?>\" data-file-type=\"<?php echo esc_attr( $this->file_type ); ?>\">\n\t\t\t<p class=\"papi-file-select <?php echo $show_button ? '' : 'papi-hide'; ?>\">\n\t\t\t\t<?php\n\t\t\t\tif ( ! $settings->multiple ) {\n\t\t\t\t\techo esc_html( $labels['no_file'] ) . '&nbsp;';\n\t\t\t\t}\n\n\t\t\t\tpapi_render_html_tag( 'input', [\n\t\t\t\t\t'name'  => esc_attr( $slug ),\n\t\t\t\t\t'type'  => 'hidden',\n\t\t\t\t\t'value' => '',\n\t\t\t\t] );\n\n\t\t\t\tpapi_render_html_tag( 'button', [\n\t\t\t\t\t'data-slug' => esc_attr( $slug ),\n\t\t\t\t\t'class'     => 'button',\n\t\t\t\t\t'type'      => 'button',\n\n\t\t\t\t\tesc_html( $labels['add'] ),\n\t\t\t\t] );\n\t\t\t\t?>\n\t\t\t</p>\n\t\t\t<div class=\"attachments\">\n\t\t\t\t<?php\n\t\t\t\tif ( is_array( $value ) ):\n\t\t\t\t\tforeach ( $value as $file ):\n\t\t\t\t\t\tif ( ! is_object( $file ) ) {\n\t\t\t\t\t\t\tcontinue;\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\t$url = wp_get_attachment_thumb_url( $file->id );\n\n\t\t\t\t\t\tif ( empty( $url ) ) {\n\t\t\t\t\t\t\t$url = wp_mime_type_icon( $file->id );\n\t\t\t\t\t\t}\n\t\t\t\t\t\t?>\n\t\t\t\t\t\t<div class=\"attachment\">\n\t\t\t\t\t\t\t<a class=\"check\" href=\"#\">&times;</a>\n\t\t\t\t\t\t\t<div class=\"attachment-preview\">\n\t\t\t\t\t\t\t\t<div class=\"thumbnail\">\n\t\t\t\t\t\t\t\t\t<div class=\"centered\">\n\t\t\t\t\t\t\t\t\t\t<?php\n\t\t\t\t\t\t\t\t\t\tpapi_render_html_tag( 'img', [\n\t\t\t\t\t\t\t\t\t\t\t'alt' => esc_attr( $file->alt ),\n\t\t\t\t\t\t\t\t\t\t\t'src' => esc_attr( $url ),\n\t\t\t\t\t\t\t\t\t\t] );\n\n\t\t\t\t\t\t\t\t\t\tpapi_render_html_tag( 'input', [\n\t\t\t\t\t\t\t\t\t\t\t'name'  => esc_attr( $slug ),\n\t\t\t\t\t\t\t\t\t\t\t'type'  => 'hidden',\n\t\t\t\t\t\t\t\t\t\t\t'value' => $file->id,\n\t\t\t\t\t\t\t\t\t\t] );\n\n\t\t\t\t\t\t\t\t\t\tif ( ! isset( $file->file ) && isset( $file->url ) ) {\n\t\t\t\t\t\t\t\t\t\t\t$file->file = $file->url;\n\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t\t?>\n\t\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t\t\t<?php if ( $this->file_type === 'file' ): ?>\n\t\t\t\t\t\t\t\t\t\t<div class=\"filename\">\n\t\t\t\t\t\t\t\t\t\t\t<div><?php echo esc_html( basename( $file->file ) ); ?></div>\n\t\t\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t\t\t<?php endif; ?>\n\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t</div>\n\t\t\t\t\t<?php\n\t\t\t\t\tendforeach;\n\t\t\t\tendif;\n\t\t\t\t?>\n\t\t\t</div>\n\t\t\t<div class=\"clear\"></div>\n\t\t</div>\n\t\t<?php\n\t}\n\n\t/**\n\t * Get labels.\n\t *\n\t * @return array\n\t */\n\tpublic function get_labels() {\n\t\treturn [\n\t\t\t'add'     => __( 'Add file', 'papi' ),\n\t\t\t'no_file' => __( 'No file selected', 'papi' ),\n\t\t];\n\t}\n\n\t/**\n\t * Render file template.\n\t */\n\tpublic function render_file_template() {\n\t\t?>\n\t\t<script type=\"text/template\" id=\"tmpl-papi-property-file\">\n\t\t\t<a class=\"check\" href=\"#\">&times;</a>\n\t\t\t<div class=\"attachment-preview\">\n\t\t\t\t<div class=\"thumbnail\">\n\t\t\t\t\t<div class=\"centered\">\n\t\t\t\t\t\t<img src=\"<%= url %>\" alt=\"<%= alt %>\"/>\n\t\t\t\t\t\t<input type=\"hidden\" value=\"<%= id %>\" name=\"<%= slug %>\"/>\n\t\t\t\t\t</div>\n\t\t\t\t\t<% if (typeof filename !== \"undefined\") { %>\n\t\t\t\t\t<div class=\"filename\">\n\t\t\t\t\t\t<div><%= filename %></div>\n\t\t\t\t\t</div>\n\t\t\t\t\t<% } %>\n\t\t\t\t</div>\n\t\t\t</div>\n\t\t</script>\n\t\t<?php\n\t}\n\n\t/**\n\t * Save file with a meta value or id.\n\t *\n\t * @param mixed  $values\n\t * @param string $slug\n\t * @param int    $post_id\n\t *\n\t * @return string\n\t */\n\tpublic function update_value( $values, $slug, $post_id ) {\n\t\tif ( ! is_array( $values ) ) {\n\t\t\t$values = $this->get_file_value( (object) [\n\t\t\t\t'id' => $values,\n\t\t\t] );\n\n\t\t\tif ( empty( $values ) ) {\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\treturn $values;\n\t\t}\n\n\t\tforeach ( $values as $index => $value ) {\n\t\t\tif ( ! is_numeric( $value ) ) {\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\t$values[ $index ] = $this->get_file_value( (object) [\n\t\t\t\t'id' => $value,\n\t\t\t] );\n\t\t}\n\n\t\treturn array_filter( $values );\n\t}\n\n\t/**\n\t * Get attachment metadata.\n\t *\n\t * @param mixed $data\n\t * @param int   $post_id\n\t *\n\t * @return mixed\n\t */\n\tpublic function wp_get_attachment_metadata( $data, $post_id ) {\n\t\tif ( papi_is_empty( $data ) ) {\n\t\t\treturn get_post_meta( $post_id, '_wp_attached_file', true );\n\t\t}\n\n\t\treturn $data;\n\t}\n\n\t/**\n\t * Check if the given id is a attachment post type or not.\n\t *\n\t * @param int $id\n\t *\n\t * @return bool\n\t */\n\tprotected function is_attachment( $id ) {\n\t\treturn get_post_type( (int) $id ) === 'attachment';\n\t}\n\n\t/**\n\t * Setup actions.\n\t */\n\tprotected function setup_actions() {\n\t\tadd_action( 'admin_head', [ $this, 'render_file_template' ] );\n\t}\n\n\t/**\n\t * Setup filters.\n\t */\n\tprotected function setup_filters() {\n\t\tadd_action( 'wp_get_attachment_metadata', [ $this, 'wp_get_attachment_metadata' ], 10, 2 );\n\t}\n}\n"
  },
  {
    "path": "src/properties/class-papi-property-flexible.php",
    "content": "<?php\n\n/**\n * Flexible repeater property that can repeat multiple properties\n * with different properties per layout.\n */\nclass Papi_Property_Flexible extends Papi_Property_Repeater {\n\n\t/**\n\t * The convert type.\n\t *\n\t * @var string\n\t */\n\tpublic $convert_type = 'array';\n\n\t/**\n\t * Flexible repeater counter number.\n\t *\n\t * @var int\n\t */\n\tprotected $counter = 0;\n\n\t/**\n\t * The default value.\n\t *\n\t * @var array\n\t */\n\tpublic $default_value = [];\n\n\t/**\n\t * Exclude properties that is not allowed in a repeater.\n\t *\n\t * @var array\n\t */\n\tprotected $exclude_properties = ['flexible'];\n\n\t/**\n\t * The layout key.\n\t *\n\t * @var string\n\t */\n\tprotected $layout_key = '_flexible_layout';\n\n\t/**\n\t * Layout value regex.\n\t *\n\t * @var string\n\t */\n\tprotected $layout_value_regex = '/^\\_flexible\\_layout\\_/';\n\n\t/**\n\t * Delete value from the database.\n\t *\n\t * @param  string $slug\n\t * @param  int    $post_id\n\t * @param  string $type\n\t *\n\t * @return bool\n\t */\n\tpublic function delete_value( $slug, $post_id, $type ) {\n\t\t$rows   = intval( papi_data_get( $post_id, $slug, $type ) );\n\t\t$value  = $this->load_value( $rows, $slug, $post_id );\n\t\t$value  = papi_property_to_array_slugs( $value, $slug );\n\t\t$result = true;\n\n\t\tforeach ( array_keys( $value ) as $key ) {\n\t\t\t$out    = papi_data_delete( $post_id, $key, $type );\n\t\t\t$result = $out ? $result : $out;\n\t\t}\n\n\t\treturn $result;\n\t}\n\n\t/**\n\t * Format the value of the property before it's returned\n\t * to WordPress admin or the site.\n\t *\n\t * @param  mixed  $values\n\t * @param  string $repeater_slug\n\t * @param  int    $post_id\n\t *\n\t * @return array\n\t */\n\tpublic function format_value( $values, $repeater_slug, $post_id ) {\n\t\tif ( ! is_array( $values ) ) {\n\t\t\treturn [];\n\t\t}\n\n\t\tforeach ( $values as $index => $layout ) {\n\t\t\tforeach ( $layout as $slug => $value ) {\n\t\t\t\tif ( is_string( $value ) && preg_match( $this->layout_value_regex, $value ) ) {\n\t\t\t\t\tif ( isset( $values[$index][$this->layout_key] ) ) {\n\t\t\t\t\t\tunset( $values[$index][$slug] );\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\n\t\t\t\t\t$values[$index][$this->layout_key] = $value;\n\t\t\t\t\tunset( $values[$index][$slug] );\n\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\n\t\t\t\tif ( papi_is_property_type_key( $slug ) ) {\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\n\t\t\t\t$property_type_slug = papi_get_property_type_key_f( $slug );\n\n\t\t\t\tif ( ! isset( $values[$index][$property_type_slug] ) ) {\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\n\t\t\t\t$property_type_value = $values[$index][$property_type_slug];\n\t\t\t\t$property_type       = papi_get_property_type( $property_type_value );\n\n\t\t\t\tif ( ! is_object( $property_type ) ) {\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\n\t\t\t\t// Set parent property.\n\t\t\t\t$property_type->set_parent_property( $this );\n\n\t\t\t\t// Get property child slug.\n\t\t\t\t$child_slug = $this->get_child_slug( $repeater_slug, $slug );\n\n\t\t\t\t// Create cache key.\n\t\t\t\t$cache_key = sprintf( '%s_%d_%s', $repeater_slug, $index, $slug );\n\n\t\t\t\t// Get raw value from cache if enabled.\n\t\t\t\tif ( $this->cache ) {\n\t\t\t\t\t$raw_value = papi_cache_get( $cache_key, $post_id, $this->get_meta_type() );\n\t\t\t\t} else {\n\t\t\t\t\t$raw_value = false;\n\t\t\t\t}\n\n\t\t\t\t// Load the value.\n\t\t\t\tif ( $raw_value === null || $raw_value === false ) {\n\t\t\t\t\t$values[$index][$slug] = $property_type->load_value( $value, $child_slug, $post_id );\n\t\t\t\t\t$values[$index][$slug] = papi_filter_load_value( $property_type->type, $values[$index][$slug], $child_slug, $post_id, papi_get_meta_type() );\n\n\t\t\t\t\tif ( ! papi_is_empty( $values[$index][$slug] ) && $this->cache ) {\n\t\t\t\t\t\tpapi_cache_set( $cache_key, $post_id, $values[$index][$slug], $this->get_meta_type() );\n\t\t\t\t\t}\n\t\t\t\t} else {\n\t\t\t\t\t$values[$index][$slug] = $raw_value;\n\t\t\t\t}\n\n\t\t\t\tif ( strtolower( $property_type->type ) === 'repeater' ) {\n\t\t\t\t\t$property_type->cache = false;\n\t\t\t\t}\n\n\t\t\t\t// Format the value from the property class.\n\t\t\t\t$values[$index][$slug] = $property_type->format_value( $values[$index][$slug], $child_slug, $post_id );\n\n\t\t\t\tif ( ! papi_is_admin() ) {\n\t\t\t\t\t$values[$index][$slug] = papi_filter_format_value( $property_type->type, $values[$index][$slug], $child_slug, $post_id, papi_get_meta_type() );\n\t\t\t\t}\n\n\t\t\t\t$values[$index][$property_type_slug] = $property_type_value;\n\n\t\t\t\tif ( papi_is_empty( $values[$index][$slug] ) ) {\n\t\t\t\t\t$values[$index][$slug] = $property_type_value->get_option( 'default', $property_type_value->default_value );\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tif ( ! papi_is_admin() ) {\n\t\t\tforeach ( $values as $index => $row ) {\n\t\t\t\tforeach ( $row as $slug => $value ) {\n\t\t\t\t\tif ( is_string( $value ) && preg_match( $this->layout_value_regex, $value ) ) {\n\t\t\t\t\t\tunset( $values[$index][$slug] );\n\t\t\t\t\t\t$values[$index]['_layout'] = preg_replace( $this->layout_value_regex, '', $value );\n\t\t\t\t\t}\n\n\t\t\t\t\tif ( papi_is_property_type_key( $slug ) ) {\n\t\t\t\t\t\tunset( $values[$index][$slug] );\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\treturn $values;\n\t}\n\n\t/**\n\t * Check if the given key is a valid layout key.\n\t *\n\t * @param  string $key\n\t *\n\t * @return bool\n\t */\n\tprotected function is_layout_key( $key ) {\n\t\treturn is_string( $key ) && preg_match( '/\\\\_layout|\\\\' . $this->layout_key . '$/', $key );\n\t}\n\n\t/**\n\t * Generate layout slug.\n\t *\n\t * @param  string $key\n\t * @param  string $extra\n\t *\n\t * @return string\n\t */\n\tprotected function get_json_id( $key, $extra = '' ) {\n\t\treturn $this->get_slug() . '_' . papi_slugify( $key ) . ( empty( $extra ) ? '' : '_' . $extra );\n\t}\n\n\t/**\n\t * Get layout by slug.\n\t *\n\t * @param  string $slug\n\t *\n\t * @return array\n\t */\n\tprotected function get_layout( $slug ) {\n\t\t$layouts = $this->get_settings_layouts();\n\n\t\tforeach ( $layouts as $layout ) {\n\t\t\tif ( $layout['slug'] === $slug ) {\n\t\t\t\treturn $layout;\n\t\t\t}\n\t\t}\n\n\t\treturn [];\n\t}\n\n\t/**\n\t * Get layout value.\n\t *\n\t * @param  string $layout\n\t *\n\t * @return string\n\t */\n\tprotected function get_layout_value( $layout ) {\n\t\tif ( preg_match( $this->layout_value_regex, $layout ) ) {\n\t\t\treturn $layout;\n\t\t}\n\n\t\treturn sprintf( '_flexible_layout_%s', $layout );\n\t}\n\n\t/**\n\t * Get results from the database.\n\t *\n\t * @param  int    $value\n\t * @param  string $repeater_slug\n\t * @param  int    $post_id\n\t *\n\t * @return array\n\t */\n\tprotected function get_results( $value, $repeater_slug, $post_id ) {\n\t\tglobal $wpdb;\n\n\t\tif ( $this->get_meta_type() === 'option' ) {\n\t\t\t$table = $wpdb->prefix . 'options';\n\t\t\t// @codingStandardsIgnoreStart\n\t\t\t$query = $wpdb->prepare(\n\t\t\t\t\"SELECT * FROM `$table` WHERE `option_name` LIKE '%s' ORDER BY `option_id` ASC\",\n\t\t\t\t$repeater_slug . '_%'\n\t\t\t);\n\t\t\t// @codingStandardsIgnoreEnd\n\t\t} else {\n\t\t\t$table  = sprintf( '%s%smeta', $wpdb->prefix, $this->get_meta_type() );\n\t\t\t$column = papi_get_meta_id_column( $this->get_meta_type() );\n\t\t\t// @codingStandardsIgnoreStart\n\t\t\t$query = $wpdb->prepare(\n\t\t\t\t\"SELECT * FROM `$table` WHERE `meta_key` LIKE '%s' AND `$column` = %s ORDER BY `meta_id` ASC\", $repeater_slug . '_%',\n\t\t\t\t$post_id\n\t\t\t);\n\t\t\t// @codingStandardsIgnoreEnd\n\t\t}\n\n\t\t$dbresults = $wpdb->get_results( $query ); // WPCS: unprepared sql\n\t\t$value     = intval( $value );\n\n\t\t// Do not proceed with empty value or columns.\n\t\tif ( empty( $value ) ) {\n\t\t\treturn [[], []];\n\t\t}\n\n\t\t$values  = [];\n\t\t$results = [];\n\t\t$trash   = [];\n\n\t\t// Get row results.\n\t\t$rows = $this->get_row_results( $dbresults );\n\n\t\t// Get columns, divde all items with two.\n\t\t$columns = array_map( function ( $row ) {\n\t\t\treturn count( $row ) / 2;\n\t\t}, $rows );\n\n\t\t$rows = array_values( $rows );\n\n\t\t// Add repeater slug with number of rows to the values array.\n\t\t$values[$repeater_slug] = $value;\n\n\t\tfor ( $i = 0; $i < $value; $i ++ ) {\n\t\t\t$no_trash = [];\n\n\t\t\tif ( ! isset( $columns[$i] ) || ! isset( $rows[$i] ) ) {\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tforeach ( $rows[$i] as $slug => $meta ) {\n\t\t\t\tif ( ! is_string( $slug ) || ! isset( $rows[$i][$slug] ) ) {\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\n\t\t\t\t// Do not deal with layout meta object here since the property meta object will deal with it later.\n\t\t\t\tif ( is_string( $meta->meta_value ) && preg_match( $this->layout_value_regex, $meta->meta_value ) ) {\n\t\t\t\t\tif ( ! isset( $values[$slug] ) ) {\n\t\t\t\t\t\t$values[$slug] = $meta->meta_value;\n\t\t\t\t\t}\n\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\n\t\t\t\t// Add meta object to the no trash array.\n\t\t\t\t// so it won't be deleted.\n\t\t\t\t$no_trash[$slug] = $meta;\n\n\t\t\t\t// Serialize value if needed.\n\t\t\t\t$meta->meta_value = papi_maybe_json_decode( maybe_unserialize( $meta->meta_value ) );\n\n\t\t\t\t// Add property value and property type value.\n\t\t\t\t$values[$meta->meta_key] = $meta->meta_value;\n\n\t\t\t\tif ( isset( $rows[$i][$slug] ) ) {\n\t\t\t\t\t// Add the meta value.\n\t\t\t\t\t$values[$slug] = $rows[$i][$slug]->meta_value;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// Get the meta keys to delete.\n\t\t\t$trash_diff = array_diff( array_keys( $rows[$i] ), array_keys( $no_trash ) );\n\n\t\t\tif ( ! empty( $trash_diff ) ) {\n\t\t\t\t// Find all trash meta objects from results array.\n\t\t\t\tforeach ( $trash_diff as $slug ) {\n\t\t\t\t\tif ( ! isset( $results[$i] ) || ! isset( $rows[$i][$slug] ) ) {\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\n\t\t\t\t\t$trash[$results[$i][$slug]->meta_key] = $rows[$i][$slug];\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t// Fetch one layout per row.\n\t\t// Since 3.0.0 this is backward compatibility.\n\t\t$dblayouts = [];\n\t\tforeach ( array_keys( $values ) as $slug ) {\n\t\t\tif ( $this->is_layout_key( $slug ) ) {\n\t\t\t\t$num = str_replace( $repeater_slug . '_', '', $slug );\n\t\t\t\t$num = explode( '_', $num );\n\t\t\t\t$num = intval( $num[0] );\n\n\t\t\t\tif ( ! isset( $dblayouts[$num] ) ) {\n\t\t\t\t\t$dblayouts[$num] = $num . $values[$slug];\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t$layouts = $this->get_settings_layouts();\n\n\t\t// Add empty rows that isn't saved to database.\n\t\tfor ( $i = 0; $i < $value; $i ++ ) {\n\t\t\tforeach ( $layouts as $layout ) {\n\t\t\t\t$layout_slug = sprintf( '%s_%d%s', $this->get_slug( true ), $i, $this->layout_key );\n\n\t\t\t\t// Since 3.0.0 the `$dblayouts` check is only for backward compatibility.\n\t\t\t\tif ( isset( $layout['slug'] ) && ( ( isset( $values[$layout_slug] ) && $layout['slug'] === $values[$layout_slug] ) || in_array( $i . $layout['slug'], $dblayouts, true ) ) ) {\n\t\t\t\t\tforeach ( $layout['items'] as $prop ) {\n\t\t\t\t\t\t$slug = sprintf( '%s_%d_%s', $repeater_slug, $i, unpapify( $prop->slug ) );\n\n\t\t\t\t\t\tif ( ! isset( $values[$slug] ) ) {\n\t\t\t\t\t\t\t$values[$slug] = null;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\treturn [$values, $trash];\n\t}\n\n\t/**\n\t * Get layouts.\n\t *\n\t * @return array\n\t */\n\tprotected function get_settings_layouts() {\n\t\t$settings = $this->get_settings();\n\n\t\treturn $this->prepare_properties( papi_to_array( $settings->items ) );\n\t}\n\n\t/**\n\t * Change value after it's loaded from the database\n\t * and populate every property in the flexible with the right property type.\n\t *\n\t * @param mixed  $value\n\t * @param string $repeater_slug\n\t * @param int    $post_id\n\t *\n\t * @return array\n\t */\n\tpublic function load_value( $value, $repeater_slug, $post_id ) {\n\t\tif ( is_array( $value ) ) {\n\t\t\treturn $value;\n\t\t}\n\n\t\tlist( $results, $trash ) = $this->get_results( $value, $repeater_slug, $post_id );\n\n\t\t// Will not need this array.\n\t\tunset( $trash );\n\n\t\t$store   = $this->get_store();\n\t\t$results = papi_property_from_array_slugs( $results, unpapify( $repeater_slug ) );\n\n\t\tif ( is_null( $store ) ) {\n\t\t\treturn $this->default_value;\n\t\t}\n\n\t\treturn $this->load_child_properties( $results, $this );\n\t}\n\n\t/**\n\t * Load child properties.\n\t *\n\t * @param  array              $results\n\t * @param  Papi_Core_Property $property\n\t *\n\t * @return array\n\t */\n\tprotected function load_child_properties( array $results, $property = null ) {\n\t\t$layout_key = substr( $this->layout_key, 1 );\n\n\t\tforeach ( $results as $index => $row ) {\n\t\t\tforeach ( $row as $slug => $value ) {\n\t\t\t\t$children = [];\n\n\t\t\t\tif ( $layout_key === $slug ) {\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\n\t\t\t\tif ( isset( $results[$index][$layout_key] ) ) {\n\t\t\t\t\t$layout = $results[$index][$layout_key];\n\t\t\t\t\t$layout = $this->get_layout( $layout );\n\n\t\t\t\t\tif ( ! empty( $layout ) && isset( $layout['items'] ) ) {\n\t\t\t\t\t\t$children = $layout['items'];\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\t$child_property = null;\n\n\t\t\t\tforeach ( $children as $child ) {\n\t\t\t\t\tif ( $child->match_slug( $slug ) ) {\n\t\t\t\t\t\t$child_property = $child;\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tif ( empty( $child_property ) ) {\n\t\t\t\t\t$child_property = $this->get_store()->get_property( $this->get_slug( true ), $slug );\n\t\t\t\t}\n\n\t\t\t\tif ( is_array( $value ) && papi_is_property( $child_property ) && ! empty( $child_property->get_child_properties() ) ) {\n\t\t\t\t\t$new_value = papi_from_property_array_slugs( $value, unpapify( $slug ) );\n\n\t\t\t\t\tif ( empty( $new_value ) ) {\n\t\t\t\t\t\t$results[$index][$slug] = $value;\n\t\t\t\t\t} else {\n\t\t\t\t\t\t$results[$index][$slug] = $this->load_child_properties( $new_value, $child_property );\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\t$type_key = papi_get_property_type_key_f( $slug );\n\n\t\t\t\tif ( $property->match_slug( $slug ) ) {\n\t\t\t\t\t$results[$index][$type_key] = $property;\n\t\t\t\t} else {\n\t\t\t\t\t$results[$index][$type_key] = $property->get_child_property( $slug, $children );\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\treturn $results;\n\t}\n\n\t/**\n\t * Prepare properties.\n\t *\n\t * Not the best name for this function, but since\n\t * property repeater using this we can't rename it.\n\t *\n\t * @param  array $layouts\n\t *\n\t * @return array\n\t */\n\tprotected function prepare_properties( $layouts ) {\n\t\t$layouts = array_map( function ( $layout ) {\n\t\t\treturn (array) $layout;\n\t\t}, $layouts );\n\n\t\tforeach ( $layouts as $index => $layout ) {\n\t\t\tif ( ! $this->valid_layout( $layout ) ) {\n\t\t\t\tif ( is_array( $layout ) ) {\n\t\t\t\t\tunset( $layout[$index] );\n\t\t\t\t} else {\n\t\t\t\t\tunset( $layouts[$index] );\n\t\t\t\t}\n\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tif ( ! isset( $layout['slug'] ) ) {\n\t\t\t\t$layout['slug'] = $layout['title'];\n\t\t\t}\n\n\t\t\tif ( ! isset( $layout['row_label'] ) ) {\n\t\t\t\t$layout['row_label'] = $layout['title'];\n\t\t\t}\n\n\t\t\tif ( ! isset( $layout['show_label'] ) ) {\n\t\t\t\t$layout['show_label'] = true;\n\t\t\t}\n\n\t\t\t$layouts[$index] = array_merge( $layouts[$index], $layout );\n\n\t\t\t$layouts[$index]['slug']  = papi_slugify( $layout['slug'] );\n\t\t\t$layouts[$index]['slug']  = $this->get_layout_value( $layouts[$index]['slug'] );\n\t\t\t$layouts[$index]['items'] = parent::prepare_properties( $layout['items'] );\n\t\t}\n\n\t\treturn array_filter( $layouts );\n\t}\n\n\t/**\n\t * Render AJAX request.\n\t */\n\tpublic function render_ajax_request() {\n\t\t$items   = null;\n\t\t$layouts = $this->get_settings_layouts();\n\n\t\tif ( defined( 'DOING_PAPI_AJAX' ) && DOING_PAPI_AJAX ) {\n\t\t\t$counter         = papi_get_qs( 'counter' );\n\t\t\t$this->counter   = intval( $counter );\n\t\t\t$flexible_layout = papi_get_qs( 'flexible_layout' );\n\n\t\t\tforeach ( $layouts as $layout ) {\n\t\t\t\tif ( $layout['slug'] === $flexible_layout ) {\n\t\t\t\t\t$items = $layout;\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tif ( ! empty( $items ) ) {\n\t\t\t$this->render_properties( $items, false );\n\t\t}\n\t}\n\n\t/**\n\t * Render layout JSON template.\n\t *\n\t * @param string $slug\n\t */\n\tprotected function render_json_template( $slug ) {\n\t\t$options = $this->get_options();\n\n\t\t$options->settings->items = papi_to_array( $options->settings->items );\n\n\t\tforeach ( $options->settings->items as $key => $value ) {\n\t\t\tif ( ! isset( $value['items'] ) ) {\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tforeach ( $value['items'] as $index => $property ) {\n\t\t\t\t$property = $this->prepare_property_for_json( papi_property( $property ) );\n\n\t\t\t\tif ( $property === false ) {\n\t\t\t\t\tunset( $options->settings->items[$key]['items'][$index] );\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\n\t\t\t\t$options->settings->items[$key]['items'][$index] = $property;\n\t\t\t}\n\t\t}\n\n\t\tpapi_render_html_tag( 'script', [\n\t\t\t'data-papi-json' => esc_attr( sprintf( '%s_repeater_json', $slug ) ),\n\t\t\t'type'           => 'application/json',\n\t\t\tpapi_maybe_json_encode( [$options] )\n\t\t] );\n\t}\n\n\t/**\n\t * Render layout input.\n\t *\n\t * @param string $value\n\t */\n\tprotected function render_layout_input( $value ) {\n\t\t$slug = sprintf( '%s[%d][%s]', $this->get_slug(), $this->counter, $this->layout_key );\n\t\t?>\n\t\t<input type=\"hidden\" name=\"<?php echo esc_attr( $slug ); ?>\" value=\"<?php echo esc_attr( $value ); ?>\" />\n\t\t<?php\n\t}\n\n\t/**\n\t * Render properties.\n\t *\n\t * @param array      $row\n\t * @param array|bool $value\n\t */\n\tprotected function render_properties( $row, $value ) {\n\t\t$has_value     = $value !== false;\n\t\t$render_layout = $this->get_setting( 'layout' );\n\t\t$layout_slug   = isset( $row['slug'] ) ? $row['slug'] : false;\n\t\t$layout_slug   = empty( $layout_slug ) && isset( $value['_layout'] ) ? $value['_layout'] : $layout_slug;\n\t\t$layout_slug   = empty( $layout_slug ) && isset( $value[$this->layout_key] ) ? $value[$this->layout_key] : $layout_slug;\n\t\t$row           = isset( $row['items'] ) ? $row['items'] : $row;\n\t\t$layout        = $this->get_layout( $layout_slug );\n\n\t\t// Render one hidden input for layout slug.\n\t\t$this->render_layout_input( $layout_slug );\n\t\t?>\n\t\t<td class=\"repeater-column flexible-column <?php echo $render_layout === 'table' ? 'flexible-layout-table' : 'flexible-layout-row'; ?>\">\n\t\t\t<div class=\"repeater-content-open\">\n\t\t\t\t<?php // flexible label table layout ?>\n\t\t\t\t<?php if ( $render_layout === 'table' && ! empty( $layout['row_label'] ) && isset( $layout['show_label'] ) && $layout['show_label'] ): ?>\n\t\t\t\t\t<label class=\"flexible-row-label\"><?php echo esc_html( $layout['row_label'] ); ?></label>\n\t\t\t\t<?php endif; ?>\n\t\t\t\t<table class=\"<?php echo $render_layout === 'table' ? 'flexible-table' : 'papi-table'; ?>\">\n\t\t\t\t\t<?php\n\t\t\t\t\tif ( $render_layout === 'table' ):\n\t\t\t\t\t\techo '<thead>';\n\t\t\t\t\t\tfor ( $i = 0, $l = count( $row ); $i < $l; $i ++ ) {\n\t\t\t\t\t\t\t// Don't show the property if it's disabled.\n\t\t\t\t\t\t\tif ( $row[$i]->disabled() ) {\n\t\t\t\t\t\t\t\tcontinue;\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\tif ( $row[$i]->sidebar ) {\n\t\t\t\t\t\t\t\techo '<th class=\"' . ( $row[$i]->display() ? '' : 'papi-hide' ) . '\">';\n\t\t\t\t\t\t\t\techo sprintf(\n\t\t\t\t\t\t\t\t\t'<label for=\"%s\">%s</label>',\n\t\t\t\t\t\t\t\t\tesc_attr( $this->html_id( $row[$i], $this->counter ) ),\n\t\t\t\t\t\t\t\t\tesc_html( $row[$i]->title )\n\t\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\t\techo '</th>';\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t\techo '</thead>';\n\t\t\t\t\tendif;\n\n\t\t\t\t\techo '<tbody>';\n\n\t\t\t\t\tif ( $render_layout === 'table' ):\n\t\t\t\t\t\techo '<tr>';\n\t\t\t\t\tendif;\n\n\t\t\t\t\tfor ( $i = 0, $l = count( $row ); $i < $l; $i ++ ) {\n\t\t\t\t\t\t// Don't show the property if it's disabled.\n\t\t\t\t\t\tif ( $row[$i]->disabled() ) {\n\t\t\t\t\t\t\tcontinue;\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\t$render_property = clone $row[$i]->get_options();\n\t\t\t\t\t\t$value_slug      = $row[$i]->get_slug( true );\n\n\t\t\t\t\t\tif ( $has_value ) {\n\t\t\t\t\t\t\tif ( array_key_exists( $value_slug, $value ) ) {\n\t\t\t\t\t\t\t\t$render_property->value = $value[$value_slug];\n\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\tif ( array_key_exists( $row[$i]->get_slug(), $value ) ) {\n\t\t\t\t\t\t\t\t\t$render_property->value = $row[$i]->default_value;\n\t\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\t\tcontinue;\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\t$render_property->slug = $this->html_name(\n\t\t\t\t\t\t\t$render_property,\n\t\t\t\t\t\t\t$this->counter\n\t\t\t\t\t\t);\n\t\t\t\t\t\t$render_property->raw  = $render_layout === 'table';\n\n\t\t\t\t\t\tif ( $render_layout === 'table' ) {\n\t\t\t\t\t\t\techo '<td class=\"' . ( $row[$i]->display() ? '' : 'papi-hide' ) . '\">';\n\t\t\t\t\t\t} else if ( $i === 0 && ! empty( $layout['row_label'] ) && isset( $layout['show_label'] ) && $layout['show_label'] ) {\n\t\t\t\t\t\t\t// flexible label row layout\n\t\t\t\t\t\t\techo sprintf( '<label class=\"flexible-row-label\">%s</label>', esc_html( $layout['row_label'] ) );\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tpapi_render_property( $render_property );\n\n\t\t\t\t\t\tif ( $render_layout === 'table' ) {\n\t\t\t\t\t\t\techo '</td>';\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\n\t\t\t\t\tif ( $render_layout === 'table' ):\n\t\t\t\t\t\techo '</tr>';\n\t\t\t\t\tendif;\n\n\t\t\t\t\techo '</tbody>';\n\t\t\t\t\t?>\n\t\t\t\t</table>\n\t\t\t</div>\n\t\t\t<div class=\"repeater-content-closed\">\n\t\t\t\t<?php\n\t\t\t\tif ( ! empty( $layout['title'] ) ) {\n\t\t\t\t\techo esc_html( $layout['title'] );\n\t\t\t\t}\n\t\t\t\t?>\n\t\t\t</div>\n\t\t</td>\n\t\t<?php\n\t}\n\n\t/**\n\t * Render repeater html.\n\t *\n\t * @param object $options\n\t */\n\tprotected function render_repeater( $options ) {\n\t\t$layouts = $this->get_settings_layouts();\n\t\t?>\n\t\t<div class=\"papi-property-flexible papi-property-repeater-top\" data-limit=\"<?php echo esc_attr( $this->get_setting( 'limit' ) ); ?>\">\n\t\t\t<table class=\"papi-table\">\n\t\t\t\t<tbody class=\"repeater-tbody flexible-tbody\">\n\t\t\t\t<?php $this->render_repeater_row(); ?>\n\t\t\t\t</tbody>\n\t\t\t</table>\n\n\t\t\t<div class=\"bottom\">\n\t\t\t\t<div class=\"flexible-layouts-btn-wrap\">\n\t\t\t\t\t<div class=\"flexible-layouts flexible-layouts-hidden\">\n\t\t\t\t\t\t<div class=\"flexible-layouts-arrow\"></div>\n\t\t\t\t\t\t<ul>\n\t\t\t\t\t\t\t<?php\n\t\t\t\t\t\t\tforeach ( $layouts as $layout ) {\n\t\t\t\t\t\t\t\tpapi_render_html_tag( 'li', [\n\t\t\t\t\t\t\t\t\tpapi_html_tag( 'a', [\n\t\t\t\t\t\t\t\t\t\t'data-layout'    => esc_html( $layout['slug'] ),\n\t\t\t\t\t\t\t\t\t\t'data-papi-json' => sprintf( '%s_repeater_json', $options->slug ),\n\t\t\t\t\t\t\t\t\t\t'href'           => '#',\n\t\t\t\t\t\t\t\t\t\t'role'           => 'button',\n\t\t\t\t\t\t\t\t\t\t'tabindex'       => 0,\n\t\t\t\t\t\t\t\t\t\tesc_html( $layout['title'] )\n\t\t\t\t\t\t\t\t\t] )\n\t\t\t\t\t\t\t\t] );\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t?>\n\t\t\t\t\t\t</ul>\n\t\t\t\t\t</div>\n\n\t\t\t\t\t<?php\n\t\t\t\t\tpapi_render_html_tag( 'button', [\n\t\t\t\t\t\t'class' => 'button button-primary',\n\t\t\t\t\t\t'type'  => 'button',\n\t\t\t\t\t\tesc_html( $this->get_setting( 'add_new_label' ) )\n\t\t\t\t\t] );\n\t\t\t\t\t?>\n\t\t\t\t</div>\n\t\t\t</div>\n\n\t\t\t<?php /* Default repeater value */ ?>\n\n\t\t\t<input type=\"hidden\" data-papi-rule=\"<?php echo esc_attr( $options->slug ); ?>\" name=\"<?php echo esc_attr( $this->get_slug() ); ?>[]\" />\n\t\t</div>\n\t\t<?php\n\t}\n\n\t/**\n\t * Render repeater row.\n\t */\n\tprotected function render_repeater_row() {\n\t\t$layouts = $this->get_settings_layouts();\n\t\t$values  = $this->get_value();\n\n\t\t// Fetch all slugs in all layouts.\n\t\t$slugs = [];\n\t\tforeach ( $layouts as $index => $layout ) {\n\t\t\tforeach ( $layout['items'] as $item ) {\n\t\t\t\t$slugs[] = unpapify( $item->slug );\n\t\t\t}\n\t\t}\n\n\t\t// Remove values that don't exists in the slugs array.\n\t\tforeach ( $values as $index => $row ) {\n\t\t\t$keys = array_keys( $row );\n\n\t\t\tforeach ( array_keys( $row ) as $slug ) {\n\t\t\t\tif ( in_array( $slug, $keys, true ) || papi_is_property_type_key( $slug ) || $this->is_layout_key( $slug ) ) {\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\n\t\t\t\tunset( $values[$index][$slug] );\n\t\t\t}\n\t\t}\n\n\t\t$values      = array_filter( $values );\n\t\t$closed_rows = $this->get_setting( 'closed_rows', true );\n\n\t\tforeach ( $values as $index => $row ):\n\t\t\t?>\n\n\t\t\t<tr <?php echo $closed_rows ? 'class=\"closed\"' : ''; ?>>\n\t\t\t\t<td class=\"handle\">\n\t\t\t\t\t<span class=\"toggle\"></span>\n\t\t\t\t\t<span class=\"count\"><?php echo esc_html( $this->counter + 1 ); ?></span>\n\t\t\t\t</td>\n\t\t\t\t<?php\n\t\t\t\tforeach ( $layouts as $layout ) {\n\t\t\t\t\t// Don't render layouts that don't have a valid value in the database.\n\t\t\t\t\tif ( ! isset( $row[$this->layout_key] ) || $layout['slug'] !== $this->get_layout_value( $row[$this->layout_key] ) ) {\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\n\t\t\t\t\t// Render all properties in the layout\n\t\t\t\t\t$this->render_properties( $layout['items'], $row );\n\t\t\t\t}\n\n\t\t\t\t$this->counter ++;\n\t\t\t\t?>\n\t\t\t\t<td class=\"last\">\n\t\t\t\t\t<span>\n\t\t\t\t\t\t<a title=\"<?php esc_html_e( 'Remove', 'papi' ); ?>\" href=\"#\" class=\"repeater-remove-item\">x</a>\n\t\t\t\t\t</span>\n\t\t\t\t</td>\n\t\t\t</tr>\n\n\t\t\t<?php\n\t\tendforeach;\n\t}\n\n\t/**\n\t * Render repeater row template.\n\t */\n\tpublic function render_repeater_row_template() {\n\t\t?>\n\t\t<script type=\"text/template\" id=\"tmpl-papi-property-flexible-row\">\n\t\t\t<tr>\n\t\t\t\t<td class=\"handle\">\n\t\t\t\t\t<span class=\"toggle\"></span>\n\t\t\t\t\t<span class=\"count\"><%= counter + 1 %></span>\n\t\t\t\t</td>\n\t\t\t\t<%= columns %>\n\t\t\t\t<td class=\"last\">\n\t\t\t\t\t<span>\n\t\t\t\t\t\t<a title=\"<?php esc_html_e( 'Remove', 'papi' ); ?>\" href=\"#\" class=\"repeater-remove-item\">x</a>\n\t\t\t\t\t</span>\n\t\t\t\t</td>\n\t\t\t</tr>\n\t\t</script>\n\t\t<?php\n\t}\n\n\t/**\n\t * Setup actions.\n\t */\n\tprotected function setup_actions() {\n\t\tadd_action( 'admin_head', [$this, 'render_repeater_row_template'] );\n\t}\n\n\t/**\n\t * Check if the layout is valid or not.\n\t *\n\t * @param  array $layout\n\t *\n\t * @return bool\n\t */\n\tprotected function valid_layout( array $layout ) {\n\t\treturn isset( $layout['title'], $layout['items'] );\n\t}\n}\n"
  },
  {
    "path": "src/properties/class-papi-property-gallery.php",
    "content": "<?php\n\n/**\n * WordPress media file property as a gallery.\n */\nclass Papi_Property_Gallery extends Papi_Property_Image {\n\n\t/**\n\t * The convert type.\n\t *\n\t * @var string\n\t */\n\tpublic $convert_type = 'array';\n\n\t/**\n\t * The default value.\n\t *\n\t * @var array\n\t */\n\tpublic $default_value = [];\n\n\t/**\n\t * Get default settings.\n\t *\n\t * @return array\n\t */\n\tpublic function get_default_settings() {\n\t\treturn [\n\t\t\t'multiple' => true\n\t\t];\n\t}\n}\n"
  },
  {
    "path": "src/properties/class-papi-property-group.php",
    "content": "<?php\n\n/**\n * Group property that render child properties in a table.\n */\nclass Papi_Property_Group extends Papi_Property_Repeater {\n\n\t/**\n\t * Format the value of the property before it's returned\n\t * to WordPress admin or the site.\n\t *\n\t * @param  mixed  $value\n\t * @param  string $slug\n\t * @param  int    $post_id\n\t *\n\t * @return array\n\t */\n\tpublic function format_value( $value, $slug, $post_id ) {\n\t\tif ( ! is_array( $value ) ) {\n\t\t\treturn [];\n\t\t}\n\n\t\t// Fixes issue when flexible and repeater are used with group property\n\t\t// cache since we use repeaters format value function that cache values.\n\t\t// At this point the value array don't know about the parent structure.\n\t\tif ( $this->get_parent_property() instanceof Papi_Property_Repeater ) {\n\t\t\t$this->cache = false;\n\t\t}\n\n\t\t$value = parent::format_value( $value, $slug, $post_id );\n\n\t\treturn array_shift( $value );\n\t}\n\n\t/**\n\t * Get default settings.\n\t *\n\t * @return array\n\t */\n\tpublic function get_default_settings() {\n\t\treturn [\n\t\t\t'items' => []\n\t\t];\n\t}\n\n\t/**\n\t * Get settings properties.\n\t *\n\t * @return array\n\t */\n\tprotected function get_settings_properties() {\n\t\t$settings = $this->get_settings();\n\n\t\tif ( is_null( $settings ) ) {\n\t\t\treturn [];\n\t\t}\n\n\t\treturn array_filter( array_map( 'papi_property', papi_to_array( $settings->items ) ), 'papi_is_property' );\n\t}\n\n\t/**\n\t * Render property html.\n\t */\n\tpublic function html() {\n\t\t$properties = $this->get_settings_properties();\n\t\t$properties = $this->prepare_properties( $properties );\n\n\t\t// Fix so group is not render over the title and description.\n\t\tif ( $this->get_option( 'layout' ) === 'vertical' ) {\n\t\t\techo '<br />';\n\t\t}\n\n\t\techo '<div class=\"papi-property-group\" data-papi-rule=\"' . esc_html( $this->html_name() ) . '\">';\n\t\t\tpapi_render_properties( $properties );\n\t\techo '</div>';\n\t}\n\n\t/**\n\t * Prepare properties and set right slug and value.\n\t *\n\t * @param  array $properties\n\t *\n\t * @return array\n\t */\n\tprotected function prepare_properties( $properties ) {\n\t\t$result = [];\n\t\t$value  = $this->get_value();\n\t\t$value  = is_array( $value ) ? $value : [];\n\n\t\tforeach ( $properties as $property ) {\n\t\t\t$render_property = clone $property->get_options();\n\t\t\t$value_slug      = $property->get_slug( true );\n\n\t\t\tif ( array_key_exists( $value_slug, $value ) ) {\n\t\t\t\t$render_property->value = $value[$value_slug];\n\t\t\t} else {\n\t\t\t\t$render_property->value = null;\n\t\t\t}\n\n\t\t\t$render_property->slug = $this->html_name( $property );\n\n\t\t\t$result[] = $render_property;\n\t\t}\n\n\t\treturn $result;\n\t}\n\n\t/**\n\t * Update value before it's saved to the database.\n\t *\n\t * @param mixed  $values\n\t * @param string $slug\n\t * @param int    $post_id\n\t *\n\t * @return array\n\t */\n\tpublic function update_value( $values, $slug, $post_id ) {\n\t\tif ( ! isset( $values[0] ) && ! empty( $values ) ) {\n\t\t\t$values = [$values];\n\t\t}\n\n\t\treturn parent::update_value( $values, $slug, $post_id );\n\t}\n}\n"
  },
  {
    "path": "src/properties/class-papi-property-hidden.php",
    "content": "<?php\n\n/**\n * HTML hidden input property.\n */\nclass Papi_Property_Hidden extends Papi_Property_String {\n\n\t/**\n\t * Don't display the property in WordPress admin.\n\t *\n\t * @var bool\n\t */\n\tprotected $display = false;\n\n\t/**\n\t * The input type to use.\n\t *\n\t * @var string\n\t */\n\tpublic $input_type = 'hidden';\n\n}\n"
  },
  {
    "path": "src/properties/class-papi-property-html.php",
    "content": "<?php\n\n/**\n * HTML property that can display html in the admin.\n */\nclass Papi_Property_Html extends Papi_Property {\n\n\t/**\n\t * Determine if the property require a slug or not.\n\t *\n\t * @var bool\n\t */\n\tprotected $slug_required = false;\n\n\t/**\n\t * Get default settings.\n\t *\n\t * @return array\n\t */\n\tpublic function get_default_settings() {\n\t\treturn [\n\t\t\t'html' => '',\n\t\t\t'save' => false\n\t\t];\n\t}\n\n\t/**\n\t * Render property html.\n\t */\n\tpublic function html() {\n\t\t$settings = $this->get_settings();\n\t\t$html     = papi_maybe_get_callable_value( $settings->html );\n\n\t\tif ( $settings->save ) {\n\t\t\t$value = $this->get_value();\n\n\t\t\tif ( ! empty( $value ) && is_string( $value ) ) {\n\t\t\t\t$html = $value;\n\t\t\t}\n\n\t\t\tpapi_render_html_tag( 'input', [\n\t\t\t\t'name'  => esc_attr( $this->html_name() ),\n\t\t\t\t'type'  => 'hidden',\n\t\t\t\t'value' => $html\n\t\t\t] );\n\t\t}\n\n\t\tpapi_render_html_tag( 'div', [\n\t\t\t'data-papi-rule' => esc_attr( $this->html_name() ),\n\t\t\t'class'          => 'property-html',\n\t\t\t$html\n\t\t] );\n\t}\n}\n"
  },
  {
    "path": "src/properties/class-papi-property-image.php",
    "content": "<?php\n\n/**\n * WordPress media image property.\n */\nclass Papi_Property_Image extends Papi_Property_File {\n\n\t/**\n\t * File type.\n\t *\n\t * @var string\n\t */\n\tprotected $file_type  = 'image';\n\n\t/**\n\t * Get labels.\n\t *\n\t * @return array\n\t */\n\tpublic function get_labels() {\n\t\treturn [\n\t\t\t'add'     => __( 'Add image', 'papi' ),\n\t\t\t'no_file' => __( 'No image selected', 'papi' )\n\t\t];\n\t}\n}\n"
  },
  {
    "path": "src/properties/class-papi-property-link.php",
    "content": "<?php\n\n/**\n * WordPress link manager property.\n */\nclass Papi_Property_Link extends Papi_Property {\n\n\t/**\n\t * The convert type.\n\t *\n\t * @var string\n\t */\n\tpublic $convert_type = 'object';\n\n\t/**\n\t * The default value.\n\t *\n\t * @var array\n\t */\n\tpublic $default_value = [];\n\n\t/**\n\t * Link fields.\n\t *\n\t * @var array\n\t */\n\tprotected $link_fields = [\n\t\t'post_id',\n\t\t'url',\n\t\t'title',\n\t\t'target'\n\t];\n\n\t/**\n\t * Delete value from the database.\n\t *\n\t * @param  string $slug\n\t * @param  int    $post_id\n\t * @param  string $type\n\t *\n\t * @return bool\n\t */\n\tpublic function delete_value( $slug, $post_id, $type ) {\n\t\t$values = $this->load_value( null, $slug, $post_id );\n\t\t$values = is_object( $values ) ? (array) $values : $values;\n\t\t$result = true;\n\n\t\tforeach ( array_keys( $values ) as $key ) {\n\t\t\t$out    = papi_data_delete( $post_id, $slug . '_' . $key );\n\t\t\t$result = $out ? $result : $out;\n\t\t}\n\n\t\tif ( $result ) {\n\t\t\t$result = papi_data_delete( $post_id, $slug );\n\t\t}\n\n\t\treturn $result;\n\t}\n\n\t/**\n\t * Format the value of the property before it's returned\n\t * to WordPress admin or the site.\n\t *\n\t * @param  mixed  $value\n\t * @param  string $slug\n\t * @param  int    $post_id\n\t *\n\t * @return mixed\n\t */\n\tpublic function format_value( $value, $slug, $post_id ) {\n\t\treturn (object) $this->prepare_link_array( $value, $slug );\n\t}\n\n\t/**\n\t * Get default settings.\n\t *\n\t * @return array\n\t */\n\tpublic function get_default_settings() {\n\t\treturn [];\n\t}\n\n\t/**\n\t * Load value from database.\n\t *\n\t * @param  mixed  $value\n\t * @param  string $slug\n\t * @param  int    $post_id\n\t *\n\t * @return array\n\t */\n\tpublic function load_value( $value, $slug, $post_id ) {\n\t\tif ( is_array( $value ) || is_object( $value ) ) {\n\t\t\t$values = $value;\n\t\t} else {\n\t\t\t$values = $this->link_fields;\n\n\t\t\tforeach ( $values as $index => $key ) {\n\t\t\t\t$values[$key] = papi_data_get(\n\t\t\t\t\t$post_id,\n\t\t\t\t\tsprintf( '%s_%s', $slug, $key ),\n\t\t\t\t\t$this->get_meta_type()\n\t\t\t\t);\n\t\t\t\tunset( $values[$index] );\n\t\t\t}\n\t\t}\n\n\t\treturn (object) $this->prepare_link_array( $values, $slug );\n\t}\n\n\t/**\n\t * Render property html.\n\t */\n\tpublic function html() {\n\t\t$value  = $this->get_value();\n\t\t$value  = is_array( $value ) || is_object( $value ) ? $value : [];\n\t\t$value  = (object) $value;\n\t\t$exists = ! empty( $value->url );\n\t\t?>\n\t\t<div class=\"papi-property-link\" data-replace-slug=\"true\" data-slug=\"<?php echo esc_attr( $this->html_name() ); ?>\">\n\t\t\t<input type=\"hidden\" name=\"<?php echo esc_attr( $this->html_name() ); ?>\" value=\"<?php echo $exists ? 1 : ''; ?>\">\n\n\t\t\t<?php if ( $exists ): ?>\n\t\t\t\t<table class=\"papi-table link-table\">\n\t\t\t\t\t<tbody>\n\t\t\t\t\t\t<tr>\n\t\t\t\t\t\t\t<td><?php esc_html_e( 'URL', 'papi' ); ?></td>\n\t\t\t\t\t\t\t<td>\n\t\t\t\t\t\t\t\t<a href=\"<?php echo esc_attr( $value->url ); ?>\" target=\"_blank\"><?php echo esc_attr( $value->url ); ?></a>\n\t\t\t\t\t\t\t\t<input type=\"hidden\" value=\"<?php echo esc_attr( $value->title . ' - ' . $value->url ); ?>\" data-papi-rule=\"<?php echo esc_attr( $this->html_name() ); ?>\">\n\t\t\t\t\t\t\t\t<input class=\"wp-link-url\" type=\"hidden\" value=\"<?php echo esc_attr( $value->url ); ?>\" name=\"<?php echo esc_attr( $this->html_name() ); ?>[url]\">\n\t\t\t\t\t\t\t</td>\n\t\t\t\t\t\t</tr>\n\t\t\t\t\t\t<tr>\n\t\t\t\t\t\t\t<td><?php esc_html_e( 'Title', 'papi' ); ?></td>\n\t\t\t\t\t\t\t<td>\n\t\t\t\t\t\t\t\t<?php echo esc_html( $value->title ); ?>\n\t\t\t\t\t\t\t\t<input class=\"wp-link-text\" type=\"hidden\" value=\"<?php echo esc_attr( $value->title ); ?>\" name=\"<?php echo esc_attr( $this->html_name() ); ?>[title]\">\n\t\t\t\t\t\t\t</td>\n\t\t\t\t\t\t</tr>\n\t\t\t\t\t\t<tr>\n\t\t\t\t\t\t\t<td><?php esc_html_e( 'Target', 'papi' ); ?></td>\n\t\t\t\t\t\t\t<td>\n\t\t\t\t\t\t\t\t<?php echo $value->target === '_blank' ? esc_html_e( 'New window', 'papi' ) : esc_html_e( 'Same window', 'papi' ); ?>\n\t\t\t\t\t\t\t\t<input class=\"wp-link-target\" type=\"hidden\" value=\"<?php echo esc_attr( $value->target ); ?>\" name=\"<?php echo esc_attr( $this->html_name() ); ?>[target]\">\n\t\t\t\t\t\t\t</td>\n\t\t\t\t\t\t</tr>\n\t\t\t\t\t</tbody>\n\t\t\t\t</table>\n\t\t\t<?php endif; ?>\n\n\t\t\t<p class=\"papi-file-select\">\n\t\t\t\t<span class=\"<?php echo empty( $value->url ) ? '' : 'papi-hide'; ?>\">\n\t\t\t\t\t<?php esc_html_e( 'No link selected', 'papi' ); ?>\n\t\t\t\t\t<button class=\"button\" data-link-action=\"add\"><?php esc_html_e( 'Add link', 'papi' ); ?></button>\n\t\t\t\t</span>\n\t\t\t\t<span class=\"<?php echo empty( $value->url ) ? 'papi-hide' : ''; ?>\">\n\t\t\t\t\t<button class=\"button\" data-link-action=\"edit\"><?php esc_html_e( 'Edit link', 'papi' ); ?></button>\n\t\t\t\t\t<button class=\"button\" data-link-action=\"remove\"><?php esc_html_e( 'Remove link', 'papi' ); ?></button>\n\t\t\t\t</span>\n\t\t\t</p>\n\t\t</div>\n\t\t<?php\n\t}\n\n\t/**\n\t * Prepare link array with post id. If it gets a post id\n\t * bigger then zero it will use the permalink as url.\n\t *\n\t * @param  array|object $link\n\t * @param  string       $slug\n\t *\n\t * @return array|object\n\t */\n\tprotected function prepare_link_array( $link, $slug ) {\n\t\t$array  = is_array( $link );\n\t\t$values = (array) $link;\n\n\t\tforeach ( $values as $key => $val ) {\n\t\t\tunset( $values[$key] );\n\t\t\t$key = preg_replace( '/^' . $slug . '\\_/', '', $key );\n\t\t\t$values[$key] = $val;\n\t\t}\n\n\t\t$link = (object) $values;\n\n\t\t// Don't continue without a url.\n\t\tif ( ! isset( $link->url ) || empty( $link->url ) ) {\n\t\t\treturn $array ? (array) $link : $link;\n\t\t}\n\n\t\t// Don't overwrite existing post id.\n\t\tif ( ! isset( $link->post_id ) || empty( $link->post_id ) ) {\n\t\t\t$link->post_id = url_to_postid( $link->url );\n\t\t}\n\n\t\t// If empty target set `_self` as default target.\n\t\tif ( empty( $link->target ) ) {\n\t\t\t$link->target = '_self';\n\t\t}\n\n\t\t// Remove slug if it exists.\n\t\tif ( isset( $link->$slug ) && is_numeric( $link->$slug ) ) {\n\t\t\tunset( $link->$slug );\n\t\t}\n\n\t\treturn $array ? (array) $link : $link;\n\t}\n\n\t/**\n\t * Render link template.\n\t */\n\tpublic function render_link_template() {\n\t\t?>\n\t\t<script type=\"text/template\" id=\"tmpl-papi-property-link\">\n\t\t\t<table class=\"papi-table link-table\">\n\t\t\t\t<tbody>\n\t\t\t\t\t<tr>\n\t\t\t\t\t\t<td>\n\t\t\t\t\t\t\t<?php esc_html_e( 'URL', 'papi' ); ?>\n\t\t\t\t\t\t</td>\n\t\t\t\t\t\t<td>\n\t\t\t\t\t\t\t<%= link %>\n\t\t\t\t\t\t\t<input type=\"hidden\" value=\"<%= title %> - <%= href %>\" data-papi-rule=\"<%= slug %>\">\n\t\t\t\t\t\t\t<input class=\"wp-link-url\" type=\"hidden\" value=\"<%= href %>\" name=\"<%= slug %>[url]\">\n\t\t\t\t\t\t</td>\n\t\t\t\t\t</tr>\n\t\t\t\t\t<tr>\n\t\t\t\t\t\t<td>\n\t\t\t\t\t\t\t<?php esc_html_e( 'Title', 'papi' ); ?>\n\t\t\t\t\t\t</td>\n\t\t\t\t\t\t<td>\n\t\t\t\t\t\t\t<%= title %>\n\t\t\t\t\t\t\t<input class=\"wp-link-text\" type=\"hidden\" value=\"<%= title %>\" name=\"<%= slug %>[title]\">\n\t\t\t\t\t\t</td>\n\t\t\t\t\t</tr>\n\t\t\t\t\t<tr>\n\t\t\t\t\t\t<td>\n\t\t\t\t\t\t\t<?php esc_html_e( 'Target', 'papi' ); ?>\n\t\t\t\t\t\t</td>\n\t\t\t\t\t\t<td>\n\t\t\t\t\t\t\t<input class=\"wp-link-target\" type=\"hidden\" value=\"<%= target %>\" name=\"<%= slug %>[target]\">\n\t\t\t\t\t\t\t<%= target === '_blank' ? '<?php esc_html_e( 'New window', 'papi' ); ?>' : '<?php esc_html_e( 'Same window', 'papi' ); ?>' %>\n\t\t\t\t\t\t</td>\n\t\t\t\t\t</tr>\n\t\t\t\t</tbody>\n\t\t\t</table>\n\t\t</script>\n\t\t<?php\n\t}\n\n\t/**\n\t * Prepare value for the database.\n\t *\n\t * @param  mixed  $values\n\t * @param  string $slug\n\t * @param  int    $post_id\n\t *\n\t * @return array\n\t */\n\tpublic function update_value( $values, $slug, $post_id ) {\n\t\tif ( is_object( $values ) ) {\n\t\t\t$values = (array) $values;\n\t\t}\n\n\t\tif ( ! isset( $values['url'] ) ) {\n\t\t\t$values = $this->link_fields;\n\n\t\t\tforeach ( $values as $index => $key ) {\n\t\t\t\t$values[sprintf( '%s_%s', $slug, $key )] = '';\n\t\t\t\tunset( $values[$index] );\n\t\t\t}\n\n\t\t\t// Delete the required field.\n\t\t\t$values[$slug] = '';\n\n\t\t\treturn $values;\n\t\t}\n\n\t\t// If a url exists we can continue making the meta fields\n\t\t// that should be saved.\n\t\t$values = $this->prepare_link_array( $values, $slug );\n\n\t\tforeach ( $values as $key => $val ) {\n\t\t\t$values[sprintf( '%s_%s', $slug, $key )] = $val;\n\t\t\tunset( $values[$key] );\n\t\t}\n\n\t\t// Required field so Papi can load a value from the\n\t\t// original property slug.\n\t\t$values[$slug] = 1;\n\n\t\treturn $values;\n\t}\n\n\t/**\n\t * Setup actions.\n\t */\n\tprotected function setup_actions() {\n\t\tadd_action( 'admin_head', [$this, 'render_link_template'] );\n\t}\n}\n"
  },
  {
    "path": "src/properties/class-papi-property-module.php",
    "content": "<?php\n\n/**\n * Module property that can handle relationship\n * between modules.\n */\nclass Papi_Property_Module extends Papi_Property {\n\n\t/**\n\t * The convert type.\n\t *\n\t * @var string\n\t */\n\tpublic $convert_type = 'object';\n\n\t/**\n\t * Default value.\n\t *\n\t * @var array\n\t */\n\tpublic $default_value = [\n\t\t'module'   => null,\n\t\t'template' => null\n\t];\n\n\t/**\n\t * Format the value of the property before it's returned\n\t * to WordPress admin or the site.\n\t *\n\t * @param  mixed  $value\n\t * @param  string $slug\n\t * @param  int    $post_id\n\t *\n\t * @return array\n\t */\n\tpublic function format_value( $value, $slug, $post_id ) {\n\t\tif ( is_numeric( $value ) && intval( $value ) !== 0 ) {\n\t\t\t$value = [\n\t\t\t\t'module'   => get_post( $value ),\n\t\t\t\t'template' => papi_data_get( $post_id, sprintf( '%s_template', unpapify( $this->html_name() ) ) )\n\t\t\t];\n\n\t\t\tif ( papi_is_admin() ) {\n\t\t\t\treturn (object) $value;\n\t\t\t}\n\n\t\t\t// Return the template value instead of index when not in admin.\n\t\t\tif ( $value['module'] instanceof WP_Post ) {\n\t\t\t\t$templates = $this->get_templates( $value['module']->ID );\n\n\t\t\t\t// Check if index exists.\n\t\t\t\tif ( isset( $templates[$value['template']] ) ) {\n\t\t\t\t\t$value['template'] = $templates[$value['template']];\n\t\t\t\t}\n\n\t\t\t\t// Supports label and template array.\n\t\t\t\tif ( is_array( $value['template'] ) && isset( $value['template']['template'] ) ) {\n\t\t\t\t\t$value['template'] = $value['template']['template'];\n\t\t\t\t}\n\t\t\t}\n\n\t\t\treturn (object) $value;\n\t\t}\n\n\t\treturn (object) $this->default_value;\n\t}\n\n\t/**\n\t * Get default settings.\n\t *\n\t * @return array\n\t */\n\tpublic function get_default_settings() {\n\t\treturn [\n\t\t\t'edit_url'      => true,\n\t\t\t'labels'        => [\n\t\t\t\t'select_template' => __( 'Select Template', 'papi' ),\n\t\t\t\t'select_module'   => __( 'Select Module', 'papi' )\n\t\t\t],\n\t\t\t'new_url'       => true,\n\t\t\t'placeholder'   => '',\n\t\t\t'post_type'     => 'module',\n\t\t\t'select2'       => true,\n\t\t\t'query'         => [],\n\t\t];\n\t}\n\n\t/**\n\t * Get post types.\n\t *\n\t * @return array\n\t */\n\tprotected function get_post_type() {\n\t\t$arr = papi_to_array( $this->get_setting( 'post_type' ) );\n\t\treturn array_shift( $arr );\n\t}\n\n\t/**\n\t * Get posts.\n\t *\n\t * @param  mixed $post_type\n\t *\n\t * @return array\n\t */\n\tprotected function get_posts( $post_type = null ) {\n\t\t$query = $this->get_setting( 'query' );\n\n\t\t// By default we add posts per page key with the value -1 (all).\n\t\tif ( ! isset( $query['posts_per_page'] ) ) {\n\t\t\t$query['posts_per_page'] = -1;\n\t\t}\n\n\t\t// Prepare arguments for WP_Query.\n\t\t$args = array_merge( [\n\t\t\t'post_status'            => 'any',\n\t\t\t'post_type'              => $post_type,\n\t\t\t'no_found_rows'          => true,\n\t\t\t'update_post_meta_cache' => false,\n\t\t\t'update_post_term_cache' => false\n\t\t], $query );\n\n\t\treturn ( new WP_Query( $args ) )->posts;\n\t}\n\n\t/**\n\t * Get module templates by post id.\n\t *\n\t * @param  int $id\n\t *\n\t * @return array\n\t */\n\tprotected function get_templates( $id ) {\n\t\tif ( empty( $id ) && ! is_numeric( $id ) ) {\n\t\t\treturn [];\n\t\t}\n\n\t\tif ( $data = papi_get_entry_type_by_meta_id( $id ) ) {\n\t\t\t$templates = papi_to_array( $data->template );\n\n\t\t\tksort( $templates );\n\n\t\t\treturn $templates;\n\t\t}\n\n\t\treturn [];\n\t}\n\n\t/**\n\t * Render property html.\n\t */\n\tpublic function html() {\n\t\t$layout             = $this->get_setting( 'layout' );\n\t\t$post_type          = $this->get_post_type();\n\t\t$settings           = $this->get_settings();\n\t\t$value              = $this->get_value();\n\t\t$posts              = $this->get_posts( $post_type );\n\t\t$selected_post_id   = is_object( $value ) ? $value->module : 0;\n\t\t$selected_post_id   = is_object( $selected_post_id ) ? $selected_post_id->ID : 0;\n\t\t$templates          = $this->get_templates( $selected_post_id );\n\t\t$selected_template  = is_object( $value ) ? intval( $value->template ) : null;\n\t\t$classes            = '';\n\n\t\tif ( $settings->select2 ) {\n\t\t\t$classes .= ' papi-component-select2';\n\t\t}\n\t\t?>\n\n\t\t<div class=\"papi-property-module papi-property-post advanced\">\n\t\t\t<table class=\"papi-table\">\n\t\t\t\t<tr>\n\t\t\t\t\t<td>\n\t\t\t\t\t\t<label for=\"<?php echo esc_attr( $this->html_id() ); ?>_modules\">\n\t\t\t\t\t\t\t<?php echo esc_html( $settings->labels['select_module'] ); ?>\n\t\t\t\t\t\t</label>\n\t\t\t\t\t</td>\n\t\t\t\t\t<td>\n\n\t\t\t\t\t\t<?php\n\t\t\t\t\t\t\t$placeholder = ! is_null( $settings->placeholder ) ? $settings->placeholder : '';\n\t\t\t\t\t\t?>\n\n\t\t\t\t\t\t<select\n\t\t\t\t\t\t\tclass=\"<?php echo esc_attr( $classes ); ?>  papi-property-module-right\"\n\t\t\t\t\t\t\tid=\"<?php echo esc_attr( $this->html_id() ); ?>_modules\"\n\t\t\t\t\t\t\tname=\"<?php echo esc_attr( $this->html_name() ); ?>\"\n\t\t\t\t\t\t\tdata-allow-clear=\"<?php echo empty( $settings->placeholder ) ? 'false' : 'true'; ?>\"\n\t\t\t\t\t\t\tdata-select-item=\"<?php echo esc_attr( $settings->labels['select_module'] ); ?>\"\n\t\t\t\t\t\t\tdata-placeholder=\"<?php echo esc_attr( $settings->placeholder ); ?>\"\n\t\t\t\t\t\t\tdata-width=\"100%\"\n\t\t\t\t\t\t\t>\n\n\t\t\t\t\t\t\t<?php if ( ! empty( $settings->placeholder ) ): ?>\n\t\t\t\t\t\t\t\t<?php if ( $settings->new_url ): ?>\n\t\t\t\t\t\t\t\t\t<option data-placeholder data-new-url=\"<?php echo esc_attr( admin_url( 'post-new.php?post_type=' . $post_type ) ); ?>\"></option>\n\t\t\t\t\t\t\t\t<?php else: ?>\n\t\t\t\t\t\t\t\t\t<option></option>\n\t\t\t\t\t\t\t\t<?php endif; ?>\n\t\t\t\t\t\t\t<?php endif; ?>\n\n\t\t\t\t\t\t\t<?php\n\t\t\t\t\t\t\tforeach ( $posts as $post ) {\n\t\t\t\t\t\t\t\tif ( papi_is_empty( $post->post_title ) ) {\n\t\t\t\t\t\t\t\t\tcontinue;\n\t\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t\tpapi_render_html_tag( 'option', [\n\t\t\t\t\t\t\t\t\t'data-entry-type' => get_post_meta( $post->ID, papi_get_page_type_key(), true ),\n\t\t\t\t\t\t\t\t\t'data-edit-url'   => get_edit_post_link( $value ),\n\t\t\t\t\t\t\t\t\t'data-new-url'    => $settings->new_url ? admin_url( 'post-new.php?post_type=' . $post->post_type ) : '',\n\t\t\t\t\t\t\t\t\t'selected'        => $selected_post_id === $post->ID,\n\t\t\t\t\t\t\t\t\t'value'           => $post->ID,\n\t\t\t\t\t\t\t\t\t$post->post_title\n\t\t\t\t\t\t\t\t] );\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t?>\n\t\t\t\t\t\t</select>\n\t\t\t\t\t</td>\n\t\t\t\t</tr>\n\t\t\t\t<tr>\n\t\t\t\t\t<td>\n\t\t\t\t\t\t<label for=\"<?php echo esc_attr( $this->html_id() ); ?>_template\">\n\t\t\t\t\t\t\t<?php echo esc_html( $settings->labels['select_template'] ); ?>\n\t\t\t\t\t\t</label>\n\t\t\t\t\t</td>\n\t\t\t\t\t<td>\n\t\t\t\t\t\t<select\n\t\t\t\t\t\t\tid=\"<?php echo esc_attr( $this->html_id() ); ?>_template\"\n\t\t\t\t\t\t\tname=\"<?php echo esc_attr( $this->html_name() ); ?>_template\"\n\t\t\t\t\t\t\tclass=\"<?php echo esc_attr( $classes ); ?> papi-property-module-left\"\n\t\t\t\t\t\t\tdata-post-query='<?php echo esc_attr( papi_maybe_json_encode( $settings->query ) ); ?>'\n\t\t\t\t\t\t\tdata-width=\"100%\"\n\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t<?php\n\t\t\t\t\t\t\tforeach ( $templates as $index => $item ) {\n\t\t\t\t\t\t\t\t// Make string to a array.\n\t\t\t\t\t\t\t\tif ( is_string( $item ) ) {\n\t\t\t\t\t\t\t\t\t$item = [\n\t\t\t\t\t\t\t\t\t\t'label'    => $item,\n\t\t\t\t\t\t\t\t\t\t'template' => $item,\n\t\t\t\t\t\t\t\t\t\t'default'  => false\n\t\t\t\t\t\t\t\t\t];\n\t\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t\t// Bail if no array.\n\t\t\t\t\t\t\t\tif ( ! is_array( $item ) ) {\n\t\t\t\t\t\t\t\t\tcontinue;\n\t\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t\t// Bail if no label or template exists.\n\t\t\t\t\t\t\t\tif ( ! isset( $item['label'], $item['template'] ) ) {\n\t\t\t\t\t\t\t\t\tcontinue;\n\t\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t\t// Set missing default value.\n\t\t\t\t\t\t\t\tif ( ! isset( $item['default'] ) ) {\n\t\t\t\t\t\t\t\t\t$item['default'] = false;\n\t\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t\tpapi_render_html_tag( 'option', [\n\t\t\t\t\t\t\t\t\t'value'    => $index,\n\t\t\t\t\t\t\t\t\t'selected' => ! papi_is_empty( $selected_template ) ? $index === $selected_template : $item['default'],\n\t\t\t\t\t\t\t\t\t$item['label']\n\t\t\t\t\t\t\t\t] );\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t?>\n\t\t\t\t\t\t</select>\n\t\t\t\t\t</td>\n\t\t\t\t</tr>\n\t\t\t</table>\n\t\t</div>\n\t\t<?php\n\t}\n\n\t/**\n\t * Render option template.\n\t */\n\tpublic function render_option_template() {\n\t\t$settings  = $this->get_settings();\n\t\t$post_type = $this->get_post_type();\n\n\t\t?>\n\t\t<script type=\"text/template\" id=\"tmpl-papi-property-module-option\">\n\t\t\t<option\n\t\t\t\tdata-allow-clear=\"<?php echo esc_attr( $settings->allow_clear ); ?>\"\n\t\t\t\tvalue=\"<%= value %>\"\n\t\t\t\t>\n\t\t\t\t<%= title %>\n\t\t\t</option>\n\t\t</script>\n\t\t<script type=\"text/template\" id=\"tmpl-papi-property-module-option-placeholder\">\n\t\t\t<option\n\t\t\t\tdata-placeholder\n\n\t\t\t\t<?php if ( $settings->new_url ): ?>\n\t\t\t\tdata-new-url=\"<?php echo esc_attr( admin_url( 'post-new.php?post_type=' . $post_type ) ); ?>\"\n\t\t\t\t<?php endif; ?>\n\t\t\t\t>\n\t\t\t</option>\n\t\t</script>\n\t\t<?php\n\t}\n\n\t/**\n\t * Setup actions.\n\t */\n\tprotected function setup_actions() {\n\t\tadd_action( 'admin_head', [$this, 'render_option_template'] );\n\t}\n}\n"
  },
  {
    "path": "src/properties/class-papi-property-number.php",
    "content": "<?php\n\n/**\n * HTML5 number input property.\n */\nclass Papi_Property_Number extends Papi_Property {\n\n\t/**\n\t * The convert type.\n\t *\n\t * @var string\n\t */\n\tpublic $convert_type = 'int';\n\n\t/**\n\t * Get convert type.\n\t *\n\t * @return string\n\t */\n\tpublic function get_convert_type() {\n\t\t$value = $this->get_value();\n\n\t\treturn is_float( $value ) ? 'float' : $this->convert_type;\n\t}\n\n\t/**\n\t * Format the value of the property before it's returned\n\t * to WordPress admin or the site.\n\t *\n\t * @param  mixed  $value\n\t * @param  string $slug\n\t * @param  int    $post_id\n\t *\n\t * @return float|int\n\t */\n\tpublic function format_value( $value, $slug, $post_id ) {\n\t\t$value = is_string( $value ) && is_numeric( $value ) ? $value + 0 : $value;\n\n\t\tif ( is_float( $value ) ) {\n\t\t\treturn floatval( $value );\n\t\t} else {\n\t\t\treturn intval( $value );\n\t\t}\n\t}\n\n\t/**\n\t * Get default settings.\n\t *\n\t * @return array\n\t */\n\tpublic function get_default_settings() {\n\t\treturn [\n\t\t\t'max'  => '',\n\t\t\t'min'  => '',\n\t\t\t'step' => 'any',\n\t\t\t'type' => 'number'\n\t\t];\n\t}\n\n\t/**\n\t * Get value from the database.\n\t *\n\t * @return float|int\n\t */\n\tpublic function get_value() {\n\t\treturn $this->format_value(\n\t\t\tparent::get_value(),\n\t\t\t$this->get_slug(),\n\t\t\tpapi_get_post_id()\n\t\t);\n\t}\n\n\t/**\n\t * Render property html.\n\t */\n\tpublic function html() {\n\t\t$settings = $this->get_settings();\n\t\t$value    = $this->get_value();\n\n\t\t// If range type is used change the default values if empty.\n\t\tif ( $settings->type === 'range' ) {\n\t\t\t$settings->max  = papi_is_empty( $settings->max )\n\t\t\t\t? 100 : $settings->max;\n\t\t\t$settings->min  = papi_is_empty( $settings->min )\n\t\t\t\t? 0 : $settings->min;\n\t\t\t$settings->step = papi_is_empty( $settings->step )\n\t\t\t\t? 1 : $settings->step;\n\t\t}\n\n\t\tif ( $settings->min !== 0 && $value < $settings->min ) {\n\t\t\t$value = $settings->min;\n\t\t}\n\n\t\tpapi_render_html_tag( 'input', [\n\t\t\t'id'    => $this->html_id(),\n\t\t\t'max'   => $settings->max,\n\t\t\t'min'   => $settings->min,\n\t\t\t'name'  => esc_attr( $this->html_name() ),\n\t\t\t'step'  => $settings->step,\n\t\t\t'type'  => esc_attr( $settings->type ),\n\t\t\t'value' => $value\n\t\t] );\n\t}\n}\n"
  },
  {
    "path": "src/properties/class-papi-property-post.php",
    "content": "<?php\n\n/**\n * Post property that can handle relationship\n * between posts.\n */\nclass Papi_Property_Post extends Papi_Property {\n\n\t/**\n\t * The convert type.\n\t *\n\t * @var string\n\t */\n\tpublic $convert_type = 'object';\n\n\t/**\n\t * Format the value of the property before it's returned\n\t * to WordPress admin or the site.\n\t *\n\t * @param  mixed  $value\n\t * @param  string $slug\n\t * @param  int    $post_id\n\t *\n\t * @return array\n\t */\n\tpublic function format_value( $value, $slug, $post_id ) {\n\t\t$meta_key = $this->get_setting( 'meta_key' );\n\n\t\tif ( ! empty( $meta_key ) ) {\n\t\t\t$args = [\n\t\t\t\t'fields'         => 'ids',\n\t\t\t\t'meta_key'       => $meta_key,\n\t\t\t\t'meta_value'     => $value,\n\t\t\t\t'posts_per_page' => 1,\n\t\t\t\t'post_type'      => $this->get_setting( 'post_type' ),\n\t\t\t];\n\n\t\t\t$query = new WP_Query( $args );\n\n\t\t\tif ( ! empty( $query->posts ) ) {\n\t\t\t\t$value = $query->posts[0];\n\t\t\t}\n\t\t}\n\n\t\t$post = $this->default_value;\n\n\t\t// Allow only id to be returned.\n\t\tif ( ! papi_is_admin() && $this->get_setting( 'fields' ) === 'ids' ) {\n\t\t\t$post = $this->get_post_value( $value );\n\t\t} else if ( ! empty( $value ) ) {\n\t\t\t$post = get_post( $value );\n\t\t}\n\n\t\tif ( empty( $post ) ) {\n\t\t\treturn $this->default_value;\n\t\t}\n\n\t\tif ( is_object( $post ) && empty( $post->ID ) ) {\n\t\t\treturn $this->default_value;\n\t\t}\n\n\t\treturn $post;\n\t}\n\n\t/**\n\t * Get default settings.\n\t *\n\t * @return array\n\t */\n\tpublic function get_default_settings() {\n\t\treturn [\n\t\t\t'allow_clear'   => true,\n\t\t\t'edit_url'      => true,\n\t\t\t'fields'        => '',\n\t\t\t'labels'        => [\n\t\t\t\t'select_post_type' => __( 'Select Post Type', 'papi' ),\n\t\t\t\t'select_item'      => __( 'Select %s', 'papi' )\n\t\t\t],\n\t\t\t'layout'        => 'single', // single or advanced\n\t\t\t'meta_key'      => '',\n\t\t\t'new_url'       => true,\n\t\t\t'placeholder'   => null,\n\t\t\t'post_type'     => 'post',\n\t\t\t'select2'       => true,\n\t\t\t'query'         => []\n\t\t];\n\t}\n\n\t/**\n\t * Get labels from post types.\n\t *\n\t * @return array\n\t */\n\tprotected function get_labels() {\n\t\t$results = [];\n\n\t\tforeach ( $this->get_post_types() as $post_type ) {\n\t\t\tif ( post_type_exists( $post_type ) ) {\n\t\t\t\t$post_type_object    = get_post_type_object( $post_type );\n\t\t\t\t$results[$post_type] = $post_type_object->labels->menu_name;\n\t\t\t}\n\t\t}\n\n\t\treturn $results;\n\t}\n\n\t/**\n\t * Get post types.\n\t *\n\t * @return array\n\t */\n\tprotected function get_post_types() {\n\t\treturn papi_to_array( $this->get_setting( 'post_type' ) );\n\t}\n\n\t/**\n\t * Get posts.\n\t *\n\t * @param  string $post_type\n\t *\n\t * @return array\n\t */\n\tprotected function get_posts( $post_type = '' ) {\n\t\t$query  = $this->get_setting( 'query' );\n\t\t$layout = $this->get_setting( 'layout' );\n\n\t\t// By default we add posts per page key with the value -1 (all).\n\t\tif ( ! isset( $query['posts_per_page'] ) ) {\n\t\t\t$query['posts_per_page'] = -1;\n\t\t}\n\n\t\tif ( $layout !== 'advanced' ) {\n\t\t\t$post_type = $this->get_post_types();\n\t\t} else if ( empty( $post_type ) ) {\n\t\t\t$post_type = $this->get_post_types();\n\t\t\t$post_type = array_shift( $post_type );\n\t\t}\n\n\t\t// Prepare arguments for WP_Query.\n\t\t$args = array_merge( [\n\t\t\t'post_status'            => 'any',\n\t\t\t'post_type'              => $post_type,\n\t\t\t'no_found_rows'          => true,\n\t\t\t'update_post_meta_cache' => false,\n\t\t\t'update_post_term_cache' => false\n\t\t], $query );\n\n\t\t$posts = ( new WP_Query( $args ) )->posts;\n\n\t\t// Keep only objects.\n\t\t$posts   = papi_get_only_objects( $posts );\n\t\t$results = [];\n\n\t\tforeach ( $posts as $post ) {\n\t\t\t$obj = get_post_type_object( $post->post_type );\n\n\t\t\tif ( empty( $obj ) ) {\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tif ( ! isset( $results[$obj->labels->menu_name] ) ) {\n\t\t\t\t$results[$obj->labels->menu_name] = [];\n\t\t\t}\n\n\t\t\t$results[$obj->labels->menu_name][] = $post;\n\t\t}\n\n\t\tksort( $results );\n\n\t\treturn $results;\n\t}\n\n\t/**\n\t * Get matching value based on key from a post.\n\t *\n\t * @param  mixed $post\n\t *\n\t * @return mixed\n\t */\n\tprotected function get_post_value( $post ) {\n\t\t$meta_key = $this->get_setting( 'meta_key' );\n\n\t\tif ( is_numeric( $post ) ) {\n\t\t\t$post_id = $post;\n\t\t} else if ( ! empty( $post ) ) {\n\t\t\t$post = get_post( $post );\n\t\t\tif ( $post instanceof WP_Post === false ) {\n\t\t\t\treturn 0;\n\t\t\t}\n\n\t\t\t$post_id = $post->ID;\n\t\t}\n\n\t\tif ( empty( $post_id ) ) {\n\t\t\treturn 0;\n\t\t}\n\n\t\tif ( ! empty( $meta_key ) ) {\n\t\t\t$value = get_post_meta( $post_id, $meta_key, true );\n\t\t} else {\n\t\t\t$value = $post_id;\n\t\t}\n\n\t\treturn $value;\n\t}\n\n\t/**\n\t * Render property html.\n\t */\n\tpublic function html() {\n\t\t$settings           = $this->get_settings();\n\t\t$layout             = $settings->layout;\n\t\t$labels             = $this->get_labels();\n\t\t$post_types         = $this->get_post_types();\n\t\t$render_label       = count( $post_types ) > 1;\n\t\t$advanced           = $render_label && $layout === 'advanced';\n\t\t$single             = $render_label && $layout !== 'advanced';\n\t\t$classes            = count( $post_types ) > 1 ? '' : 'papi-fullwidth';\n\t\t$value              = $this->get_value();\n\t\t$value              = $this->get_post_value( $value );\n\t\t$selected_label     = is_array( $labels ) && ! empty( $labels ) ? array_values( $labels )[0] : '';\n\t\t$selected_post_type = empty( $value ) ? '' : get_post_type( $value );\n\t\t$selected_post_type = empty( $selected_post_type ) ? '' : $selected_post_type;\n\t\t$posts              = $this->get_posts( $selected_post_type );\n\n\t\tif ( $settings->select2 ) {\n\t\t\t$classes .= ' papi-component-select2';\n\t\t}\n\n\t\t// When new url is true and we have more than one post type\n\t\t// we need to use the advanced layout so the placeholder option\n\t\t// will get the right post type in the new url.\n\t\tif ( $settings->new_url && $render_label ) {\n\t\t\t$advanced = true;\n\t\t\t$single = false;\n\t\t}\n\t\t?>\n\n\t\t<div class=\"papi-property-post <?php echo $advanced ? 'advanced' : ''; ?>\">\n\t\t\t<?php if ( $advanced ): ?>\n\t\t\t<table class=\"papi-table\">\n\t\t\t\t<tr>\n\t\t\t\t\t<td>\n\t\t\t\t\t\t<label for=\"<?php echo esc_attr( $this->html_id() ); ?>_post_type\">\n\t\t\t\t\t\t\t<?php echo esc_html( $settings->labels['select_post_type'] ); ?>\n\t\t\t\t\t\t</label>\n\t\t\t\t\t</td>\n\t\t\t\t\t<td>\n\t\t\t\t\t\t<select\n\t\t\t\t\t\t\tid=\"<?php echo esc_attr( $this->html_id() ); ?>_post_type\"\n\t\t\t\t\t\t\tclass=\"<?php echo esc_attr( $classes ); ?> papi-property-post-left\"\n\t\t\t\t\t\t\tdata-select-item=\"<?php echo esc_attr( $settings->labels['select_item'] ); ?>\"\n\t\t\t\t\t\t\tdata-post-query='<?php echo esc_attr( papi_maybe_json_encode( $settings->query ) ); ?>'\n\t\t\t\t\t\t\tdata-width=\"100%\"\n\t\t\t\t\t\t>\n\t\t\t\t\t\t\t<?php\n\t\t\t\t\t\t\tforeach ( $labels as $post_type => $label ) {\n\t\t\t\t\t\t\t\t$selected = $post_type === $selected_post_type;\n\n\t\t\t\t\t\t\t\tpapi_render_html_tag( 'option', [\n\t\t\t\t\t\t\t\t\t'value'    => $post_type,\n\t\t\t\t\t\t\t\t\t'selected' => $selected,\n\t\t\t\t\t\t\t\t\t$label\n\t\t\t\t\t\t\t\t] );\n\n\t\t\t\t\t\t\t\tif ( $selected ) {\n\t\t\t\t\t\t\t\t\t$selected_label = $label;\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t?>\n\t\t\t\t\t\t</select>\n\t\t\t\t\t</td>\n\t\t\t\t</tr>\n\t\t\t\t<tr>\n\t\t\t\t\t<td>\n\t\t\t\t\t\t<label for=\"<?php echo esc_attr( $this->html_id() ); ?>_posts\">\n\t\t\t\t\t\t\t<?php echo esc_html( sprintf( $settings->labels['select_item'], $selected_label ) ); ?>\n\t\t\t\t\t\t</label>\n\t\t\t\t\t</td>\n\t\t\t\t\t<td>\n\t\t\t\t\t\t<?php endif; ?>\n\n\t\t\t\t\t\t<?php\n\t\t\t\t\t\t\t$placeholder = ! is_null( $settings->placeholder ) ? $settings->placeholder : '';\n\t\t\t\t\t\t?>\n\n\t\t\t\t\t\t<select\n\t\t\t\t\t\t\tclass=\"<?php echo esc_attr( $classes ); ?>  papi-property-post-right\"\n\t\t\t\t\t\t\tid=\"<?php echo esc_attr( $this->html_id() ); ?>_posts\"\n\t\t\t\t\t\t\tname=\"<?php echo esc_attr( $this->html_name() ); ?>\"\n\t\t\t\t\t\t\tdata-allow-clear=\"<?php echo is_null( $settings->placeholder ) ? 'false' : 'true'; ?>\"\n\t\t\t\t\t\t\tdata-placeholder=\"<?php echo esc_attr( $placeholder ); ?>\"\n\t\t\t\t\t\t\tdata-width=\"100%\"\n\t\t\t\t\t\t>\n\n\t\t\t\t\t\t\t<?php if ( ! empty( $settings->placeholder ) ): ?>\n\t\t\t\t\t\t\t\t<?php if ( $settings->new_url ): ?>\n\t\t\t\t\t\t\t\t\t<option data-placeholder data-new-url=\"<?php echo esc_attr( admin_url( 'post-new.php?post_type=' . $post_types[0] ) ); ?>\"></option>\n\t\t\t\t\t\t\t\t<?php else: ?>\n\t\t\t\t\t\t\t\t\t<option></option>\n\t\t\t\t\t\t\t\t<?php endif; ?>\n\t\t\t\t\t\t\t<?php endif; ?>\n\n\t\t\t\t\t\t\t<?php foreach ( $posts as $label => $items ) : ?>\n\n\t\t\t\t\t\t\t\t<?php if ( $single ): ?>\n\t\t\t\t\t\t\t\t\t<optgroup label=\"<?php echo esc_attr( $label ); ?>\">\n\t\t\t\t\t\t\t\t<?php endif; ?>\n\n\t\t\t\t\t\t\t\t<?php\n\t\t\t\t\t\t\t\tforeach ( $items as $post ) {\n\t\t\t\t\t\t\t\t\tif ( papi_is_empty( $post->post_title ) ) {\n\t\t\t\t\t\t\t\t\t\tcontinue;\n\t\t\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t\t\tpapi_render_html_tag( 'option', [\n\t\t\t\t\t\t\t\t\t\t'data-allow-clear' => $settings->allow_clear,\n\t\t\t\t\t\t\t\t\t\t'data-edit-url'    => $settings->edit_url ? get_edit_post_link( $post ) : '',\n\t\t\t\t\t\t\t\t\t\t'data-new-url'     => $settings->new_url ? admin_url( 'post-new.php?post_type=' . $post->post_type ) : '',\n\t\t\t\t\t\t\t\t\t\t'selected'         => $value === $this->get_post_value( $post ),\n\t\t\t\t\t\t\t\t\t\t'value'            => $this->get_post_value( $post ),\n\t\t\t\t\t\t\t\t\t\t$post->post_title\n\t\t\t\t\t\t\t\t\t] );\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t?>\n\n\t\t\t\t\t\t\t\t<?php if ( $single ): ?>\n\t\t\t\t\t\t\t\t\t</optgroup>\n\t\t\t\t\t\t\t\t<?php endif; ?>\n\n\t\t\t\t\t\t\t<?php endforeach; ?>\n\t\t\t\t\t\t</select>\n\n\t\t\t\t\t\t<?php if ( $advanced ): ?>\n\t\t\t\t\t</td>\n\t\t\t\t</tr>\n\t\t\t</table>\n\t\t<?php endif; ?>\n\n\t\t</div>\n\t\t<?php\n\t}\n\n\t/**\n\t * Render option template.\n\t */\n\tpublic function render_option_template() {\n\t\t$settings   = $this->get_settings();\n\t\t$post_types = $this->get_post_types();\n\n\t\t?>\n\t\t<script type=\"text/template\" id=\"tmpl-papi-property-post-option\">\n\t\t\t<option\n\t\t\t\tdata-allow-clear=\"<?php echo esc_attr( $settings->allow_clear ); ?>\"\n\n\t\t\t\t<?php if ( $settings->edit_url ): ?>\n\t\t\t\tdata-edit-url=\"<?php echo esc_attr( admin_url( 'post.php' ) ); ?>?post=<%= id %>&action=edit\"\n\t\t\t\t<?php endif; ?>\n\n\t\t\t\t<?php if ( $settings->new_url ): ?>\n\t\t\t\tdata-new-url=\"<?php echo esc_attr( admin_url( 'post-new.php?post_type=' ) ); ?><%= typeof type !== 'undefined' ? type : '<?php echo esc_attr( $post_types[0] ); ?>' %>\"\n\t\t\t\t<?php endif; ?>\n\n\t\t\t\tvalue=\"<%= id %>\"\n\t\t\t\t>\n\t\t\t\t<%= title %>\n\t\t\t</option>\n\t\t</script>\n\t\t<script type=\"text/template\" id=\"tmpl-papi-property-post-option-placeholder\">\n\t\t\t<option\n\t\t\t\tdata-placeholder\n\n\t\t\t\t<?php if ( $settings->new_url ): ?>\n\t\t\t\tdata-new-url=\"<?php echo esc_attr( admin_url( 'post-new.php?post_type=' ) ); ?><%= typeof type !== 'undefined' ? type : '<?php echo esc_attr( $post_types[0] ); ?>' %>\"\n\t\t\t\t<?php endif; ?>\n\t\t\t\t>\n\t\t\t</option>\n\t\t</script>\n\t\t<?php\n\t}\n\n\t/**\n\t * Setup actions.\n\t */\n\tprotected function setup_actions() {\n\t\tadd_action( 'admin_head', [$this, 'render_option_template'] );\n\t}\n}\n"
  },
  {
    "path": "src/properties/class-papi-property-radio.php",
    "content": "<?php\n\n/**\n * HTML radio buttons property that can handle one or\n * more radio buttons.\n */\nclass Papi_Property_Radio extends Papi_Property {\n\n\t/**\n\t * Format the value of the property before it's returned\n\t * to WordPress admin or the site.\n\t *\n\t * @param  mixed  $value\n\t * @param  string $slug\n\t * @param  int    $post_id\n\t *\n\t * @return mixed\n\t */\n\tpublic function format_value( $value, $slug, $post_id ) {\n\t\treturn papi_cast_string_value( $value );\n\t}\n\n\t/**\n\t * Get default settings.\n\t *\n\t * @return array\n\t */\n\tpublic function get_default_settings() {\n\t\treturn [\n\t\t\t'items'    => [],\n\t\t\t'selected' => ''\n\t\t];\n\t}\n\n\t/**\n\t * Render property html.\n\t */\n\tpublic function html() {\n\t\t$settings = $this->get_settings();\n\t\t$value    = papi_cast_string_value( $this->get_value() );\n\n\t\t// Override selected setting with\n\t\t// database value if not null.\n\t\tif ( ! papi_is_empty( $value ) ) {\n\t\t\t$settings->selected = $value;\n\t\t}\n\n\t\techo '<div class=\"papi-property-radio\">';\n\n\t\tforeach ( $settings->items as $key => $value ) {\n\t\t\t$key = is_numeric( $key ) ? $value : $key;\n\n\t\t\tpapi_render_html_tag( 'p', [\n\t\t\t\tpapi_html_tag( 'label', [\n\t\t\t\t\t'class' => 'light',\n\t\t\t\t\t'for'   => esc_attr( $this->html_id( $key ) ),\n\n\t\t\t\t\tpapi_html_tag( 'input', [\n\t\t\t\t\t\t'id'      => esc_attr( $this->html_id( $key ) ),\n\t\t\t\t\t\t'name'    => esc_attr( $this->html_name() ),\n\t\t\t\t\t\t'type'    => 'radio',\n\t\t\t\t\t\t'checked' => $value === $settings->selected,\n\t\t\t\t\t\t'value'   => $value\n\t\t\t\t\t] ),\n\n\t\t\t\t\tesc_html( papi_convert_to_string( $key ) )\n\t\t\t\t] )\n\t\t\t] );\n\t\t}\n\n\t\techo '</div>';\n\t}\n}\n"
  },
  {
    "path": "src/properties/class-papi-property-reference.php",
    "content": "<?php\n\n/**\n * Property that shows which relationships that exists\n * between the current post and other posts.\n */\nclass Papi_Property_Reference extends Papi_Property {\n\n\t/**\n\t * Get default settings.\n\t *\n\t * @return array\n\t */\n\tpublic function get_default_settings() {\n\t\treturn [\n\t\t\t'slug'      => [],\n\t\t\t'page_type' => []\n\t\t];\n\t}\n\n\t/**\n\t * Render property html.\n\t */\n\tpublic function html() {\n\t\t$post_id  = papi_get_post_id();\n\t\t$settings = $this->get_settings();\n\n\t\t// Create query array for every page type.\n\t\t$page_types = array_map( function ( $page_type ) {\n\t\t\treturn [\n\t\t\t\t'key'     => papi_get_page_type_key(),\n\t\t\t\t'value'   => $page_type,\n\t\t\t\t'compare' => 'LIKE'\n\t\t\t];\n\t\t}, papi_to_array( $settings->page_type ) );\n\n\t\t// Add relation.\n\t\t$page_types['relation'] = 'OR';\n\n\t\t// Prepare arguments for WP_Query.\n\t\t$args = [\n\t\t\t'post_type'              => 'any',\n\t\t\t'no_found_rows'          => true,\n\t\t\t'update_post_meta_cache' => false,\n\t\t\t'update_post_term_cache' => false,\n\t\t\t'meta_query'             => $page_types\n\t\t];\n\n\t\t$posts = ( new WP_Query( $args ) )->posts;\n\n\t\t$values = [];\n\n\t\tforeach ( papi_to_array( $settings->slug ) as $slug ) {\n\t\t\tforeach ( $posts as $post ) {\n\t\t\t\t$val = papi_get_field( $post->ID, $slug );\n\n\t\t\t\t$val = array_filter( papi_to_array( $val ), function ( $item ) use ( $post_id ) {\n\t\t\t\t\treturn is_object( $item ) && $item->ID === $post_id;\n\t\t\t\t} );\n\n\t\t\t\tif ( empty( $val ) ) {\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\n\t\t\t\t$page_type = papi_get_entry_type_by_meta_id( $post->ID );\n\n\t\t\t\tif ( empty( $page_type ) ) {\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\n\t\t\t\t// Create the array\n\t\t\t\tif ( ! isset( $values[$post->post_type] ) ) {\n\t\t\t\t\t$values[$post->post_type] = [];\n\t\t\t\t}\n\n\t\t\t\tif ( ! isset( $values[$post->post_type][$page_type->name] ) ) {\n\t\t\t\t\t$values[$post->post_type][$page_type->name] = [];\n\t\t\t\t}\n\n\t\t\t\t// Add the post\n\t\t\t\tif ( ! isset( $values[$post->post_type][$page_type->name][$post->ID] ) && ! empty( $val ) ) {\n\t\t\t\t\t$values[$post->post_type][$page_type->name][$post->ID] = $post;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t?>\n\t\t<ul class=\"papi-property-reference\" data-papi-rule=\"<?php echo esc_attr( $this->html_name() ); ?>\">\n\t\t\t<?php if ( empty( $values ) ): ?>\n\t\t\t\t<p>\n\t\t\t\t\t<?php esc_html_e( 'No references exists', 'papi' ); ?>\n\t\t\t\t</p>\n\t\t\t<?php\n\t\t\tendif;\n\t\t\tksort( $values ); foreach ( $values as $title => $val ):\n\t\t\t\t$post_type = get_post_type_object( $title );\n\t\t\t\t?>\n\t\t\t\t<li>\n\t\t\t\t\t<h3><?php echo esc_html( $post_type->labels->name ); ?></h3>\n\t\t\t\t\t<div class=\"handlediv\" title=\"Click to toggle\"><br></div>\n\t\t\t\t</li>\n\t\t\t\t<li>\n\t\t\t\t\t<div class=\"page-types\">\n\t\t\t\t\t\t<ul>\n\t\t\t\t\t\t\t<?php ksort( $val ); foreach ( $val as $name => $posts ): ?>\n\t\t\t\t\t\t\t\t<li class=\"heading-border\">\n\t\t\t\t\t\t\t\t\t<h4><?php echo esc_html( $name ); ?></h4>\n\t\t\t\t\t\t\t\t\t<div class=\"handlediv\" title=\"Click to toggle\"><br></div>\n\t\t\t\t\t\t\t\t</li>\n\t\t\t\t\t\t\t\t<li>\n\t\t\t\t\t\t\t\t\t<div class=\"box\">\n\t\t\t\t\t\t\t\t\t\t<?php $i = 0;\n\t\t\t\t\t\t\t\t\t\tforeach ( $posts as $post ): ?>\n\t\t\t\t\t\t\t\t\t\t\t<a href=\"<?php echo esc_attr( get_edit_post_link( $post->ID ) ); ?>\"><?php echo esc_html( $post->post_title ); ?></a>\n\t\t\t\t\t\t\t\t\t\t<?php $i++;\n\t\t\t\t\t\t\t\t\t\tendforeach; ?>\n\t\t\t\t\t\t\t\t\t\t<div class=\"clear\"></div>\n\t\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t\t</li>\n\t\t\t\t\t\t\t<?php endforeach; ?>\n\t\t\t\t\t\t</ul>\n\t\t\t\t\t\t<div class=\"clear\"></div>\n\t\t\t\t\t</div>\n\t\t\t\t</li>\n\t\t\t<?php endforeach; ?>\n\t\t</ul>\n\t\t<?php\n\t}\n}\n"
  },
  {
    "path": "src/properties/class-papi-property-relationship.php",
    "content": "<?php\n\n/**\n * Relationship property that can handle more than\n * one relationship between posts or other data items.\n */\nclass Papi_Property_Relationship extends Papi_Property {\n\n\t/**\n\t * The convert type.\n\t *\n\t * @var string\n\t */\n\tpublic $convert_type = 'array';\n\n\t/**\n\t * The default value.\n\t *\n\t * @var array\n\t */\n\tpublic $default_value = [];\n\n\t/**\n\t * Convert WordPress post object to a item object.\n\t *\n\t * @param  WP_Post $post\n\t *\n\t * @return object\n\t */\n\tprotected function convert_post_to_item( WP_Post $post ) {\n\t\treturn (object) [\n\t\t\t'id'    => $this->get_post_value( $post ),\n\t\t\t'title' => $post->post_title\n\t\t];\n\t}\n\n\t/**\n\t * Format the value of the property before it's returned\n\t * to WordPress admin or the site.\n\t *\n\t * @param  mixed  $values\n\t * @param  string $slug\n\t * @param  int    $post_id\n\t *\n\t * @return array\n\t */\n\tpublic function format_value( $values, $slug, $post_id ) {\n\t\tif ( is_array( $values ) || is_object( $values ) ) {\n\t\t\t$items  = $this->get_setting( 'items' );\n\t\t\t$result = [];\n\n\t\t\tforeach ( $values as $id ) {\n\t\t\t\t// Backwards compatibility with array `id` and `id`.\n\t\t\t\t$id = is_object( $id ) ? $id->id : $id;\n\t\t\t\t$id = is_array( $id ) ? $id['id'] : $id;\n\n\t\t\t\tif ( empty( $id ) ) {\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\n\t\t\t\tif ( papi_is_empty( $items ) ) {\n\t\t\t\t\tif ( empty( $this->get_setting( 'meta_key' ) ) ) {\n\t\t\t\t\t\t$post = get_post( $id );\n\t\t\t\t\t} else {\n\t\t\t\t\t\t$args = [\n\t\t\t\t\t\t\t'fields'         => 'ids',\n\t\t\t\t\t\t\t'meta_key'       => $this->get_setting( 'meta_key' ),\n\t\t\t\t\t\t\t'meta_value'     => $id,\n\t\t\t\t\t\t\t'posts_per_page' => 1,\n\t\t\t\t\t\t\t'post_type'      => $this->get_setting( 'post_type' ),\n\t\t\t\t\t\t];\n\n\t\t\t\t\t\t$query = new WP_Query( $args );\n\n\t\t\t\t\t\tif ( ! empty( $query->posts ) ) {\n\t\t\t\t\t\t\t$post = get_post( $query->posts[0] );\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\n\t\t\t\t\tif ( empty( $post ) ) {\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\n\t\t\t\t\t$result[] = $post;\n\t\t\t\t} else {\n\t\t\t\t\t$id   = (int) $id;\n\t\t\t\t\t$item = null;\n\n\t\t\t\t\tforeach ( (array) $items as $value ) {\n\t\t\t\t\t\t$ids = wp_list_pluck( [$value], 'id' );\n\t\t\t\t\t\t$ids = count( $ids ) > 0 ? strval( $ids[0] ) : '';\n\n\t\t\t\t\t\tif ( $ids === (string) $id ) {\n\t\t\t\t\t\t\t$item = $value;\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\n\t\t\t\t\tif ( is_array( $item ) || is_object( $item ) ) {\n\t\t\t\t\t\t$result[] = papi_maybe_convert_to_object( $item );\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t$values = $this->sort_value( $result, $slug, $post_id );\n\n\t\t\t// Allow only id to be returned.\n\t\t\tif ( ! papi_is_admin() && $this->get_setting( 'fields' ) === 'ids' ) {\n\t\t\t\treturn array_map( function ( $item ) {\n\t\t\t\t\tif ( $id = $this->get_post_value( $item ) ) {\n\t\t\t\t\t\treturn $id;\n\t\t\t\t\t}\n\n\t\t\t\t\t$id = $item->ID;\n\n\t\t\t\t\tif ( is_numeric( $id ) ) {\n\t\t\t\t\t\treturn $id == (int) $id ? (int) $id : (float) $id; // loose comparison\n\t\t\t\t\t}\n\n\t\t\t\t\treturn $id;\n\t\t\t\t}, $values );\n\t\t\t}\n\n\t\t\treturn $values;\n\t\t}\n\n\t\treturn $this->default_value;\n\t}\n\n\t/**\n\t * Get default settings.\n\t *\n\t * @return array\n\t */\n\tpublic function get_default_settings() {\n\t\treturn [\n\t\t\t'fields'       => '',\n\t\t\t'items'        => [],\n\t\t\t'limit'        => -1,\n\t\t\t'meta_key'     => '',\n\t\t\t'only_once'    => false,\n\t\t\t'post_type'    => 'page',\n\t\t\t'query'        => [],\n\t\t\t'show_sort_by' => true,\n\t\t\t'title'        => __( 'Post', 'papi' )\n\t\t];\n\t}\n\n\t/**\n\t * Get sort option value.\n\t *\n\t * @param  int $post_id\n\t *\n\t * @return string\n\t */\n\tpublic function get_sort_option( $post_id ) {\n\t\t$slug = $this->html_id( 'sort_option' );\n\t\t$slug = str_replace( '][', '_', $slug );\n\t\t$slug = str_replace( '[', '_', $slug );\n\t\t$slug = str_replace( ']', '', $slug );\n\n\t\treturn papi_data_get( $post_id, $slug, $this->get_meta_type() );\n\t}\n\n\t/**\n\t * Get sort options for relationship property.\n\t *\n\t * @return array\n\t */\n\tpublic static function get_sort_options() {\n\t\t$sort_options = [];\n\n\t\t$sort_options[__( 'Select', 'papi' )] = null;\n\n\t\t$sort_options[__( 'Name (alphabetically)', 'papi' )] = function ( $a, $b ) {\n\t\t\t// Backwards compatibility with both `post_title` and `title`.\n\t\t\treturn strcmp(\n\t\t\t\tstrtolower( isset( $a->post_title ) ? $a->post_title : $a->title ),\n\t\t\t\tstrtolower( isset( $b->post_title ) ? $b->post_title : $b->title )\n\t\t\t);\n\t\t};\n\n\t\t$sort_options[__( 'Post created date (ascending)', 'papi' )] = function ( $a, $b ) {\n\t\t\treturn strtotime( $a->post_date ) > strtotime( $b->post_date );\n\t\t};\n\n\t\t$sort_options[__( 'Post created date (descending)', 'papi' )] = function ( $a, $b ) {\n\t\t\treturn strtotime( $a->post_date ) < strtotime( $b->post_date );\n\t\t};\n\n\t\t$sort_options[__( 'Post id (ascending)', 'papi' )] = function ( $a, $b ) {\n\t\t\t// Backwards compatibility with both `ID` and `id`.\n\t\t\treturn isset( $a->ID ) ? $a->ID > $b->ID : $a->id > $b->id;\n\t\t};\n\n\t\t$sort_options[__( 'Post id (descending)', 'papi' )] = function ( $a, $b ) {\n\t\t\t// Backwards compatibility with both `ID` and `id`.\n\t\t\treturn isset( $a->ID ) ? $a->ID < $b->ID : $a->id < $b->id;\n\t\t};\n\n\t\t$sort_options[__( 'Post order value (ascending)', 'papi' )] = function ( $a, $b ) {\n\t\t\treturn $a->menu_order > $b->menu_order;\n\t\t};\n\n\t\t$sort_options[__( 'Post order value (descending)', 'papi' )] = function ( $a, $b ) {\n\t\t\treturn $a->menu_order < $b->menu_order;\n\t\t};\n\n\t\t$sort_options[__( 'Post modified date (ascending)', 'papi' )] = function ( $a, $b ) {\n\t\t\treturn strtotime( $a->post_modified ) > strtotime( $b->post_modified );\n\t\t};\n\n\t\t$sort_options[__( 'Post modified date (descending)', 'papi' )] = function ( $a, $b ) {\n\t\t\treturn strtotime( $a->post_modified ) < strtotime( $b->post_modified );\n\t\t};\n\n\t\treturn apply_filters( 'papi/property/relationship/sort_options', $sort_options );\n\t}\n\n\t/**\n\t * Get items to display from settings.\n\t *\n\t * @param  stdClass $settings\n\t *\n\t * @return array\n\t */\n\tprotected function get_items( $settings ) {\n\t\tif ( is_array( $settings->items ) && ! empty( $settings->items ) ) {\n\t\t\t$mapping = function ( $item ) {\n\t\t\t\treturn is_array( $item ) ?\n\t\t\t\t\tisset( $item['id'], $item['title'] ) :\n\t\t\t\t\tisset( $item->id, $item->title );\n\t\t\t};\n\n\t\t\treturn array_map(\n\t\t\t\t'papi_maybe_convert_to_object',\n\t\t\t\tarray_filter( $settings->items, $mapping )\n\t\t\t);\n\t\t}\n\n\t\t// By default we add posts per page key with the value -1 (all).\n\t\tif ( ! isset( $settings->query['posts_per_page'] ) ) {\n\t\t\t$settings->query['posts_per_page'] = -1;\n\t\t}\n\n\t\t// Prepare arguments for WP_Query.\n\t\t$args = array_merge( [\n\t\t\t'post_status'            => 'any',\n\t\t\t'post_type'              => papi_to_array( $settings->post_type ),\n\t\t\t'no_found_rows'          => true,\n\t\t\t'update_post_meta_cache' => false,\n\t\t\t'update_post_term_cache' => false\n\t\t], $settings->query );\n\n\t\t$items = ( new WP_Query( $args ) )->posts;\n\n\t\treturn array_map( [$this, 'convert_post_to_item'], papi_get_only_objects( $items ) );\n\t}\n\n\t/**\n\t * Get matching value based on key from a post.\n\t *\n\t * @param  mixed $value\n\t *\n\t * @return mixed\n\t */\n\tprotected function get_post_value( $value ) {\n\t\t$meta_key = $this->get_setting( 'meta_key' );\n\n\t\tif ( $value instanceof WP_Post === false ) {\n\t\t\treturn 0;\n\t\t}\n\n\t\tif ( empty( $meta_key ) ) {\n\t\t\treturn $value->ID;\n\t\t}\n\n\t\tif ( $value = get_post_meta( $value->ID, $meta_key, true ) ) {\n\t\t\treturn $value;\n\t\t}\n\n\t\treturn 0;\n\t}\n\n\t/**\n\t * Render property html.\n\t */\n\tpublic function html() {\n\t\t$post_id       = papi_get_post_id();\n\t\t$slug          = $this->html_name();\n\t\t$settings      = $this->get_settings();\n\t\t$settings_json = [];\n\t\t$sort_option   = $this->get_sort_option( $post_id );\n\t\t$sort_options  = static::get_sort_options();\n\t\t$values        = papi_get_only_objects( $this->get_value() );\n\t\t$items         = $this->get_items( $settings );\n\n\n\t\tif ( papi_is_empty( $settings->items ) ) {\n\t\t\t$values = array_map( [$this, 'convert_post_to_item'], $values );\n\t\t} else {\n\t\t\tforeach ( array_keys( $sort_options ) as $key ) {\n\t\t\t\tif ( strpos( $key, 'Post' ) === 0 ) {\n\t\t\t\t\tunset( $sort_options[$key] );\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t// Remove existing values if `only once` is active.\n\t\tif ( $this->get_setting( 'only_once' ) ) {\n\t\t\t$items = array_udiff( $items, $values, function( $a, $b ) {\n\t\t\t\t// Backwards compatibility with both `post_title` and `title`.\n\t\t\t\treturn strcmp(\n\t\t\t\t\tstrtolower( isset( $a->post_title ) ? $a->post_title : $a->title ),\n\t\t\t\t\tstrtolower( isset( $b->post_title ) ? $b->post_title : $b->title )\n\t\t\t\t);\n\t\t\t} );\n\t\t}\n\n\t\t// Convert all sneak case key to camel case.\n\t\tforeach ( (array) $settings as $key => $val ) {\n\t\t\tif ( ! is_string( $key ) || ! in_array( $key, ['only_once', 'limit'], true ) ) {\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tif ( $key = papi_camel_case( $key ) ) {\n\t\t\t\t$settings_json[$key] = $val;\n\t\t\t}\n\t\t}\n\t\t?>\n\t\t<div class=\"papi-property-relationship\" data-settings='<?php echo esc_attr( papi_maybe_json_encode( $settings_json ) ); ?>'>\n\t\t\t<input type=\"hidden\" name=\"<?php echo esc_attr( $slug ); ?>[]\" data-papi-rule=\"<?php echo esc_attr( $slug ); ?>\" />\n\t\t\t<div class=\"relationship-inner\">\n\t\t\t\t<div class=\"relationship-top-left\">\n\t\t\t\t\t<label for=\"<?php echo esc_attr( $this->html_id( 'search' ) ); ?>\"><?php esc_html_e( 'Search', 'papi' ); ?></label>\n\t\t\t\t\t<input id=\"<?php echo esc_attr( $this->html_id( 'search' ) ); ?>\" type=\"search\" />\n\t\t\t\t</div>\n\t\t\t\t<div class=\"relationship-top-right\">\n\t\t\t\t\t<?php if ( $settings->show_sort_by ): ?>\n\t\t\t\t\t\t<label for=\"<?php echo esc_attr( $this->html_id( 'sort_option' ) ); ?>\"><?php esc_html_e( 'Sort by', 'papi' ); ?></label>\n\t\t\t\t\t\t<select id=\"<?php echo esc_attr( $this->html_id( 'sort_option' ) ); ?>\" name=\"<?php echo esc_attr( $this->html_id( 'sort_option' ) ); ?>\">\n\t\t\t\t\t\t\t<?php foreach ( array_keys( $sort_options ) as $key ): ?>\n\t\t\t\t\t\t\t\t<option value=\"<?php echo esc_attr( $key ); ?>\" <?php echo $key === $sort_option ? 'selected=\"selected\"' : ''; ?>><?php echo esc_html( $key ); ?></option>\n\t\t\t\t\t\t\t<?php endforeach; ?>\n\t\t\t\t\t\t</select>\n\t\t\t\t\t<?php endif; ?>\n\t\t\t\t</div>\n\t\t\t\t<div class=\"papi-clear\"></div>\n\t\t\t</div>\n\t\t\t<div class=\"relationship-inner\">\n\t\t\t\t<div class=\"relationship-left\">\n\t\t\t\t\t<ul>\n\t\t\t\t\t\t<?php\n\t\t\t\t\t\tforeach ( $items as $item ):\n\t\t\t\t\t\t\tif ( ! empty( $item->title ) ):\n\t\t\t\t\t\t\t\t?>\n\t\t\t\t\t\t\t\t<li>\n\t\t\t\t\t\t\t\t\t<input type=\"hidden\" data-name=\"<?php echo esc_attr( $slug ); ?>[]\" value=\"<?php echo esc_attr( $item->id ); ?>\"/>\n\t\t\t\t\t\t\t\t\t<a href=\"#\" title=\"<?php echo esc_attr( $item->title ); ?>\"><?php echo esc_html( $item->title ); ?></a>\n\t\t\t\t\t\t\t\t\t<span class=\"icon plus\"></span>\n\t\t\t\t\t\t\t\t</li>\n\t\t\t\t\t\t\t<?php\n\t\t\t\t\t\t\tendif;\n\t\t\t\t\t\tendforeach;\n\t\t\t\t\t\t?>\n\t\t\t\t\t</ul>\n\t\t\t\t</div>\n\t\t\t\t<div class=\"relationship-right\">\n\t\t\t\t\t<ul>\n\t\t\t\t\t\t<?php foreach ( $values as $item ): ?>\n\t\t\t\t\t\t\t<li>\n\t\t\t\t\t\t\t\t<input type=\"hidden\" name=\"<?php echo esc_attr( $slug ); ?>[]\" value=\"<?php echo esc_attr( $item->id ); ?>\"/>\n\t\t\t\t\t\t\t\t<a href=\"#\"><?php echo esc_attr( $item->title ); ?></a>\n\t\t\t\t\t\t\t\t<span class=\"icon minus\"></span>\n\t\t\t\t\t\t\t</li>\n\t\t\t\t\t\t<?php endforeach; ?>\n\t\t\t\t\t</ul>\n\t\t\t\t</div>\n\t\t\t\t<div class=\"papi-clear\"></div>\n\t\t\t</div>\n\t\t</div>\n\t<?php\n\t}\n\n\t/**\n\t * Change value after it's loaded from the database.\n\t *\n\t * @param  mixed  $values\n\t * @param  string $slug\n\t * @param  int    $post_id\n\t *\n\t * @return mixed\n\t */\n\tpublic function load_value( $values, $slug, $post_id ) {\n\t\t$values = (array) papi_maybe_json_decode( maybe_unserialize( $values ), true );\n\t\treturn array_map( 'papi_maybe_convert_to_object', $values );\n\t}\n\n\t/**\n\t * Sort the values.\n\t *\n\t * @param  array  $values\n\t * @param  string $slug\n\t * @param  int    $post_id\n\t *\n\t * @return array\n\t */\n\tpublic function sort_value( $values, $slug, $post_id ) {\n\t\t$sort_option  = $this->get_sort_option( $post_id );\n\t\t$sort_options = static::get_sort_options();\n\n\t\tif ( empty( $sort_option ) || ! isset( $sort_options[$sort_option] ) || is_null( $sort_options[$sort_option] ) ) {\n\t\t\treturn $values;\n\t\t}\n\n\t\tusort( $values, $sort_options[$sort_option] );\n\n\t\treturn $values;\n\t}\n\n\t/**\n\t * Sort the values on update.\n\t *\n\t * @param  mixed  $values\n\t * @param  string $slug\n\t * @param  int    $post_id\n\t *\n\t * @return string\n\t */\n\tpublic function update_value( $values, $slug, $post_id ) {\n\t\t$values = $this->format_value( $values, $slug, $post_id );\n\t\t$values = array_map( function ( $item ) {\n\t\t\tif ( $item instanceof WP_Post ) {\n\t\t\t\t$item = $this->convert_post_to_item( $item );\n\t\t\t}\n\n\t\t\tif ( isset( $item->title ) ) {\n\t\t\t\tunset( $item->title );\n\t\t\t}\n\n\t\t\treturn $item;\n\t\t}, $values );\n\n\t\treturn papi_maybe_json_decode( $values );\n\t}\n}\n"
  },
  {
    "path": "src/properties/class-papi-property-repeater.php",
    "content": "<?php\n\n/**\n * Repeater property that can repeat multiple properties.\n */\nclass Papi_Property_Repeater extends Papi_Property {\n\n\t/**\n\t * The convert type.\n\t *\n\t * @var string\n\t */\n\tpublic $convert_type = 'array';\n\n\t/**\n\t * Repeater counter number.\n\t *\n\t * @var int\n\t */\n\tprotected $counter = 0;\n\n\t/**\n\t * The default value.\n\t *\n\t * @var array\n\t */\n\tpublic $default_value = [];\n\n\t/**\n\t * Exclude properties that is not allowed in a repeater.\n\t *\n\t * @var array\n\t */\n\tprotected $exclude_properties = ['flexible'];\n\n\t/**\n\t * Delete value from the database.\n\t *\n\t * @param  string $slug\n\t * @param  int    $post_id\n\t * @param  string $type\n\t *\n\t * @return bool\n\t */\n\tpublic function delete_value( $slug, $post_id, $type ) {\n\t\t$rows   = intval( papi_data_get( $post_id, $slug, $type ) );\n\t\t$value  = $this->load_value( $rows, $slug, $post_id );\n\t\t$value  = papi_property_to_array_slugs( $value, $slug );\n\t\t$result = true;\n\n\t\tforeach ( array_keys( $value ) as $key ) {\n\t\t\t$out    = papi_data_delete( $post_id, $key, $type );\n\t\t\t$result = $out ? $result : $out;\n\t\t}\n\n\t\treturn $result;\n\t}\n\n\t/**\n\t * Format the value of the property before it's returned\n\t * to WordPress admin or the site.\n\t *\n\t * @param  mixed  $values\n\t * @param  string $repeater_slug\n\t * @param  int    $post_id\n\t *\n\t * @return array\n\t */\n\tpublic function format_value( $values, $repeater_slug, $post_id ) {\n\t\tif ( ! is_array( $values ) ) {\n\t\t\treturn [];\n\t\t}\n\n\t\t$top_property = new Papi_Core_Property;\n\n\t\tforeach ( $values as $index => $row ) {\n\t\t\tif ( ! is_array( $row ) ) {\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\t$top_property->slug = $repeater_slug . '_' . $index;\n\n\t\t\tforeach ( $row as $slug => $value ) {\n\t\t\t\tif ( papi_is_property_type_key( $slug ) ) {\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\n\t\t\t\t// Property type slug may have a prefixed underscore.\n\t\t\t\t$property_type_slug = papi_get_property_type_key_f( $slug );\n\n\t\t\t\tif ( ! isset( $row[$property_type_slug] ) ) {\n\t\t\t\t\t// If property type slug isn't prefixed we should try without.\n\t\t\t\t\t$property_type_slug = papi_get_property_type_key( $slug );\n\n\t\t\t\t\tif ( ! isset( $row[$property_type_slug] ) ) {\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\t// Get property type.\n\t\t\t\t$property_type_value = $row[$property_type_slug];\n\t\t\t\t$property_type       = papi_get_property_type( $property_type_value );\n\n\t\t\t\tif ( ! is_object( $property_type ) ) {\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\n\n\t\t\t\t// Get property child slug.\n\t\t\t\t$child_slug = $this->get_child_slug( $repeater_slug, $slug );\n\n\t\t\t\t// Set top property.\n\t\t\t\t$property_type->set_parent_property( $top_property );\n\n\t\t\t\t// Create cache key.\n\t\t\t\t$cache_key = sprintf( '%s_%s', $top_property->slug, $slug );\n\t\t\t\tif ( $parent_property = $this->get_parent_property() ) {\n\t\t\t\t\t$cache_key = sprintf( '%s_%s', $parent_property->get_slug( true ), $cache_key );\n\t\t\t\t}\n\n\t\t\t\t// Get raw value from cache if enabled.\n\t\t\t\tif ( $this->cache ) {\n\t\t\t\t\t$raw_value = papi_cache_get( $cache_key, $post_id, $this->get_meta_type() );\n\t\t\t\t} else {\n\t\t\t\t\t$raw_value = false;\n\t\t\t\t}\n\n\t\t\t\t// Load the value.\n\t\t\t\tif ( $raw_value === null || $raw_value === false ) {\n\t\t\t\t\t$row[$slug] = $property_type->load_value( $value, $child_slug, $post_id );\n\t\t\t\t\t$row[$slug] = papi_filter_load_value( $property_type->type, $row[$slug], $child_slug, $post_id, papi_get_meta_type() );\n\n\t\t\t\t\tif ( ! papi_is_empty( $row[$slug] ) && $this->cache ) {\n\t\t\t\t\t\tpapi_cache_set( $cache_key, $post_id, $row[$slug], $this->get_meta_type() );\n\t\t\t\t\t}\n\t\t\t\t} else {\n\t\t\t\t\t$row[$slug] = $raw_value;\n\t\t\t\t}\n\n\t\t\t\t// Format the value from the property class.\n\t\t\t\t$row[$slug] = $property_type->format_value( $row[$slug], $child_slug, $post_id );\n\n\t\t\t\tif ( ! papi_is_admin() ) {\n\t\t\t\t\t$row[$slug] = papi_filter_format_value( $property_type->type, $row[$slug], $child_slug, $post_id, papi_get_meta_type() );\n\t\t\t\t}\n\n\t\t\t\t$row[$property_type_slug] = $property_type_value;\n\n\t\t\t\tif ( papi_is_empty( $row[$slug] ) ) {\n\t\t\t\t\t$row[$slug] = $property_type_value->get_option( 'default', $property_type_value->default_value );\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif ( ! papi_is_admin() ) {\n\t\t\t\tforeach ( $row as $slug => $value ) {\n\t\t\t\t\tif ( papi_is_property_type_key( $slug ) ) {\n\t\t\t\t\t\tunset( $row[$slug] );\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t$values[$index] = $row;\n\t\t}\n\n\t\treturn $values;\n\t}\n\n\t/**\n\t * Get child slug from the repeater slug.\n\t *\n\t * @param  string $repeater_slug\n\t * @param  string $child_slug\n\t *\n\t * @return string\n\t */\n\tprotected function get_child_slug( $repeater_slug, $child_slug ) {\n\t\treturn preg_replace( '/^\\_/', '', preg_replace( '/\\_\\d+\\_/', '', preg_replace( sprintf( '/^%s/', $repeater_slug ), '', $child_slug ) ) );\n\t}\n\n\t/**\n\t * Get default settings.\n\t *\n\t * @return array\n\t */\n\tpublic function get_default_settings() {\n\t\treturn [\n\t\t\t'add_new_label' => __( 'Add new row', 'papi' ),\n\t\t\t'closed_rows'   => false,\n\t\t\t'items'         => [],\n\t\t\t'layout'        => 'table',\n\t\t\t'limit'         => - 1\n\t\t];\n\t}\n\n\t/**\n\t * Get results from the database.\n\t *\n\t * @param  int    $value\n\t * @param  string $repeater_slug\n\t * @param  int    $post_id\n\t *\n\t * @return array\n\t */\n\tprotected function get_results( $value, $repeater_slug, $post_id ) {\n\t\tglobal $wpdb;\n\n\t\tif ( $this->get_meta_type() === 'option' ) {\n\t\t\t$table = $wpdb->prefix . 'options';\n\n\t\t\t// @codingStandardsIgnoreStart\n\t\t\t$query = $wpdb->prepare(\n\t\t\t\t\"SELECT * FROM `$table` WHERE `option_name` LIKE '%s' ORDER BY `option_id` ASC\",\n\t\t\t\t$repeater_slug . '_%'\n\t\t\t);\n\t\t\t// @codingStandardsIgnoreEnd\n\t\t} else {\n\t\t\t$table  = sprintf( '%s%smeta', $wpdb->prefix, $this->get_meta_type() );\n\t\t\t$column = papi_get_meta_id_column( $this->get_meta_type() );\n\t\t\t// @codingStandardsIgnoreStart\n\t\t\t$query = $wpdb->prepare(\n\t\t\t\t\"SELECT * FROM `$table` WHERE `meta_key` LIKE '%s' AND `$column` = %s ORDER BY `meta_id` ASC\", $repeater_slug . '_%',\n\t\t\t\t$post_id\n\t\t\t);\n\t\t\t// @codingStandardsIgnoreEnd\n\t\t}\n\n\t\t$dbresults = $wpdb->get_results( $query ); // WPCS: unprepared SQL\n\t\t$value     = intval( $value );\n\n\t\t// Do not proceed with empty value.\n\t\tif ( empty( $value ) ) {\n\t\t\treturn [[], []];\n\t\t}\n\n\t\t$values  = [];\n\t\t$results = [];\n\t\t$trash   = [];\n\n\t\t// Get row results.\n\t\t$rows = array_values( $this->get_row_results( $dbresults ) );\n\n\t\t// Add repeater slug with number of rows to the values array.\n\t\t$values[$repeater_slug] = $value;\n\n\t\tfor ( $i = 0; $i < $value; $i ++ ) {\n\t\t\t$no_trash = [];\n\n\t\t\tif ( ! isset( $no_trash[$i] ) ) {\n\t\t\t\t$no_trash[$i] = [];\n\t\t\t}\n\n\t\t\tif ( ! isset( $rows[$i] ) ) {\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tforeach ( $rows[$i] as $slug => $meta ) {\n\t\t\t\tif ( ! is_string( $slug ) || ! isset( $rows[$i][$slug] ) ) {\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\n\t\t\t\t// Add meta object to the no trash array.\n\t\t\t\t// so it won't be deleted.\n\t\t\t\t$no_trash[$slug] = $meta;\n\n\t\t\t\t// Add property value.\n\t\t\t\t$values[$meta->meta_key] = papi_maybe_json_decode( maybe_unserialize( $meta->meta_value ) );\n\t\t\t}\n\n\t\t\t// Get the meta keys to delete.\n\t\t\t$trash_diff = array_diff( array_keys( $rows[$i] ), array_keys( $no_trash[$i] ) );\n\n\t\t\tif ( ! empty( $trash_diff ) ) {\n\t\t\t\t// Find all trash meta objects from results array.\n\t\t\t\tforeach ( $trash_diff as $slug ) {\n\t\t\t\t\tif ( ! isset( $results[$i] ) || ! isset( $rows[$i][$slug] ) ) {\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\n\t\t\t\t\t$trash[$results[$i][$slug]->meta_key] = $rows[$i][$slug];\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t$properties = $this->get_settings_properties();\n\n\t\t// Add empty rows that isn't saved to database.\n\t\tfor ( $i = 0; $i < $value; $i ++ ) {\n\t\t\tforeach ( $properties as $prop ) {\n\t\t\t\tif ( ! isset( $prop->slug ) ) {\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\n\t\t\t\t$slug = sprintf( '%s_%d_%s', $repeater_slug, $i, unpapify( $prop->slug ) );\n\n\t\t\t\tif ( ! isset( $values[$slug] ) ) {\n\t\t\t\t\t$values[$slug] = null;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\treturn [$values, $trash];\n\t}\n\n\t/**\n\t * Get row results.\n\t *\n\t * @param  array $dbresults\n\t *\n\t * @return array\n\t */\n\tprotected function get_row_results( $dbresults ) {\n\t\t$results   = [];\n\t\t$is_option = $this->get_meta_type() === 'option';\n\n\t\tforeach ( $dbresults as $meta ) {\n\t\t\tif ( $is_option ) {\n\t\t\t\tpreg_match( '/^[^\\d]*(\\d+)/', $meta->option_name, $matches );\n\t\t\t} else {\n\t\t\t\tpreg_match( '/^[^\\d]*(\\d+)/', $meta->meta_key, $matches );\n\t\t\t}\n\n\t\t\tif ( count( $matches ) < 2 ) {\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\t$i = intval( $matches[1] );\n\n\t\t\tif ( ! isset( $results[$i] ) ) {\n\t\t\t\t$results[$i] = [];\n\t\t\t}\n\n\t\t\tif ( $is_option ) {\n\t\t\t\t$results[$i][$meta->option_name] = (object) [\n\t\t\t\t\t'meta_key'   => $meta->option_name,\n\t\t\t\t\t'meta_value' => $meta->option_value\n\t\t\t\t];\n\t\t\t} else {\n\t\t\t\t$results[$i][$meta->meta_key] = $meta;\n\t\t\t}\n\t\t}\n\n\t\treturn $results;\n\t}\n\n\t/**\n\t * Get settings properties.\n\t *\n\t * @return array\n\t */\n\tprotected function get_settings_properties() {\n\t\t$settings = $this->get_settings();\n\n\t\tif ( is_null( $settings ) ) {\n\t\t\treturn [];\n\t\t}\n\n\t\treturn $this->prepare_properties( papi_to_array( $settings->items ) );\n\t}\n\n\t/**\n\t * Render property html.\n\t */\n\tpublic function html() {\n\t\t$options = $this->get_options();\n\n\t\t// Reset list counter number.\n\t\t$this->counter = 0;\n\n\t\t// Fix so repeater is not render over the title and description.\n\t\tif ( $this->get_option( 'layout' ) === 'vertical' ) {\n\t\t\techo '<br />';\n\t\t}\n\n\t\t// Render repeater html.\n\t\t$this->render_repeater( $options );\n\n\t\t// Render JSON template that is used for Papi ajax.\n\t\t$this->render_json_template( $options->slug );\n\t}\n\n\t/**\n\t * Check if the given layout is the layouted used.\n\t *\n\t * @param  string $layout\n\t *\n\t * @return bool\n\t */\n\tprotected function layout( $layout ) {\n\t\treturn $this->get_setting( 'layout' ) === $layout;\n\t}\n\n\t/**\n\t * Change value after it's loaded from the database\n\t * and populate every property in the repeater with the right property type.\n\t *\n\t * @param int    $value\n\t * @param string $repeater_slug\n\t * @param int    $post_id\n\t *\n\t * @return array\n\t */\n\tpublic function load_value( $value, $repeater_slug, $post_id ) {\n\t\tif ( is_array( $value ) ) {\n\t\t\treturn $value;\n\t\t}\n\n\t\tlist( $results, $trash ) = $this->get_results( $value, $repeater_slug, $post_id );\n\n\t\t// Will not need this array.\n\t\tunset( $trash );\n\n\t\t$results = papi_from_property_array_slugs( $results, unpapify( $repeater_slug ) );\n\n\t\tif ( empty( $results ) ) {\n\t\t\treturn $this->default_value;\n\t\t}\n\n\t\treturn $this->load_child_properties( $results, $this );\n\t}\n\n\t/**\n\t * Load child properties.\n\t *\n\t * @param  array              $results\n\t * @param  Papi_Core_Property $property\n\t *\n\t * @return array\n\t */\n\tprotected function load_child_properties( array $results, $property = null ) {\n\t\tforeach ( $results as $index => $row ) {\n\t\t\tforeach ( $row as $slug => $value ) {\n\t\t\t\tif ( is_array( $value ) && isset( $value[$slug] ) ) {\n\t\t\t\t\t$child_property = $this->get_store()->get_property( $this->get_slug( true ), $slug );\n\n\t\t\t\t\tif ( papi_is_property( $child_property ) && ! empty( $child_property->get_child_properties() ) ) {\n\t\t\t\t\t\t$value                  = papi_from_property_array_slugs( $value, unpapify( $slug ) );\n\t\t\t\t\t\t$results[$index][$slug] = $this->load_child_properties( $value, $child_property );\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\t$type_key = papi_get_property_type_key_f( $slug );\n\n\t\t\t\tif ( $property->match_slug( $slug ) ) {\n\t\t\t\t\t$results[$index][$type_key] = $property;\n\t\t\t\t} else {\n\t\t\t\t\t$results[$index][$type_key] = $property->get_child_property( $slug );\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\treturn $results;\n\t}\n\n\t/**\n\t * Prepare properties, get properties options object,\n\t * check which properties that are allowed to use.\n\t *\n\t * @param  array $items\n\t *\n\t * @return array\n\t */\n\tprotected function prepare_properties( $items ) {\n\t\t$key   = isset( $this->layout_key ) &&\n\t\t$this->layout_key === '_layout' ? 'flexible' : 'repeater';\n\t\t$items = array_map( 'papi_property', $items );\n\n\t\t$exclude_properties = $this->exclude_properties;\n\t\t$exclude_properties = array_merge( $exclude_properties, apply_filters( 'papi/property/' . $key . '/exclude', [] ) );\n\n\t\treturn array_filter( $items, function ( $item ) use ( $exclude_properties ) {\n\t\t\tif ( ! is_object( $item ) ) {\n\t\t\t\treturn false;\n\t\t\t}\n\n\t\t\tif ( empty( $item->type ) ) {\n\t\t\t\treturn false;\n\t\t\t}\n\n\t\t\treturn ! in_array( $item->type, $exclude_properties, true );\n\t\t} );\n\t}\n\n\t/**\n\t * Prepare property for JSON.\n\t *\n\t * @param  Papi_Property $property\n\t *\n\t * @return bool|object\n\t */\n\tprotected function prepare_property_for_json( $property ) {\n\t\t// Only real property objects and not properties that are disabled.\n\t\tif ( ! papi_is_property( $property ) || $property->disabled() ) {\n\t\t\treturn false;\n\t\t}\n\n\t\t$options = clone $property->get_options();\n\n\t\tif ( isset( $options->settings->items ) ) {\n\t\t\tforeach ( $options->settings->items as $index => $property ) {\n\t\t\t\tif ( is_array( $property ) && isset( $property['items'] ) ) {\n\t\t\t\t\tforeach ( $property['items'] as $child_index => $child_property ) {\n\t\t\t\t\t\t$options->settings->items[$index]['items'][$child_index] = $this->prepare_property_for_json( $child_property );\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tif ( $property = $this->prepare_property_for_json( $property ) ) {\n\t\t\t\t\t$options->settings->items[$index] = $property;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\treturn $options;\n\t}\n\n\t/**\n\t * Remove all repeater rows from the database.\n\t *\n\t * @param int    $post_id\n\t * @param string $repeater_slug\n\t */\n\tprotected function remove_repeater_rows( $post_id, $repeater_slug ) {\n\t\tglobal $wpdb;\n\n\t\t$is_option     = $this->get_meta_type() === 'option';\n\t\t$repeater_slug = $repeater_slug . '_%';\n\n\t\tif ( $is_option ) {\n\t\t\t$table = $wpdb->prefix . 'options';\n\n\t\t\t// @codingStandardsIgnoreStart\n\t\t\t$query = $wpdb->prepare(\n\t\t\t\t\"SELECT * FROM `$table` WHERE (`option_name` LIKE %s OR `option_name` LIKE %s AND NOT `option_name` = %s)\",\n\t\t\t\t$repeater_slug,\n\t\t\t\tpapi_f( $repeater_slug ),\n\t\t\t\tpapi_get_property_type_key_f( $repeater_slug )\n\t\t\t);\n\t\t\t// @codingStandardsIgnoreEnd\n\t\t} else {\n\t\t\t$table = $wpdb->prefix . 'postmeta';\n\t\t\t// @codingStandardsIgnoreStart\n\t\t\t$query = $wpdb->prepare(\n\t\t\t\t\"SELECT * FROM `$table` WHERE `post_id` = %d AND (`meta_key` LIKE %s OR `meta_key` LIKE %s AND NOT `meta_key` = %s)\",\n\t\t\t\t$post_id,\n\t\t\t\t$repeater_slug,\n\t\t\t\tpapi_f( $repeater_slug ),\n\t\t\t\tpapi_get_property_type_key_f( $repeater_slug )\n\t\t\t);\n\t\t\t// @codingStandardsIgnoreEnd\n\t\t}\n\n\t\t$results = $wpdb->get_results( $query ); // WPCS: unprepared sql\n\n\t\tforeach ( $results as $res ) {\n\t\t\tif ( $is_option ) {\n\t\t\t\t$key = $res->option_name;\n\t\t\t} else {\n\t\t\t\t$key = $res->meta_key;\n\t\t\t}\n\n\t\t\tpapi_data_delete( $post_id, $key, $this->get_meta_type() );\n\t\t}\n\t}\n\n\t/**\n\t * Render AJAX request.\n\t */\n\tpublic function render_ajax_request() {\n\t\t$items = $this->get_settings_properties();\n\n\t\tif ( papi_doing_ajax() ) {\n\t\t\t$counter       = papi_get_qs( 'counter' );\n\t\t\t$this->counter = intval( $counter );\n\t\t}\n\n\t\t$this->render_properties( $items, false );\n\t}\n\n\t/**\n\t * Render property JSON template.\n\t *\n\t * @param string $slug\n\t */\n\tprotected function render_json_template( $slug ) {\n\t\t$options = $this->get_options();\n\n\t\t$options->settings->items = papi_to_array( $options->settings->items );\n\n\t\tforeach ( $options->settings->items as $key => $value ) {\n\t\t\t$property = $this->prepare_property_for_json( papi_property( $value ) );\n\n\t\t\tif ( $property === false ) {\n\t\t\t\tunset( $options->settings->items[$key] );\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\t$options->settings->items[$key] = $property;\n\t\t}\n\n\t\tpapi_render_html_tag( 'script', [\n\t\t\t'data-papi-json' => esc_attr( sprintf( '%s_repeater_json', $slug ) ),\n\t\t\t'type'           => 'application/json',\n\t\t\tpapi_maybe_json_encode( [$options] )\n\t\t] );\n\t}\n\n\t/**\n\t * Render properties.\n\t *\n\t * @param array      $row\n\t * @param array|bool $value\n\t */\n\tprotected function render_properties( $row, $value ) {\n\t\t$layout = $this->get_setting( 'layout' );\n\n\t\tif ( $layout === 'row' ): ?>\n\t\t\t<td class=\"repeater-layout-row\">\n\t\t\t<div class=\"repeater-content-open\">\n\t\t\t<table class=\"papi-table\">\n\t\t\t<tbody>\n\t\t<?php endif;\n\n\t\t$has_value = $value !== false;\n\n\t\tforeach ( $row as $property ) {\n\t\t\t// Don't show the property if it's disabled.\n\t\t\tif ( $property->disabled() ) {\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\t$render_property = clone $property->get_options();\n\t\t\t$value_slug      = $property->get_slug( true );\n\n\t\t\tif ( $has_value ) {\n\t\t\t\tif ( array_key_exists( $value_slug, $value ) ) {\n\t\t\t\t\t$render_property->value = $value[$value_slug];\n\t\t\t\t} else {\n\t\t\t\t\tif ( array_key_exists( $property->get_slug(), $value ) ) {\n\t\t\t\t\t\t$render_property->value = $property->default_value;\n\t\t\t\t\t} else {\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t$render_property->slug = $this->html_name( $property, $this->counter );\n\t\t\t$render_property->raw  = $layout === 'table';\n\n\t\t\tif ( $layout === 'table' ) {\n\t\t\t\techo '<td class=\"repeater-column ' . ( $property->display() ? '' : 'papi-hide' ) . '\">';\n\t\t\t\techo '<div class=\"repeater-content-open\">';\n\t\t\t\techo sprintf(\n\t\t\t\t\t'<label for=\"%s\" class=\"papi-visually-hidden\">%s</label>',\n\t\t\t\t\tesc_attr( $this->html_id( $property, $this->counter ) ),\n\t\t\t\t\tesc_html( $property->title )\n\t\t\t\t);\n\t\t\t}\n\n\t\t\tpapi_render_property( $render_property );\n\n\t\t\tif ( $layout === 'table' ) {\n\t\t\t\techo '</div>';\n\t\t\t\techo '</td>';\n\t\t\t}\n\t\t}\n\n\t\tif ( $layout === 'row' ): ?>\n\t\t\t</tbody>\n\t\t\t</table>\n\t\t\t</div>\n\t\t\t</td>\n\t\t<?php endif;\n\t}\n\n\t/**\n\t * Render repeater html.\n\t *\n\t * @param stdClass $options\n\t */\n\tprotected function render_repeater( $options ) {\n\t\t?>\n\t\t<div class=\"papi-property-repeater papi-property-repeater-top\" data-limit=\"<?php echo esc_attr( $this->get_setting( 'limit' ) ); ?>\">\n\t\t\t<table class=\"papi-table\">\n\t\t\t\t<?php $this->render_repeater_head(); ?>\n\n\t\t\t\t<tbody class=\"repeater-tbody\">\n\t\t\t\t<?php $this->render_repeater_rows(); ?>\n\t\t\t\t</tbody>\n\t\t\t</table>\n\n\t\t\t<div class=\"bottom\">\n\t\t\t\t<?php\n\t\t\t\tpapi_render_html_tag( 'button', [\n\t\t\t\t\t'class'          => 'button button-primary',\n\t\t\t\t\t'data-papi-json' => sprintf( '%s_repeater_json', $options->slug ),\n\t\t\t\t\t'type'           => 'button',\n\t\t\t\t\tesc_html( $this->get_setting( 'add_new_label' ) )\n\t\t\t\t] );\n\t\t\t\t?>\n\t\t\t</div>\n\n\t\t\t<?php /* Default repeater value */ ?>\n\n\t\t\t<input type=\"hidden\" data-papi-rule=\"<?php echo esc_attr( $options->slug ); ?>\" name=\"<?php echo esc_attr( $options->slug ); ?>[]\" />\n\t\t</div>\n\t\t<?php\n\t}\n\n\t/**\n\t * Render repeater head.\n\t */\n\tprotected function render_repeater_head() {\n\t\t$properties = $this->get_settings_properties();\n\t\t?>\n\t\t<thead>\n\t\t<?php if ( ! $this->layout( 'row' ) ): ?>\n\t\t\t<tr>\n\t\t\t\t<th></th>\n\t\t\t\t<?php\n\t\t\t\tforeach ( $properties as $property ):\n\t\t\t\t\t// Don't show the property if it's disabled.\n\t\t\t\t\tif ( $property->disabled() ) {\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\t\t\t\t\t?>\n\t\t\t\t\t<th class=\"repeater-column <?php echo $property->display() ? '' : 'papi-hide'; ?>\">\n\t\t\t\t\t\t<?php echo esc_html( $property->title ); ?>\n\t\t\t\t\t</th>\n\t\t\t\t<?php endforeach; ?>\n\t\t\t\t<th class=\"last\"></th>\n\t\t\t</tr>\n\t\t<?php endif; ?>\n\t\t</thead>\n\t\t<?php\n\t}\n\n\t/**\n\t * Render repeater rows.\n\t */\n\tprotected function render_repeater_rows() {\n\t\t$items  = $this->get_settings_properties();\n\t\t$values = $this->get_value();\n\t\t$values = is_array( $values ) ? $values : [];\n\t\t$slugs  = wp_list_pluck( $items, 'slug' );\n\n\t\t// Remove values that don't exists in the slugs array.\n\t\tforeach ( $values as $index => $value ) {\n\t\t\tif ( ! is_array( $value ) ) {\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\t$keys = array_keys( $value );\n\n\t\t\tforeach ( $slugs as $slug ) {\n\t\t\t\tif ( in_array( $slug, $keys, true ) ) {\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\n\t\t\t\t$values[$index][$slug] = '';\n\t\t\t}\n\t\t}\n\n\t\t$values      = array_filter( $values );\n\t\t$closed_rows = $this->get_setting( 'closed_rows', true );\n\n\t\tforeach ( $values as $row ):\n\t\t\t?>\n\t\t\t<tr <?php echo $closed_rows ? 'class=\"closed\"' : ''; ?>>\n\t\t\t\t<td class=\"handle\">\n\t\t\t\t\t<span class=\"toggle\"></span>\n\t\t\t\t\t<span class=\"count\"><?php echo esc_html( $this->counter + 1 ); ?></span>\n\t\t\t\t</td>\n\t\t\t\t<?php\n\t\t\t\t$this->render_properties( $items, $row );\n\t\t\t\t$this->counter ++;\n\t\t\t\t?>\n\t\t\t\t<td class=\"last\">\n\t\t\t\t\t<span>\n\t\t\t\t\t\t<a title=\"<?php esc_attr_e( 'Remove', 'papi' ); ?>\" href=\"#\" class=\"repeater-remove-item\">x</a>\n\t\t\t\t\t</span>\n\t\t\t\t</td>\n\t\t\t</tr>\n\t\t\t<?php\n\t\tendforeach;\n\t}\n\n\t/**\n\t * Render repeater row template.\n\t */\n\tpublic function render_repeater_rows_template() {\n\t\t?>\n\t\t<script type=\"text/template\" id=\"tmpl-papi-property-repeater-row\">\n\t\t\t<tr>\n\t\t\t\t<td class=\"handle\">\n\t\t\t\t\t<span class=\"toggle\"></span>\n\t\t\t\t\t<span class=\"count\"><%= counter + 1 %></span>\n\t\t\t\t</td>\n\t\t\t\t<%= columns %>\n\t\t\t\t<td class=\"last\">\n\t\t\t\t\t<span>\n\t\t\t\t\t\t<a title=\"<?php esc_attr_e( 'Remove', 'papi' ); ?>\" href=\"#\" class=\"repeater-remove-item\">x</a>\n\t\t\t\t\t</span>\n\t\t\t\t</td>\n\t\t\t</tr>\n\t\t</script>\n\t\t<?php\n\t}\n\n\t/**\n\t * Setup actions.\n\t */\n\tprotected function setup_actions() {\n\t\tadd_action( 'admin_head', [$this, 'render_repeater_rows_template'] );\n\t}\n\n\t/**\n\t * Update value before it's saved to the database.\n\t *\n\t * @param mixed  $values\n\t * @param string $repeater_slug\n\t * @param int    $post_id\n\t *\n\t * @return array\n\t */\n\tpublic function update_value( $values, $repeater_slug, $post_id ) {\n\t\t$rows = intval( papi_data_get( $post_id, $repeater_slug, $this->get_meta_type() ) );\n\n\t\tif ( ! is_array( $values ) ) {\n\t\t\t$values = [];\n\t\t}\n\n\t\tlist( $results, $trash ) = $this->get_results( $rows, $repeater_slug, $post_id );\n\n\t\t// Delete trash values.\n\t\tforeach ( $trash as $meta ) {\n\t\t\tpapi_data_delete( $post_id, $meta->meta_key, $this->get_meta_type() );\n\t\t}\n\n\t\t$top_property = new Papi_Core_Property;\n\n\t\tforeach ( $values as $index => $row ) {\n\t\t\tif ( ! is_array( $row ) ) {\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\t$top_property->slug = $repeater_slug . '_' . $index;\n\n\t\t\tforeach ( $row as $slug => $value ) {\n\t\t\t\tif ( papi_is_property_type_key( $slug ) ) {\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\n\t\t\t\t// Property type slug may have a prefixed underscore.\n\t\t\t\t$property_type_slug = papi_get_property_type_key_f( $slug );\n\n\t\t\t\tif ( ! isset( $row[$property_type_slug] ) ) {\n\t\t\t\t\t// If property type slug isn't prefixed we should try without.\n\t\t\t\t\t$property_type_slug = papi_get_property_type_key( $slug );\n\n\t\t\t\t\tif ( ! isset( $row[$property_type_slug] ) ) {\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tif ( ! isset( $row[$property_type_slug] ) ) {\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\n\t\t\t\t// Get real property slug\n\t\t\t\t$property_slug = $this->get_child_slug( $repeater_slug, $slug );\n\n\t\t\t\t// Get property type\n\t\t\t\t$property_type_value = $row[$property_type_slug]->type;\n\t\t\t\t$property_type       = papi_get_property_type( $row[$property_type_slug] );\n\n\t\t\t\tif ( ! is_object( $property_type ) ) {\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\n\t\t\t\t// Set top property.\n\t\t\t\t$property_type->set_parent_property( $top_property );\n\n\t\t\t\t// Create cache key.\n\t\t\t\t$cache_key = sprintf( '%s_%s', $top_property->slug, $slug );\n\t\t\t\tif ( $parent_property = $this->get_parent_property() ) {\n\t\t\t\t\t$cache_key = sprintf( '%s_%s', $parent_property->get_slug( true ), $cache_key );\n\t\t\t\t}\n\n\t\t\t\t// Delete cache if any.\n\t\t\t\tpapi_cache_delete( $cache_key, $post_id, papi_get_meta_type() );\n\n\t\t\t\t// Unserialize if needed.\n\t\t\t\t$value = papi_maybe_json_decode( maybe_unserialize( $value ) );\n\n\t\t\t\t// Run update value on each property type class.\n\t\t\t\t$value = $property_type->update_value( $value, $property_slug, $post_id );\n\n\t\t\t\t// Run update value on each property type filter.\n\t\t\t\t$row[$slug] = papi_filter_update_value( $property_type_value, $value, $property_slug, $post_id, papi_get_meta_type() );\n\n\t\t\t\tif ( is_array( $row[$slug] ) ) {\n\t\t\t\t\tforeach ( $row[$slug] as $key => $val ) {\n\t\t\t\t\t\tif ( ! is_string( $key ) ) {\n\t\t\t\t\t\t\tcontinue;\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tunset( $row[$slug][$key] );\n\n\t\t\t\t\t\t$key = preg_replace( '/^\\_/', '', $key );\n\n\t\t\t\t\t\t$row[$slug][$key] = $val;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// Remove unnecessary property type keys if any is left.\n\t\t\tforeach ( $row as $slug => $value ) {\n\t\t\t\tif ( papi_is_property_type_key( $slug ) ) {\n\t\t\t\t\tunset( $row[$slug] );\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t$values[$index] = $row;\n\t\t}\n\n\t\t$values = papi_property_to_array_slugs( $values, $repeater_slug );\n\n\t\t// Find out which keys that should be deleted.\n\t\t$trash = array_diff( array_keys( papi_to_array( $results ) ), array_keys( papi_to_array( $values ) ) );\n\n\t\t// Delete unwanted (trash) values.\n\t\tforeach ( array_keys( $trash ) as $trash_key ) {\n\t\t\tpapi_data_delete( $post_id, $trash_key, $this->get_meta_type() );\n\t\t}\n\n\t\t// It's safe to remove all rows in the database here.\n\t\t$this->remove_repeater_rows( $post_id, $repeater_slug );\n\n\t\treturn $values;\n\t}\n}\n"
  },
  {
    "path": "src/properties/class-papi-property-sidebar.php",
    "content": "<?php\n\n/**\n * Sidebar property that populates a dropdown.\n */\nclass Papi_Property_Sidebar extends Papi_Property_Dropdown {\n\n\t/**\n\t * The convert type.\n\t *\n\t * @var string\n\t */\n\tpublic $convert_type = 'string';\n\n\t/**\n\t * Format the value of the property before it's returned\n\t * to WordPress admin or the site.\n\t *\n\t * @param  mixed  $value\n\t * @param  string $slug\n\t * @param  int    $post_id\n\t *\n\t * @return string\n\t */\n\tpublic function format_value( $value, $slug, $post_id ) {\n\t\tif ( is_admin() || ! $this->settings->render ) {\n\t\t\treturn $value;\n\t\t}\n\n\t\tob_start();\n\t\tif ( is_active_sidebar( $value ) ) {\n\t\t\tdynamic_sidebar( $value );\n\t\t}\n\n\t\treturn ob_get_clean();\n\t}\n\n\t/**\n\t * Get default settings.\n\t *\n\t * @return array\n\t */\n\tpublic function get_default_settings() {\n\t\treturn [\n\t\t\t'allow_clear'  => true,\n\t\t\t'placeholder'  => '',\n\t\t\t'render'       => true,\n\t\t\t'select2'      => true\n\t\t];\n\t}\n\n\t/**\n\t * Get registered sidebars as dropdown items.\n\t *\n\t * @return array\n\t */\n\tpublic function get_items() {\n\t\tglobal $wp_registered_sidebars;\n\t\t$items = [];\n\n\t\tforeach ( $wp_registered_sidebars as $item ) {\n\t\t\t$items[$item['name']] = $item['id'];\n\t\t}\n\n\t\tksort( $items );\n\n\t\treturn $items;\n\t}\n}\n"
  },
  {
    "path": "src/properties/class-papi-property-string.php",
    "content": "<?php\n\n/**\n * HTML text input property.\n */\nclass Papi_Property_String extends Papi_Property {\n\n\t/**\n\t * The input type to use.\n\t *\n\t * @var string\n\t */\n\tpublic $input_type = 'text';\n\n\t/**\n\t * Format the value of the property before it's returned\n\t * to WordPress admin or the site.\n\t *\n\t * @param  mixed  $value\n\t * @param  string $slug\n\t * @param  int    $post_id\n\t *\n\t * @return array\n\t */\n\tpublic function format_value( $value, $slug, $post_id ) {\n\t\tif ( ! $this->get_setting( 'allow_html' ) && $this->input_type === 'text' ) {\n\t\t\t$value = papi_maybe_json_decode( maybe_unserialize( $value ) );\n\n\t\t\tif ( ! is_string( $value ) ) {\n\t\t\t\t$value = '';\n\t\t\t}\n\n\t\t\t$value = wp_strip_all_tags( $value );\n\t\t}\n\n\t\treturn $value;\n\t}\n\n\t/**\n\t * Get default settings.\n\t *\n\t * @return array\n\t */\n\tpublic function get_default_settings() {\n\t\treturn [\n\t\t\t'allow_html'  => false,\n\t\t\t'placeholder' => ''\n\t\t];\n\t}\n\n\t/**\n\t * Get value from the database.\n\t *\n\t * @return string\n\t */\n\tpublic function get_value() {\n\t\t$value = $this->format_value(\n\t\t\tparent::get_value(),\n\t\t\t$this->get_slug(),\n\t\t\tpapi_get_post_id()\n\t\t);\n\n\t\treturn $this->get_setting( 'allow_html' ) ? $value : esc_html( $value );\n\t}\n\n\t/**\n\t * Render property html.\n\t */\n\tpublic function html() {\n\t\tpapi_render_html_tag( 'input', [\n\t\t\t'id'          => esc_attr( $this->html_id() ),\n\t\t\t'name'        => esc_attr( $this->html_name() ),\n\t\t\t'type'        => esc_attr( $this->input_type ),\n\t\t\t'value'       => $this->get_value(),\n\t\t\t'placeholder' => esc_attr( $this->get_setting( 'placeholder' ) )\n\t\t] );\n\t}\n}\n"
  },
  {
    "path": "src/properties/class-papi-property-table.php",
    "content": "<?php\n\nclass Papi_Property_Table extends Papi_Property {\n\n\t/**\n\t * The convert type.\n\t *\n\t * @var string\n\t */\n\tpublic $convert_type = 'array';\n\n\t/**\n\t * Build table from array data.\n\t *\n\t * @param  array $arr\n\t * @param  bool  $child\n\t *\n\t * @return stringe\n\t */\n\tprotected function build_table( array $arr, $child = false ) {\n\t\t$html = '<div class=\"papi-property-table\"><table class=\"papi-table\">';\n\t\t$allow_html = $this->get_setting( 'allow_html' );\n\n\t\tif ( $child ) {\n\t\t\t$html .= '<thead>';\n\n\t\t\tforeach ( $arr[0] as $key => $value ) {\n\t\t\t\tif ( $allow_html ) {\n\t\t\t\t\t$key = html_entity_decode( $key );\n\t\t\t\t}\n\n\t\t\t\t$html .= sprintf( '<th>%s</th>', $key );\n\t\t\t}\n\n\t\t\t$html .= '</thead>';\n\t\t}\n\n\t\tforeach ( $arr as $key => $value ) {\n\t\t\t$html .= '<tr>';\n\n\t\t\tforeach ( $value as $key2 => $value2 ) {\n\t\t\t\tif ( is_array( $value2 ) ) {\n\t\t\t\t\t$value2 = $this->build_table( $value2, true );\n\t\t\t\t}\n\n\t\t\t\t$value2 = papi_convert_to_string( $value2 );\n\n\t\t\t\tif ( $allow_html ) {\n\t\t\t\t\t$value2 = html_entity_decode( $value2 );\n\t\t\t\t}\n\n\t\t\t\t$html .= sprintf( '<td>%s</td>', $value2 );\n\t\t\t}\n\n\t\t\t$html .= '</tr>';\n\t\t}\n\n\t\treturn $html . '</table></div>';\n\t}\n\n\t/**\n\t * Get default settings.\n\t *\n\t * @return array\n\t */\n\tpublic function get_default_settings() {\n\t\treturn [\n\t\t\t'allow_html'  => false,\n\t\t\t'items'       => [],\n\t\t];\n\t}\n\n\t/**\n\t * Render property html.\n\t */\n\tpublic function html() {\n\t\t$value = $this->get_value();\n\t\t$data  = $this->get_setting( 'items' );\n\n\t\tif ( ! is_array( $data ) ) {\n\t\t\treturn;\n\t\t}\n\n\t\t// Convert key/value array to [key, value] value.\n\t\tforeach ( $data as $key => $value ) {\n\t\t\tif ( is_array( $value ) ) {\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\t$data[$key] = [$key, $value];\n\t\t}\n\n\t\techo $this->build_table( $data ); // wpcs: xss ok\n\t}\n}\n"
  },
  {
    "path": "src/properties/class-papi-property-term.php",
    "content": "<?php\n\n/**\n * Term property that populates a dropdown.\n */\nclass Papi_Property_Term extends Papi_Property {\n\n\t/**\n\t * The convert type.\n\t *\n\t * @var string\n\t */\n\tpublic $convert_type = 'object';\n\n\t/**\n\t * Format the value of the property before it's returned to the application.\n\t *\n\t * @param  mixed  $value\n\t * @param  string $slug\n\t * @param  int    $term_id\n\t *\n\t * @return array\n\t */\n\tpublic function format_value( $value, $slug, $term_id ) {\n\t\t$meta_key = $this->get_setting( 'meta_key' );\n\n\t\tif ( is_string( $value ) ) {\n\t\t\t$value = trim( $value );\n\t\t}\n\n\t\tif ( papi_is_empty( $value ) ) {\n\t\t\treturn $this->default_value;\n\t\t}\n\n\t\tif ( empty( $meta_key ) ) {\n\t\t\tif ( is_numeric( $value ) && intval( $value ) !== 0 ) {\n\t\t\t\t$term_id = $value;\n\t\t\t}\n\t\t} else {\n\t\t\t$args = [\n\t\t\t\t'fields'     => 'ids',\n\t\t\t\t'meta_key'   => $meta_key,\n\t\t\t\t'meta_value' => $value,\n\t\t\t\t'hide_empty' => false,\n\t\t\t\t'taxonomy'   => $this->get_setting( 'taxonomy' ),\n\t\t\t\t'number'     => 1\n\t\t\t];\n\n\t\t\t$terms = get_terms( $args );\n\n\t\t\tif ( ! empty( $terms ) && ! is_wp_error( $terms ) ) {\n\t\t\t\t$term_id = $terms[0];\n\t\t\t}\n\t\t}\n\n\t\t// Allow only id to be returned.\n\t\tif ( ! papi_is_admin() && $this->get_setting( 'fields' ) === 'ids' ) {\n\t\t\t$term = $this->get_term_value( $term_id );\n\t\t} elseif ( ! empty( $term_id ) ) {\n\t\t\t$term = get_term( $term_id );\n\t\t}\n\n\t\tif ( empty( $term ) || is_wp_error( $term ) ) {\n\t\t\t$term = $this->default_value;\n\t\t}\n\n\t\treturn $term;\n\t}\n\n\t/**\n\t * Get default settings.\n\t *\n\t * @return array\n\t */\n\tpublic function get_default_settings() {\n\t\treturn [\n\t\t\t'allow_clear' => true,\n\t\t\t'fields'      => '',\n\t\t\t'labels'      => [\n\t\t\t\t'select_taxonomy' => __( 'Select Taxonomy', 'papi' ),\n\t\t\t\t'select_item'     => __( 'Select %s term', 'papi' )\n\t\t\t],\n\t\t\t'layout'      => 'single', // Single or advanced\n\t\t\t'meta_key'    => '',\n\t\t\t'placeholder' => null,\n\t\t\t'taxonomy'    => '',\n\t\t\t'select2'     => true,\n\t\t\t'query'       => []\n\t\t];\n\t}\n\n\t/**\n\t * Get labels from taxonomies.\n\t *\n\t * @return array\n\t */\n\tprotected function get_labels() {\n\t\t$results = [];\n\n\t\tforeach ( $this->get_taxonomies() as $taxonomy ) {\n\t\t\tif ( taxonomy_exists( $taxonomy ) ) {\n\t\t\t\t$taxonomy_object    = get_taxonomy( $taxonomy );\n\t\t\t\t$results[$taxonomy] = $taxonomy_object->labels->name;\n\t\t\t}\n\t\t}\n\n\t\treturn $results;\n\t}\n\n\t/**\n\t * Get taxonomies.\n\t *\n\t * @return array\n\t */\n\tprotected function get_taxonomies() {\n\t\treturn papi_to_array( $this->get_setting( 'taxonomy' ) );\n\t}\n\n\t/**\n\t * Get terms for specified taxonomy.\n\t *\n\t * @param  string $taxonomy\n\t *\n\t * @return array\n\t */\n\tprotected function get_terms( $taxonomy ) {\n\t\t// Prepare arguments for get_terms.\n\t\t$query = $this->get_setting( 'query' );\n\t\t$args  = array_merge( [\n\t\t\t'fields' => 'id=>name'\n\t\t], $query );\n\n\t\t$terms = [];\n\t\tif ( taxonomy_exists( $taxonomy ) ) {\n\t\t\t$terms = get_terms( $taxonomy, $args );\n\t\t}\n\n\t\treturn $terms;\n\t}\n\n\t/**\n\t * Get matching value based on key from a term.\n\t *\n\t * @param  mixed $term\n\t *\n\t * @return mixed\n\t */\n\tprotected function get_term_value( $term ) {\n\t\t$meta_key = $this->get_setting( 'meta_key' );\n\n\t\tif ( is_numeric( $term ) ) {\n\t\t\t$term_id = $term;\n\t\t} else {\n\t\t\t$term = get_term( $term );\n\n\t\t\tif ( $term instanceof WP_Term === false ) {\n\t\t\t\treturn 0;\n\t\t\t}\n\n\t\t\t$term_id = $term->term_id;\n\t\t}\n\n\t\tif ( ! empty( $meta_key ) ) {\n\t\t\t$value = get_term_meta( $term_id, $meta_key, true );\n\t\t} else {\n\t\t\t$value = $term_id;\n\t\t}\n\n\t\treturn $value;\n\t}\n\n\t/**\n\t * Render property html.\n\t */\n\tpublic function html() {\n\t\t$settings          = $this->get_settings();\n\t\t$layout            = $settings->layout;\n\t\t$labels            = $this->get_labels();\n\t\t$taxonomies        = $this->get_taxonomies();\n\t\t$render_label      = count( $taxonomies ) > 1;\n\t\t$advanced          = $render_label && $layout === 'advanced';\n\t\t$single            = $render_label && $layout !== 'advanced';\n\t\t$classes           = count( $taxonomies ) > 1 ? '' : 'papi-fullwidth';\n\t\t$value             = $this->get_value();\n\t\t$selected_term     = get_term( $value );\n\t\t$selected_term     = is_wp_error( $selected_term ) || empty( $selected_term ) ? '' : $selected_term;\n\t\t$selected_taxonomy = empty( $selected_term ) ? reset( $taxonomies ) : $selected_term->taxonomy;\n\t\t$value             = $this->get_term_value( $value );\n\t\t$selected_label    = reset( $labels );\n\n\t\tif ( $settings->select2 ) {\n\t\t\t$classes = ' papi-component-select2';\n\t\t}\n\n\t\t?>\n\n\t\t<div class=\"papi-property-term <?php echo $advanced ? 'advanced' : ''; ?>\">\n\t\t\t<?php if ( $advanced ): ?>\n\t\t\t\t<table class=\"papi-table\">\n\t\t\t\t\t<tr>\n\t\t\t\t\t\t<td>\n\t\t\t\t\t\t\t<label for=\"<?php echo esc_attr( $this->html_id() ); ?>_taxonomy\">\n\t\t\t\t\t\t\t\t<?php echo esc_html( $settings->labels['select_taxonomy'] ); ?>\n\t\t\t\t\t\t\t</label>\n\t\t\t\t\t\t</td>\n\t\t\t\t\t\t<td>\n\t\t\t\t\t\t\t<select\n\t\t\t\t\t\t\t\tid=\"<?php echo esc_attr( $this->html_id() ); ?>_taxonomy\"\n\t\t\t\t\t\t\t\tclass=\"<?php echo esc_attr( $classes ); ?> papi-property-term-left\"\n\t\t\t\t\t\t\t\tdata-select-item=\"<?php echo esc_attr( $settings->labels['select_item'] ); ?>\"\n\t\t\t\t\t\t\t\tdata-term-query='<?php echo esc_attr( papi_maybe_json_encode( $settings->query ) ); ?>'\n\t\t\t\t\t\t\t\tdata-width=\"100%\"\n\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t<?php\n\t\t\t\t\t\t\t\tforeach ( $labels as $taxonomy => $label ) {\n\t\t\t\t\t\t\t\t\tpapi_render_html_tag( 'option', [\n\t\t\t\t\t\t\t\t\t\t'value'    => $taxonomy,\n\t\t\t\t\t\t\t\t\t\t'selected' => $taxonomy === $selected_taxonomy,\n\t\t\t\t\t\t\t\t\t\t$label\n\t\t\t\t\t\t\t\t\t] );\n\n\t\t\t\t\t\t\t\t\tif ( $selected ) {\n\t\t\t\t\t\t\t\t\t\t$selected_label = $label;\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t?>\n\t\t\t\t\t\t\t</select>\n\t\t\t\t\t\t</td>\n\t\t\t\t\t</tr>\n\t\t\t\t\t<tr>\n\t\t\t\t\t\t<td>\n\t\t\t\t\t\t\t<label for=\"<?php echo esc_attr( $this->html_id() ); ?>_terms\">\n\t\t\t\t\t\t\t\t<?php echo esc_html( sprintf( $settings->labels['select_item'], $selected_label ) ); ?>\n\t\t\t\t\t\t\t</label>\n\t\t\t\t\t\t</td>\n\t\t\t\t\t<td>\n\t\t\t<?php endif; ?>\n\n\t\t\t<?php\n\t\t\t\t$placeholder = ! is_null( $settings->placeholder ) ? $settings->placeholder : '';\n\t\t\t\t$placeholder = papi_is_empty( $placeholder ) ? '&nbsp;' : $placeholder;\n\t\t\t?>\n\n\t\t\t<select\n\t\t\t\tclass=\"<?php echo esc_attr( $classes ); ?>  papi-property-term-right\"\n\t\t\t\tid=\"<?php echo esc_attr( $this->html_id() ); ?>_terms\"\n\t\t\t\tname=\"<?php echo esc_attr( $this->html_name() ); ?>\"\n\t\t\t\tclass=\"<?php echo esc_attr( $classes ); ?>\"\n\t\t\t\tdata-allow-clear=\"<?php echo is_null( $settings->placeholder ) ? 'false' : 'true'; ?>\"\n\t\t\t\tdata-placeholder=\"<?php echo esc_attr( $placeholder ); ?>\"\n\t\t\t\tdata-width=\"100%\">\n\n\t\t\t\t<?php if ( ! is_null( $settings->placeholder ) ): ?>\n\t\t\t\t\t<option value=\"<?php echo esc_attr( $this->get_option( 'default', '' ) ); ?>\"><?php echo esc_html( $placeholder ); ?></option>\n\t\t\t\t<?php endif; ?>\n\n\t\t\t\t<?php foreach ( $taxonomies as $taxonomy ) : ?>\n\t\t\t\t\t<?php\n\t\t\t\t\tif ( $advanced && $taxonomy !== $selected_taxonomy ) {\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\n\t\t\t\t\t$terms = $this->get_terms( $taxonomy );\n\t\t\t\t\tif ( empty( $terms ) ) {\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\t\t\t\t\t?>\n\n\t\t\t\t\t<?php if ( $single ): ?>\n\t\t\t\t\t\t<optgroup label=\"<?php echo esc_attr( $labels[$taxonomy] ); ?>\">\n\t\t\t\t\t<?php endif; ?>\n\n\t\t\t\t\t<?php\n\t\t\t\t\tforeach ( $terms as $term_id => $term_name ) {\n\t\t\t\t\t\tif ( papi_is_empty( $term_name ) ) {\n\t\t\t\t\t\t\tcontinue;\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tpapi_render_html_tag( 'option', [\n\t\t\t\t\t\t\t'data-allow-clear' => $settings->allow_clear,\n\t\t\t\t\t\t\t'value'            => $this->get_term_value( $term_id ),\n\t\t\t\t\t\t\t'selected'         => $value === $this->get_term_value( $term_id ),\n\t\t\t\t\t\t\tesc_html( $term_name )\n\t\t\t\t\t\t] );\n\t\t\t\t\t}\n\t\t\t\t\t?>\n\n\t\t\t\t\t<?php if ( $single ): ?>\n\t\t\t\t\t\t</optgroup>\n\t\t\t\t\t<?php endif; ?>\n\n\t\t\t\t<?php endforeach; ?>\n\t\t\t</select>\n\n\t\t\t<?php if ( $advanced ): ?>\n\t\t\t\t\t</td>\n\t\t\t\t</tr>\n\t\t\t</table>\n\t\t\t<?php endif; ?>\n\n\t\t</div>\n\t\t<?php\n\t}\n}\n"
  },
  {
    "path": "src/properties/class-papi-property-text.php",
    "content": "<?php\n\n/**\n * HTML textarea property.\n */\nclass Papi_Property_Text extends Papi_Property {\n\n\t/**\n\t * Format the value of the property before it's returned\n\t * to WordPress admin or the site.\n\t *\n\t * @param  mixed  $value\n\t * @param  string $slug\n\t * @param  int    $post_id\n\t *\n\t * @return array\n\t */\n\tpublic function format_value( $value, $slug, $post_id ) {\n\t\tif ( ! $this->get_setting( 'allow_html' ) ) {\n\t\t\t$value = maybe_unserialize( $value );\n\n\t\t\tif ( ! is_string( $value ) ) {\n\t\t\t\t$value = '';\n\t\t\t}\n\n\t\t\t$value = wp_strip_all_tags( $value );\n\n\t\t\tif ( ! papi_is_admin() ) {\n\t\t\t\t$value = $this->get_setting( 'nl2br' ) ? nl2br( $value ) : $value;\n\t\t\t}\n\t\t}\n\n\t\treturn $value;\n\t}\n\n\t/**\n\t * Get default settings.\n\t *\n\t * @return array\n\t */\n\tpublic function get_default_settings() {\n\t\treturn [\n\t\t\t'allow_html' => false,\n\t\t\t'nl2br'      => true\n\t\t];\n\t}\n\n\t/**\n\t * Get value from the database.\n\t *\n\t * @return string\n\t */\n\tpublic function get_value() {\n\t\t$value = $this->format_value(\n\t\t\tparent::get_value(),\n\t\t\t$this->get_slug(),\n\t\t\tpapi_get_post_id()\n\t\t);\n\n\t\treturn $this->get_setting( 'allow_html' ) ? $value : esc_html( $value );\n\t}\n\n\t/**\n\t * Render property html.\n\t */\n\tpublic function html() {\n\t\tpapi_render_html_tag( 'textarea', [\n\t\t\t'class' => 'papi-property-text',\n\t\t\t'id'    => esc_attr( $this->html_id() ),\n\t\t\t'name'  => esc_attr( $this->html_name() ),\n\t\t\t$this->get_value()\n\t\t] );\n\t}\n}\n"
  },
  {
    "path": "src/properties/class-papi-property-url.php",
    "content": "<?php\n\n/**\n * HTML5 url input property.\n */\nclass Papi_Property_Url extends Papi_Property {\n\n\t/**\n\t * Get default settings.\n\t *\n\t * @return array\n\t */\n\tpublic function get_default_settings() {\n\t\treturn [\n\t\t\t'mediauploader' => false\n\t\t];\n\t}\n\n\t/**\n\t * Render property html.\n\t */\n\tpublic function html() {\n\t\t$settings = $this->get_settings();\n\n\t\tpapi_render_html_tag( 'input', [\n\t\t\t'class'   => $settings->mediauploader ? 'papi-url-media-input' : null,\n\t\t\t'id'      => esc_attr( $this->html_id() ),\n\t\t\t'name'    => esc_attr( $this->html_name() ),\n\t\t\t'type'    => 'url',\n\t\t\t'value'   => $this->get_value()\n\t\t] );\n\n\t\tif ( $settings->mediauploader ) {\n\t\t\techo '&nbsp;';\n\n\t\t\tpapi_render_html_tag( 'input', [\n\t\t\t\t'class'            => 'button papi-url-media-button',\n\t\t\t\t'data-papi-action' => 'mediauploader',\n\t\t\t\t'id'               => esc_attr( $this->html_id() ),\n\t\t\t\t'name'             => esc_attr( $this->html_name() . '_button' ),\n\t\t\t\t'type'             => 'button',\n\t\t\t\t'value'            => esc_attr__( 'Select file', 'papi' )\n\t\t\t] );\n\t\t}\n\t}\n\n\t/**\n\t * Change value after it's loaded from the database.\n\t *\n\t * @param  mixed  $value\n\t * @param  string $slug\n\t * @param  int    $post_id\n\t *\n\t * @return mixed\n\t */\n\tpublic function load_value( $value, $slug, $post_id ) {\n\t\tif ( filter_var( $value, FILTER_VALIDATE_URL ) ) {\n\t\t\treturn $value;\n\t\t}\n\t}\n\n\t/**\n\t * Update value before it's saved to the database.\n\t *\n\t * @param  mixed  $value\n\t * @param  string $slug\n\t * @param  int    $post_id\n\t *\n\t * @return mixed\n\t */\n\tpublic function update_value( $value, $slug, $post_id ) {\n\t\treturn $this->load_value( $value, $slug, $post_id );\n\t}\n}\n"
  },
  {
    "path": "src/properties/class-papi-property-user.php",
    "content": "<?php\n\n/**\n * User property that populates a dropdown.\n */\nclass Papi_Property_User extends Papi_Property_Dropdown {\n\n\t/**\n\t * The convert type.\n\t *\n\t * @var string\n\t */\n\tpublic $convert_type = 'int';\n\n\t/**\n\t * Format the value of the property before it's returned\n\t * to WordPress admin or the site.\n\t *\n\t * @param  mixed  $value\n\t * @param  string $slug\n\t * @param  int    $post_id\n\t *\n\t * @return null|WP_User\n\t */\n\tpublic function format_value( $value, $slug, $post_id ) {\n\t\tif ( is_object( $value ) && isset( $value->ID ) ) {\n\t\t\t$value = $value->ID;\n\t\t}\n\n\t\tif ( is_numeric( $value ) ) {\n\t\t\treturn $value === 0 ? null : new WP_User( $value );\n\t\t}\n\n\t\treturn $value;\n\t}\n\n\t/**\n\t * Get default settings.\n\t *\n\t * @return array\n\t */\n\tpublic function get_default_settings() {\n\t\treturn [\n\t\t\t'allow_clear'  => true,\n\t\t\t'capabilities' => [],\n\t\t\t'placeholder'  => '',\n\t\t\t'select2'      => true\n\t\t];\n\t}\n\n\t/**\n\t * Get value from database.\n\t *\n\t * @return int\n\t */\n\tpublic function get_value() {\n\t\t$user = parent::get_value();\n\n\t\tif ( is_object( $user ) && isset( $user->ID ) ) {\n\t\t\treturn $user->ID;\n\t\t}\n\n\t\treturn 0;\n\t}\n\n\t/**\n\t * Get users as dropdown items.\n\t *\n\t * @return array\n\t */\n\tpublic function get_items() {\n\t\t$capabilities = papi_to_array( $this->get_setting( 'capabilities' ) );\n\t\t$users        = get_users();\n\t\t$items        = [];\n\n\t\tforeach ( $users as $user ) {\n\t\t\t$allcaps = $user->allcaps;\n\n\t\t\tif ( count( array_diff( $capabilities, array_keys( $allcaps ) ) ) === 0 ) {\n\t\t\t\t$items[$user->display_name] = $user->ID;\n\t\t\t}\n\t\t}\n\n\t\tksort( $items );\n\n\t\treturn $items;\n\t}\n\n\t/**\n\t * Change value after it's loaded from the database.\n\t *\n\t * @param  mixed  $value\n\t * @param  string $slug\n\t * @param  int    $post_id\n\t *\n\t * @return int\n\t */\n\tpublic function load_value( $value, $slug, $post_id ) {\n\t\treturn (int) $value;\n\t}\n\n\t/**\n\t * Update value before it's saved to the database.\n\t *\n\t * @param mixed  $value\n\t * @param string $slug\n\t * @param int    $post_id\n\t *\n\t * @return int\n\t */\n\tpublic function update_value( $value, $slug, $post_id ) {\n\t\tif ( $value instanceof WP_User ) {\n\t\t\t$value = $value->ID;\n\t\t}\n\n\t\treturn (int) $value;\n\t}\n}\n"
  },
  {
    "path": "src/properties/class-papi-property.php",
    "content": "<?php\n\n/**\n * Base property that implements the rendering of\n * a property in WordPress admin.\n */\nclass Papi_Property extends Papi_Core_Property {\n\n\t/**\n\t * Get value.\n\t *\n\t * @return mixed\n\t */\n\tpublic function get_value() {\n\t\t$value = $this->get_option( 'value' );\n\n\t\tif ( papi_is_empty( $value ) ) {\n\t\t\t$type        = papi_get_meta_type();\n\t\t\t$slug        = $this->get_slug( true );\n\t\t\t$value       = papi_get_field( $slug, null, $type );\n\t\t\t$post_status = get_post_status( $this->get_post_id() );\n\n\t\t\tif ( papi_is_empty( $value ) && ( $post_status === false || $post_status === 'auto-draft' ) ) {\n\t\t\t\t$value = $this->get_option( 'default' );\n\t\t\t}\n\t\t}\n\n\t\tif ( papi_is_empty( $value ) ) {\n\t\t\treturn $this->default_value;\n\t\t}\n\n\t\tif ( $this->convert_type === 'string' ) {\n\t\t\t$value = papi_convert_to_string( $value );\n\t\t}\n\n\t\treturn papi_santize_data( $value );\n\t}\n\n\t/**\n\t * Render property html.\n\t */\n\tpublic function html() {\n\t}\n\n\t/**\n\t * Determine if the property can be rendered or not.\n\t *\n\t * @return bool\n\t */\n\tpublic function can_render() {\n\t\t// Check if current user can view the property.\n\t\tif ( ! $this->current_user_can() ) {\n\t\t\treturn false;\n\t\t}\n\n\t\t// A disabled property should not be rendered.\n\t\tif ( $this->disabled() ) {\n\t\t\treturn false;\n\t\t}\n\n\t\t// Check language option, so we don't render properties on a different language.\n\t\tif ( $lang = $this->get_option( 'lang' ) ) {\n\t\t\treturn in_array( papi_get_lang(), papi_to_array( $lang ), true );\n\t\t}\n\n\t\t// If no valid lang query string exists we have to override the display property.\n\t\treturn $this->get_option( 'lang' ) === false;\n\t}\n\n\t/**\n\t * Render the property.\n\t */\n\tpublic function render() {\n\t\t// Bail if we can't render the property.\n\t\tif ( ! $this->can_render() ) {\n\t\t\treturn;\n\t\t}\n\n\t\t// Override display with rules check.\n\t\tif ( $this->display() ) {\n\t\t\t$this->display = $this->render_is_allowed_by_rules();\n\t\t}\n\n\t\t// Render property.\n\t\t$this->render_row_html();\n\t}\n\n\t/**\n\t * Render AJAX request.\n\t */\n\tpublic function render_ajax_request() {\n\t\tpapi_render_property( $this );\n\t}\n\n\t/**\n\t * Render the property description.\n\t */\n\tprotected function render_description_html() {\n\t\tif ( papi_is_empty( $this->get_option( 'description' ) ) ) {\n\t\t\treturn;\n\t\t}\n\n\t\tpapi_render_html_tag( 'p', [\n\t\t\tpapi_nl2br( $this->get_option( 'description' ) )\n\t\t] );\n\t}\n\n\t/**\n\t * Output hidden input field that cointains which property is used.\n\t */\n\tprotected function render_hidden_html() {\n\t\t$slug = $this->get_option( 'slug' );\n\n\t\tif ( substr( $slug, - 1 ) === ']' ) {\n\t\t\t$slug = substr( $slug, 0, - 1 );\n\t\t\t$slug = papi_get_property_type_key( $slug );\n\t\t\t$slug .= ']';\n\t\t} else {\n\t\t\t$slug = papi_get_property_type_key( $slug );\n\t\t}\n\n\t\t$slug          = papify( $slug );\n\t\t$options       = $this->get_options();\n\t\t$property_json = base64_encode( papi_maybe_json_encode( $options ) );\n\n\t\tpapi_render_html_tag( 'input', [\n\t\t\t'data-property' => strtolower( $this->get_option( 'type' ) ),\n\t\t\t'name'          => $slug,\n\t\t\t'type'          => 'hidden',\n\t\t\t'value'         => $property_json\n\t\t] );\n\t}\n\n\t/**\n\t * Render label for the property.\n\t */\n\tprotected function render_label_html() {\n\t\t$title = $this->get_option( 'title' );\n\n\t\tpapi_render_html_tag( 'label', [\n\t\t\t'for'   => $this->html_id(),\n\t\t\t'title' => trim( $title . ' ' . papi_property_require_text( $this->get_options() ) ),\n\t\t\t$title,\n\t\t\tpapi_property_required_html( $this )\n\t\t] );\n\t}\n\n\t/**\n\t * Render property html.\n\t */\n\tprotected function render_property_html() {\n\t\tpapi_render_html_tag( 'div', [\n\t\t\t'class'         => 'papi-before-html ' . $this->get_option( 'before_class' ),\n\t\t\t'data-property' => $this->get_option( 'type' ),\n\t\t\tpapi_maybe_get_callable_value( $this->get_option( 'before_html' ) )\n\t\t] );\n\n\t\t$this->html();\n\n\t\tpapi_render_html_tag( 'div', [\n\t\t\t'class'         => 'papi-after-html ' . $this->get_option( 'after_class' ),\n\t\t\t'data-property' => $this->get_option( 'type' ),\n\t\t\tpapi_maybe_get_callable_value( $this->get_option( 'after_html' ) )\n\t\t] );\n\t}\n\n\t/**\n\t * Render the final html that is displayed in the table.\n\t */\n\tprotected function render_row_html() {\n\t\t$display_class = $this->display() ? '' : ' papi-hide';\n\t\t$rules_class   = papi_is_empty( $this->get_rules() ) ? '' : ' papi-rules-exists';\n\t\t$css_class     = trim( $display_class . $rules_class );\n\t\t$css_class    .= sprintf( ' papi-layout-%s', $this->get_option( 'layout' ) );\n\n\t\tif ( $this->get_option( 'raw' ) ) {\n\t\t\techo sprintf( '<div class=\"%s\">', esc_attr( $css_class ) );\n\t\t\t$this->render_property_html();\n\t\t\t$this->render_hidden_html();\n\t\t\t$this->render_rules_json();\n\t\t\techo '</div>';\n\t\t} else {\n\t\t\t?>\n\t\t\t<tr class=\"<?php echo esc_attr( $css_class ); ?>\">\n\t\t\t\t<?php if ( $this->get_option( 'sidebar' ) && $this->get_option( 'layout' ) === 'horizontal' ): ?>\n\t\t\t\t\t<td class=\"papi-table-sidebar\">\n\t\t\t\t\t\t<?php\n\t\t\t\t\t\t\t$this->render_label_html();\n\t\t\t\t\t\t\t$this->render_description_html();\n\t\t\t\t\t\t?>\n\t\t\t\t\t</td>\n\t\t\t\t<?php endif; ?>\n\t\t\t\t<td <?php echo $this->get_option( 'sidebar' ) && $this->get_option( 'layout' ) === 'horizontal' ? '' : 'colspan=\"2\"'; ?>>\n\t\t\t\t\t<?php\n\t\t\t\t\t// Render vertical layout where title and description is above the property.\n\t\t\t\t\tif ( $this->get_option( 'layout' ) === 'vertical' && $this->get_option( 'sidebar' ) ) {\n\t\t\t\t\t\t$this->render_label_html();\n\t\t\t\t\t\t$this->render_description_html();\n\t\t\t\t\t}\n\n\t\t\t\t\t$this->render_property_html();\n\t\t\t\t\t$this->render_hidden_html();\n\t\t\t\t\t$this->render_rules_json();\n\t\t\t\t\t?>\n\t\t\t\t</td>\n\t\t\t</tr>\n\t\t\t<?php\n\t\t}\n\t}\n\n\t/**\n\t * Render Conditional rules as script tag with JSON.\n\t */\n\tprotected function render_rules_json() {\n\t\t$rules = $this->get_rules();\n\n\t\tif ( empty( $rules ) ) {\n\t\t\treturn;\n\t\t}\n\n\t\t$rules = $this->conditional->prepare_rules( $rules, $this );\n\n\t\tpapi_render_html_tag( 'script', [\n\t\t\t'data-papi-rule-source-slug' => $this->html_name(),\n\t\t\t'data-papi-rules'            => 'true',\n\t\t\t'type'                       => 'application/json',\n\t\t\tpapi_maybe_json_encode( $rules )\n\t\t] );\n\t}\n}\n"
  },
  {
    "path": "src/query/class-papi-query.php",
    "content": "<?php\n\nclass Papi_Query {\n\n\t/**\n\t * The query arguments.\n\t *\n\t * @var array\n\t */\n\tprotected $args;\n\n\t/**\n\t * The default query arguments.\n\t *\n\t * @var array\n\t */\n\tprotected $default_args = [\n\t\t'entry_type' => ''\n\t];\n\n\t/**\n\t * The query type.\n\t *\n\t * @var string\n\t */\n\tprotected $type;\n\n\t/**\n\t * The query instance.\n\t *\n\t * @var WP_Term_Query|WP_Query\n\t */\n\tprotected $query;\n\n\t/**\n\t * Query constructor.\n\t *\n\t * @param array  $args\n\t * @param string $type\n\t */\n\tpublic function __construct( array $args = [], $type = 'post' ) {\n\t\t$this->type  = $type === 'page' ? 'post' : $type;\n\t\t$this->query = $this->get_query_class();\n\t\t$this->args  = $args;\n\t}\n\n\t/**\n\t * Dynamically access query properties.\n\t *\n\t * @param  string $key\n\t *\n\t * @return mixed\n\t */\n\tpublic function __get( $key ) {\n\t\tswitch ( $key ) {\n\t\t\tcase 'posts':\n\t\t\tcase 'terms':\n\t\t\t\treturn $this->get_result();\n\t\t\tdefault:\n\t\t\t\tbreak;\n\t\t}\n\t}\n\n\t/**\n\t * Get first item of result.\n\t *\n\t * @return array\n\t */\n\tpublic function first() {\n\t\t$result = $this->get_result();\n\n\t\treturn array_shift( $result );\n\t}\n\n\t/**\n\t * Parse query arguments.\n\t *\n\t * @param  array $args\n\t */\n\tpublic function parse_args( array $args ) {\n\t\t$args = array_merge( $this->default_args, $args );\n\n\t\t// Since a page type has defined post types we should use them.\n\t\t// With a fallback on `post_type` args or `any` value.\n\t\tif ( $this->type === 'post' ) {\n\t\t\t$args = $this->parse_post_args( $args );\n\t\t} else if ( $this->type === 'term' ) {\n\t\t\t$args = $this->parse_term_args( $args );\n\t\t}\n\n\t\t$this->args = $args;\n\t}\n\n\t/**\n\t * Parse post query arguments.\n\t *\n\t * @param  array $args\n\t *\n\t * @return array\n\t */\n\tprotected function parse_post_args( array $args ) {\n\t\tif ( isset( $args['page_type'] ) ) {\n\t\t\t$args['entry_type'] = $args['page_type'];\n\n\t\t\tunset( $args['page_type'] );\n\t\t}\n\n\t\t$entry_type = papi_get_entry_type_by_id( $args['entry_type'] );\n\n\t\tif ( $entry_type instanceof Papi_Page_Type ) {\n\t\t\t$args['post_type'] = papi_to_array( $entry_type->post_type );\n\t\t} else {\n\t\t\t$args['post_type'] = isset( $args['post_type'] ) ? $args['post_type'] : '';\n\t\t}\n\n\t\treturn $args;\n\t}\n\n\t/**\n\t * Parse term query arguments.\n\t *\n\t * @param  array $args\n\t *\n\t * @return array\n\t */\n\tprotected function parse_term_args( array $args ) {\n\t\tif ( isset( $args['taxonomy_type'] ) ) {\n\t\t\t$args['entry_type'] = $args['taxonomy_type'];\n\n\t\t\tunset( $args['taxonomy_type'] );\n\t\t}\n\n\t\t$entry_type = papi_get_entry_type_by_id( $args['entry_type'] );\n\n\t\tif ( $entry_type instanceof Papi_Taxonomy_Type ) {\n\t\t\t$args['taxonomy'] = papi_to_array( $entry_type->taxonomy );\n\t\t} else {\n\t\t\t$args['taxonomy'] = isset( $args['taxonomy'] ) ? $args['taxonomy'] : '';\n\t\t}\n\n\t\treturn $args;\n\t}\n\n\t/**\n\t * Get query object for right query type.\n\t *\n\t * @return WP_Query|WP_Term_Query\n\t */\n\tpublic function get_query_class() {\n\t\tswitch ( $this->type ) {\n\t\t\tcase 'post':\n\t\t\tcase 'page':\n\t\t\t\treturn new WP_Query;\n\t\t\tcase 'term':\n\t\t\t\t// `WP_Term_Query` was added in WordPress 4.6.\n\t\t\t\tif ( class_exists( 'WP_Term_Query' ) ) {\n\t\t\t\t\treturn new WP_Term_Query;\n\t\t\t\t}\n\n\t\t\t\tbreak;\n\t\t\tdefault:\n\t\t\t\tbreak;\n\t\t}\n\t}\n\n\t/**\n\t * Get real query arguments without Papi Query specific arguments.\n\t *\n\t * @return array\n\t */\n\tpublic function get_query_args() {\n\t\t$args = $this->args;\n\n\t\tif ( empty( $args['meta_query'] ) ) {\n\t\t\t// Add new meta key/value if `meta_key` or `meta_value` is empty.\n\t\t\tif ( empty( $args['meta_key'] ) || empty( $args['meta_value'] ) ) {\n\t\t\t\t$args['meta_key']   = papi_get_page_type_key();\n\t\t\t\t$args['meta_value'] = $args['entry_type'];\n\t\t\t} else if ( papi_entry_type_exists( $args['entry_type'] ) ) {\n\t\t\t\t$item = [\n\t\t\t\t\t'key'   => $args['meta_key'],\n\t\t\t\t\t'value' => $args['meta_value']\n\t\t\t\t];\n\n\t\t\t\t// Add `meta_compare` if set.\n\t\t\t\tif ( isset( $args['meta_compare'] ) ) {\n\t\t\t\t\t$item['compare'] = $args['meta_compare'];\n\n\t\t\t\t\tunset( $args['meta_compare'] );\n\t\t\t\t}\n\n\t\t\t\t// Add new meta query item.\n\t\t\t\t$args['meta_query'][] = $item;\n\n\t\t\t\t// Add Papi entry/page type meta query.\n\t\t\t\t$args['meta_query'][] = [\n\t\t\t\t\t'key'   => papi_get_page_type_key(),\n\t\t\t\t\t'value' => $args['entry_type']\n\t\t\t\t];\n\n\t\t\t\t// Add meta query relation when two query items.\n\t\t\t\tif ( isset( $args['relation'] ) ) {\n\t\t\t\t\t$args['meta_query']['relation'] = $args['relation'];\n\t\t\t\t} else {\n\t\t\t\t\t$args['meta_query']['relation'] = 'AND';\n\t\t\t\t}\n\n\t\t\t\tunset( $args['meta_key'] );\n\t\t\t\tunset( $args['meta_value'] );\n\t\t\t}\n\t\t} else if ( papi_entry_type_exists( $args['entry_type'] ) ) {\n\t\t\t// Add Papi entry/page type meta query.\n\t\t\t$args['meta_query'][] = [\n\t\t\t\t'key'   => papi_get_page_type_key(),\n\t\t\t\t'value' => $args['entry_type']\n\t\t\t];\n\n\t\t\t// Add meta query relation if not set.\n\t\t\tif ( ! isset( $args['meta_query']['relation'] ) ) {\n\t\t\t\t$args['meta_query']['relation'] = 'AND';\n\t\t\t}\n\t\t}\n\n\t\t// Since the real query classes don't support\n\t\t// custom arguments the should be deleted.\n\t\tforeach ( array_keys( $this->default_args ) as $key ) {\n\t\t\tif ( isset( $args[$key] ) ) {\n\t\t\t\tunset( $args[$key] );\n\t\t\t}\n\t\t}\n\n\t\treturn $args;\n\t}\n\n\t/**\n\t * Get result.\n\t *\n\t * Works for all query types.\n\t *\n\t * @return array\n\t */\n\tpublic function get_result() {\n\t\tif ( ! method_exists( $this->query, 'query' ) ) {\n\t\t\treturn [];\n\t\t}\n\n\t\t$this->parse_args( $this->args );\n\n\t\treturn $this->query->query( $this->get_query_args() );\n\t}\n\n\t/**\n\t * Get last item of result.\n\t *\n\t * @return array\n\t */\n\tpublic function last() {\n\t\t$result = $this->get_result();\n\n\t\treturn array_pop( $result );\n\t}\n}\n"
  },
  {
    "path": "src/rest-api/class-papi-rest-api-post.php",
    "content": "<?php\n\nclass Papi_REST_API_Post {\n\n\t/**\n\t * REST API Post construct.\n\t */\n\tpublic function __construct() {\n\t\tadd_filter( 'the_post', [$this, 'get_post'] );\n\t\tadd_action( 'rest_api_init', [$this, 'setup_fields'] );\n\t}\n\n\t/**\n\t * Get page type.\n\t *\n\t * @param  array           $data\n\t * @param  string          $field_name\n\t * @param  WP_REST_Request $request\n\t *\n\t * @return array\n\t */\n\tpublic function get_page_type( array $data, $field_name, $request ) {\n\t\tif ( ! isset( $data['ID'] ) ) {\n\t\t\treturn '';\n\t\t}\n\n\t\treturn papi_get_page_type_id( $data['ID'] ) ?: '';\n\t}\n\n\t/**\n\t * Filter the post.\n\t *\n\t * Register all properties with `register_meta` and prepare response filter.\n\t *\n\t * @param  WP_Post $post\n\t *\n\t * @return WP_Post\n\t */\n\tpublic function get_post( WP_Post $post ) {\n\t\tif ( ! ( $page_type = papi_get_entry_type_by_meta_id( $post->ID ) ) ) {\n\t\t\treturn $post;\n\t\t}\n\n\t\t// Register all properties fields with register meta.\n\t\tforeach ( $page_type->get_properties() as $property ) {\n\t\t\t$property->register();\n\t\t}\n\n\t\t// Add filter to prepare the response for a post type.\n\t\tadd_filter( 'rest_prepare_' . $post->post_type, [$this, 'prepare_response'] );\n\n\t\treturn $post;\n\t}\n\n\t/**\n\t * Prepare response.\n\t *\n\t * @param  WP_REST_Response $response\n\t *\n\t * @return WP_REST_Response\n\t */\n\tpublic function prepare_response( $response ) {\n\t\tif ( ! isset( $response->data['meta'] ) ) {\n\t\t\treturn $response;\n\t\t}\n\n\t\tforeach ( $response->data['meta'] as $key => $value ) {\n\t\t\t$response->data['meta'][$key] = papi_get_field( $key, $value, 'post' );\n\t\t}\n\n\t\treturn $response;\n\t}\n\n\t/**\n\t * Setup REST API fields.\n\t */\n\tpublic function setup_fields() {\n\t\tif ( ! function_exists( 'register_rest_field' ) ) {\n\t\t\treturn;\n\t\t}\n\n\t\t$post_types = papi_get_post_types();\n\n\t\tforeach ( $post_types as $post_type ) {\n\t\t\tregister_rest_field( $post_type, 'page_type', [\n\t\t\t\t'get_callback' => [$this, 'get_page_type']\n\t\t\t] );\n\t\t}\n\t}\n}\n\nnew Papi_REST_API_Post;\n"
  },
  {
    "path": "src/rest-api/class-papi-rest-api-settings.php",
    "content": "<?php\n\nclass Papi_REST_API_Settings {\n\n\t/**\n\t * The post store class.\n\t *\n\t * @var Papi_Post_Store\n\t */\n\tprotected $entries;\n\n\t/**\n\t * REST API Settings construct.\n\t */\n\tpublic function __construct() {\n\t\t// Hook into the REST API.\n\t\tadd_action( 'rest_api_init', [$this, 'register'] );\n\t\tadd_filter( 'rest_pre_get_setting', [$this, 'pre_get_setting'] );\n\t}\n\n\t/**\n\t * Register options properties.\n\t */\n\tpublic function register() {\n\t\t// Fetch all options entries.\n\t\t$this->entries = papi_get_all_entry_types( [\n\t\t\t'types' => 'option'\n\t\t] );\n\n\t\tforeach ( $this->entries as $entry ) {\n\t\t\tforeach ( $entry->get_properties() as $property ) {\n\t\t\t\t$property->register( 'option' );\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * Setup request after callbacks filter so it's only runs on settings endpoint.\n\t *\n\t * When the filter is added the `rest_pre_get_setting` will be removed since it's\n\t * not used to anything good.\n\t *\n\t * @param  mixed $value\n\t *\n\t * @return mixed\n\t */\n\tpublic function pre_get_setting( $value ) {\n\t\tif ( ! has_filter( 'rest_request_after_callbacks', [$this, 'prepare_response'] ) ) {\n\t\t\tadd_filter( 'rest_request_after_callbacks', [$this, 'prepare_response'] );\n\t\t\tremove_filter( 'rest_pre_get_setting', [$this, 'pre_get_setting'] );\n\t\t}\n\n\t\treturn $value;\n\t}\n\n\t/**\n\t * Get setting value for a property.\n\t *\n\t * @param  string $key\n\t * @param  string $value\n\t *\n\t * @return mixed\n\t */\n\tpublic function get_setting( $key, $value ) {\n\t\t$property = null;\n\n\t\tforeach ( (array) $this->entries as $entry ) {\n\t\t\tif ( $property = $entry->get_property( $key ) ) {\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\n\t\tif ( is_null( $property ) ) {\n\t\t\treturn $value;\n\t\t}\n\n\t\t$value = papi_get_option( $key );\n\n\t\treturn $property->rest_prepare_value( $value );\n\t}\n\n\t/**\n\t * Prepare settings response.\n\t *\n\t * @param  WP_HTTP_Response $response\n\t *\n\t * @return array\n\t */\n\tpublic function prepare_response( $response ) {\n\t\t$response = (array) $response;\n\n\t\tforeach ( $response as $key => $value ) {\n\t\t\t$setting = $this->get_setting( $key, $value );\n\n\t\t\tif ( $setting !== $value ) {\n\t\t\t\t$response[$key] = $setting;\n\t\t\t}\n\t\t}\n\n\t\treturn $response;\n\t}\n}\n\nnew Papi_REST_API_Settings;\n"
  },
  {
    "path": "src/rest-api/class-papi-rest-api.php",
    "content": "<?php\n\nfinal class Papi_REST_API {\n\n\t/**\n\t * The constructor.\n\t */\n\tpublic function __construct() {\n\t\t$this->load_files();\n\t\t$this->setup_actions();\n\t}\n\n\t/**\n\t * Load admin files that are not loaded by the autoload.\n\t */\n\tprotected function load_files() {\n\t\trequire_once __DIR__ . '/class-papi-rest-api-post.php';\n\t\trequire_once __DIR__ . '/class-papi-rest-api-settings.php';\n\t}\n\n\t/**\n\t * Register REST API routes.\n\t */\n\tpublic function register_routes() {\n\t}\n\n\t/**\n\t * REST API init callback.\n\t */\n\tpublic function rest_api_init() {\n\t\tpapi_get_all_entry_types();\n\t}\n\n\t/**\n\t * Setup actions.\n\t */\n\tprotected function setup_actions() {\n\t\tadd_action( 'rest_api_init', [$this, 'rest_api_init'] );\n\t\tadd_action( 'rest_api_init', [$this, 'register_routes'] );\n\t}\n}\n\nnew Papi_REST_API;\n"
  },
  {
    "path": "src/stores/class-papi-option-store.php",
    "content": "<?php\n\n/**\n * Option store implementation of Papi meta store.\n */\nclass Papi_Option_Store extends Papi_Core_Meta_Store {\n\n\t/**\n\t * The meta type.\n\t *\n\t * @var string\n\t */\n\tprotected $type = 'option';\n\n\t/**\n\t * The constructor.\n\t *\n\t * @param int $id\n\t */\n\tpublic function __construct( $id = 0 ) {\n\t\t// Options don't have a id so set it to zero.\n\t\t$this->id = 0;\n\t}\n\n\t/**\n\t * Load property from page type.\n\t *\n\t * @param  string $slug\n\t * @param  string $child_slug\n\t *\n\t * @return null|object\n\t */\n\tpublic function get_property( $slug, $child_slug = '' ) {\n\t\t$entry_type_id = papi_get_qs( 'page' );\n\n\t\tif ( empty( $entry_type_id ) ) {\n\t\t\t$property   = null;\n\t\t\t$entry_types = papi_get_all_entry_types( [\n\t\t\t\t'types' => 'option'\n\t\t\t] );\n\n\t\t\tforeach ( $entry_types as $entry_type ) {\n\t\t\t\tif ( $property = $entry_type->get_property( $slug, $child_slug ) ) {\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif ( is_null( $property ) ) {\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\treturn $property;\n\t\t}\n\n\t\t$entry_type = papi_get_entry_type_by_id( $entry_type_id );\n\n\t\tif ( $entry_type instanceof Papi_Option_Type === false ) {\n\t\t\treturn;\n\t\t}\n\n\t\tif ( $property = $entry_type->get_property( $slug, $child_slug ) ) {\n\t\t\treturn $this->prepare_property( $property );\n\t\t}\n\t}\n\n\t/**\n\t * Check if it's a valid store.\n\t *\n\t * @return bool\n\t */\n\tpublic function valid() {\n\t\treturn $this->id === 0;\n\t}\n}\n"
  },
  {
    "path": "src/stores/class-papi-post-store.php",
    "content": "<?php\n\n/**\n * Post store implementation of Papi meta store.\n */\nclass Papi_Post_Store extends Papi_Core_Meta_Store {\n\n\t/**\n\t * The WordPress post.\n\t *\n\t * @var object\n\t */\n\tprotected $post;\n\n\t/**\n\t * The meta type.\n\t *\n\t * @var string\n\t */\n\tprotected $type = 'post';\n\n\t/**\n\t * The constructor.\n\t *\n\t * @param int $id\n\t */\n\tpublic function __construct( $id = 0 ) {\n\t\t$this->id         = papi_get_post_id( $id );\n\t\t$this->post       = get_post( $this->id );\n\t\t$id               = papi_get_page_type_id( $this->id );\n\t\t$this->type_class = papi_get_entry_type_by_id( $id );\n\t}\n\n\t/**\n\t * Get the permalink for the page.\n\t *\n\t * @return string\n\t */\n\tpublic function get_permalink() {\n\t\treturn get_permalink( $this->id );\n\t}\n\n\t/**\n\t * Get the WordPress post object.\n\t *\n\t * @return WP_Post\n\t */\n\tpublic function get_post() {\n\t\treturn $this->post;\n\t}\n\n\t/**\n\t * Get the post status of a page.\n\t *\n\t * @return string\n\t */\n\tpublic function get_status() {\n\t\treturn get_post_status( $this->id );\n\t}\n\n\t/**\n\t * Load property from page type.\n\t *\n\t * @param  string $slug\n\t * @param  string $child_slug\n\t *\n\t * @return null|Papi_Core_Property\n\t */\n\tpublic function get_property( $slug, $child_slug = '' ) {\n\t\t$page_type_id = papi_get_page_type_id( $this->id );\n\t\t$page_type    = papi_get_entry_type_by_id( $page_type_id );\n\n\t\tif ( $page_type instanceof Papi_Page_Type === false ) {\n\t\t\treturn;\n\t\t}\n\n\t\tif ( $property = $page_type->get_property( $slug, $child_slug ) ) {\n\t\t\treturn $this->prepare_property( $property );\n\t\t}\n\t}\n\n\t/**\n\t * Prepare load value.\n\t *\n\t * @param  Papi_Core_Property $property\n\t * @param  mixed              $value\n\t *\n\t * @return mixed\n\t */\n\tprotected function prepare_load_value( Papi_Core_Property $property, $value ) {\n\t\tif ( $property->overwrite ) {\n\t\t\t// Clear post cache to solve issue with cached post objects\n\t\t\t// when selecting post field.\n\t\t\tclean_post_cache( $this->id );\n\n\t\t\t$slug    = $property->get_slug( true );\n\t\t\t$context = papi_is_admin() ? 'edit' : 'display';\n\t\t\t$value   = get_post_field( $slug, $this->id, $context );\n\t\t}\n\n\t\treturn $value;\n\t}\n\n\t/**\n\t * Check if the page has the post object and that it's not null.\n\t *\n\t * @return bool\n\t */\n\tpublic function valid() {\n\t\treturn ! is_null( $this->post );\n\t}\n}\n"
  },
  {
    "path": "src/stores/class-papi-term-store.php",
    "content": "<?php\n\n/**\n * Term store implementation of Papi meta store.\n */\nclass Papi_Term_Store extends Papi_Core_Meta_Store {\n\n\t/**\n\t * The WordPress term.\n\t *\n\t * @var object\n\t */\n\tprotected $term;\n\n\t/**\n\t * The meta type.\n\t *\n\t * @var string\n\t */\n\tprotected $type = 'term';\n\n\t/**\n\t * The constructor.\n\t *\n\t * @param int $id\n\t */\n\tpublic function __construct( $id = 0 ) {\n\t\t$this->id         = papi_get_term_id( $id );\n\t\t$this->term       = get_term( $this->id, '' );\n\t\t$id               = papi_get_taxonomy_type_id( $this->id );\n\t\t$this->type_class = papi_get_entry_type_by_id( $id );\n\t}\n\n\t/**\n\t * Get the permalink for the term.\n\t *\n\t * @return string\n\t */\n\tpublic function get_permalink() {\n\t\treturn get_term_link( $this->id );\n\t}\n\n\t/**\n\t * Get the WordPress term object.\n\t *\n\t * @return WP_Term\n\t */\n\tpublic function get_term() {\n\t\treturn $this->term;\n\t}\n\n\t/**\n\t * Check if the term is a valid term object.\n\t *\n\t * @return bool\n\t */\n\tpublic function valid() {\n\t\treturn $this->term instanceof WP_Term;\n\t}\n\n\t/**\n\t * Load property from page type.\n\t *\n\t * @param  string $slug\n\t * @param  string $child_slug\n\t *\n\t * @return null|Papi_Core_Property\n\t */\n\tpublic function get_property( $slug, $child_slug = '' ) {\n\t\t$taxonomy_type_id = papi_get_taxonomy_type_id( $this->id, 'term' );\n\t\t$taxonomy_type    = papi_get_entry_type_by_id( $taxonomy_type_id );\n\n\t\tif ( $taxonomy_type instanceof Papi_Taxonomy_Type === false ) {\n\t\t\treturn;\n\t\t}\n\n\t\tif ( $property = $taxonomy_type->get_property( $slug, $child_slug ) ) {\n\t\t\treturn $this->prepare_property( $property );\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "src/types/class-papi-attachment-type.php",
    "content": "<?php\n\n/**\n * Papi type that handle attachment post type, post data\n * and rendering. All attachment types should extend this\n * class.\n */\nclass Papi_Attachment_Type extends Papi_Page_Type {\n\n\t/**\n\t * The post type to register the type with.\n\t *\n\t * @var string\n\t */\n\tpublic $post_type = 'attachment';\n\n\t/**\n\t * The type name.\n\t *\n\t * @var string\n\t */\n\tpublic $type = 'attachment';\n\n\t/**\n\t * Get post type.\n\t *\n\t * @return string\n\t */\n\tpublic function get_post_type() {\n\t\treturn $this->post_type[0];\n\t}\n\n\t/**\n\t * Setup filters.\n\t */\n\tprotected function setup_filters() {\n\t\t// Don't add any filters on post.php page.\n\t\tif ( ! isset( $_GET['post'] ) ) {\n\t\t\tadd_filter( 'attachment_fields_to_edit', [$this, 'edit_attachment'], 10, 2 );\n\t\t\tadd_filter( 'attachment_fields_to_save', [$this, 'save_attachment'], 10 );\n\t\t}\n\t}\n\n\t/**\n\t * Add attachment fields.\n\t *\n\t * @param  array   $form_fields\n\t * @param  WP_Post $post\n\t *\n\t * @return array\n\t */\n\tpublic function edit_attachment( $form_fields, $post ) {\n\t\tforeach ( $this->get_boxes() as $box ) {\n\t\t\tif ( ! empty( $box->title ) ) {\n\t\t\t\t$form_fields['papi-media-title-' . uniqid()] = [\n\t\t\t\t\t'label' => '',\n\t\t\t\t\t'input' => 'html',\n\t\t\t\t\t'html'  => '<h4 class=\"papi-media-title\">' . $box->title . '</h4>'\n\t\t\t\t];\n\t\t\t}\n\n\t\t\tforeach ( $box->properties as $prop ) {\n\t\t\t\t// Raw output is required.\n\t\t\t\t$prop->raw = true;\n\n\t\t\t\t// Set post id to the property.\n\t\t\t\t$prop->set_post_id( $post->ID );\n\n\t\t\t\t// Add property to form fields.\n\t\t\t\t$form_fields[$prop->get_slug()] = [\n\t\t\t\t\t'label' => $prop->title,\n\t\t\t\t\t'input' => 'html',\n\t\t\t\t\t'helps' => $prop->description,\n\t\t\t\t\t'html'  => papi_maybe_get_callable_value(\n\t\t\t\t\t\t'papi_render_property',\n\t\t\t\t\t\t$prop\n\t\t\t\t\t)\n\t\t\t\t];\n\t\t\t}\n\t\t}\n\n\t\t// Add nonce field.\n\t\t$form_fields['papi_meta_nonce'] = [\n\t\t\t'label' => '',\n\t\t\t'input' => 'html',\n\t\t\t'html'  => sprintf(\n\t\t\t\t'<input name=\"papi_meta_nonce\" type=\"hidden\" value=\"%s\" />',\n\t\t\t\twp_create_nonce( 'papi_save_data' )\n\t\t\t)\n\t\t];\n\n\t\treturn $form_fields;\n\t}\n\n\t/**\n\t * Save attachment post data.\n\t *\n\t * @param  array $post\n\t *\n\t * @return array\n\t */\n\tpublic function save_attachment( $post ) {\n\t\tupdate_post_meta( $post['ID'], papi_get_page_type_key(), $this->get_id() );\n\n\t\t$handler = new Papi_Admin_Meta_Handler();\n\t\t$handler->save_meta_boxes( $post['ID'], $post );\n\n\t\treturn $post;\n\t}\n\n\t/**\n\t * Check if the entry type is a singleton.\n\t *\n\t * @return bool\n\t */\n\tpublic function singleton() {\n\t\t$key = sprintf( 'entry_type_id.post_type.%s', $this->get_post_type() );\n\n\t\tif ( papi()->exists( $key ) ) {\n\t\t\treturn true;\n\t\t}\n\n\t\tpapi()->singleton( $key, $this->get_id() );\n\n\t\treturn false;\n\t}\n}\n"
  },
  {
    "path": "src/types/class-papi-entry-type.php",
    "content": "<?php\n\n/**\n * Papi Entry Type is a base class that can\n * register boxes with properties.\n */\nclass Papi_Entry_Type extends Papi_Core_Type {\n\n\t/**\n\t * The array of meta boxes to register.\n\t *\n\t * @var array\n\t */\n\tprotected $boxes = [];\n\n\t/**\n\t * Menu to render entry type on.\n\t *\n\t * @var string\n\t */\n\tpublic $menu = '';\n\n\t/**\n\t * Load all boxes.\n\t *\n\t * @var bool\n\t */\n\tprotected $load_boxes = false;\n\n\t/**\n\t * Show help tabs.\n\t *\n\t * @return bool\n\t */\n\tpublic $show_help_tabs = true;\n\n\t/**\n\t * Show screen options.\n\t *\n\t * @var bool\n\t */\n\tpublic $show_screen_options = true;\n\n\t/**\n\t * The type name.\n\t *\n\t * @var string\n\t */\n\tpublic $type = 'entry';\n\n\t/**\n\t * Add help tabs.\n\t */\n\tpublic function add_help_tabs() {\n\t\t$help   = $this->help();\n\t\t$screen = get_current_screen();\n\n\t\t// No screen available.\n\t\tif ( $screen instanceof WP_Screen === false ) {\n\t\t\treturn;\n\t\t}\n\n\t\t// Clean up all existing tabs.\n\t\tif ( ! $this->show_help_tabs || ! empty( $help ) ) {\n\t\t\t$screen->remove_help_tabs();\n\t\t}\n\n\t\t// No new help tabs available.\n\t\tif ( empty( $help ) ) {\n\t\t\treturn;\n\t\t}\n\n\t\t// Add help sidebar content. By default it will be disabled\n\t\t// since `help_sidebar` method returns false.\n\t\t$help_sidebar = $this->help_sidebar();\n\t\t$help_sidebar = papi_maybe_get_callable_value( $help_sidebar );\n\t\t$screen->set_help_sidebar( wpautop( $help_sidebar ) );\n\n\t\tforeach ( $help as $key => $value ) {\n\t\t\t$args = [\n\t\t\t\t'id'    => papi_html_name( $key ),\n\t\t\t\t'title' => $key\n\t\t\t];\n\n\t\t\tif ( is_callable( $value ) ) {\n\t\t\t\t$args['callback'] = function () use ( $value ) {\n\t\t\t\t\treturn wpautop( $value() );\n\t\t\t\t};\n\t\t\t} else {\n\t\t\t\t$args['content'] = wpautop( $value );\n\t\t\t}\n\n\t\t\t$screen->add_help_tab( $args );\n\t\t}\n\t}\n\n\t/**\n\t * Get custom admin body classes.\n\t *\n\t * @return array\n\t */\n\tpublic function body_classes() {\n\t\treturn [];\n\t}\n\n\t/**\n\t * Add new meta box with properties.\n\t *\n\t * @param mixed $file_or_options\n\t * @param array $properties\n\t */\n\tprotected function box( $file_or_options = [], $properties = [] ) {\n\t\tif ( ! is_string( $file_or_options ) && ! is_array( $file_or_options ) && ! is_object( $file_or_options ) ) {\n\t\t\treturn;\n\t\t}\n\n\t\tlist( $options, $properties ) = papi_get_options_and_properties(\n\t\t\t$file_or_options,\n\t\t\t$properties,\n\t\t\ttrue\n\t\t);\n\n\t\t// Check so we have a post the to add the box to.\n\t\t// @codeCoverageIgnoreStart\n\t\tif ( ! $this->load_boxes ) {\n\t\t\treturn;\n\t\t}\n\t\t// @codeCoverageIgnoreEnd\n\n\t\tif ( is_callable( $properties ) ) {\n\t\t\t$properties = call_user_func( $properties );\n\t\t}\n\n\t\t// Check and convert all non properties objects to properties objects.\n\t\t$properties = $this->convert_properties( $properties );\n\n\t\t// Create a core box instance and add it to the boxes array.\n\t\tarray_push( $this->boxes, new Papi_Core_Box( $options, $properties ) );\n\t}\n\n\t/**\n\t * Call parent register if it exists\n\t * to collect boxes on the parent entry type.\n\t */\n\tprotected function call_parent_register() {\n\t\t$parent_class = get_parent_class( $this );\n\n\t\tif ( ! method_exists( $parent_class, 'register' ) ) {\n\t\t\treturn;\n\t\t}\n\n\t\t$rc = new ReflectionClass( $parent_class );\n\n\t\t// Bail if not instantiable.\n\t\tif ( ! $rc->isinstantiable() ) {\n\t\t\treturn;\n\t\t}\n\n\t\t$parent = $rc->newInstance();\n\t\t$parent->register();\n\t\t$this->boxes = $parent->get_boxes();\n\t}\n\n\t/**\n\t * Convert properties to properties objects.\n\t *\n\t * @param  array|object $properties\n\t *\n\t * @return array\n\t */\n\tprotected function convert_properties( $properties ) {\n\t\tif ( is_array( $properties ) ) {\n\t\t\tif ( isset( $properties['type'] ) ) {\n\t\t\t\t$properties = [$properties];\n\t\t\t} else if ( isset( $properties[0] ) && $properties[0] instanceof Papi_Core_Tab ) {\n\t\t\t\tforeach ( $properties as $items ) {\n\t\t\t\t\t$items->properties = array_map( 'papi_get_property_type', $items->properties );\n\t\t\t\t}\n\n\t\t\t\treturn $properties;\n\t\t\t}\n\t\t}\n\n\t\tif ( is_object( $properties ) ) {\n\t\t\t$properties = papi_get_property_type( $properties );\n\t\t}\n\n\t\tif ( papi_is_property( $properties ) ) {\n\t\t\t$properties = [$properties];\n\t\t}\n\n\t\t$properties = is_array( $properties ) ? $properties : [];\n\t\t$properties = array_map( 'papi_get_property_type', $properties );\n\n\t\treturn array_filter( $properties, 'papi_is_property' );\n\t}\n\n\t/**\n\t * Modify fields that are returned from the entry type.\n\t *\n\t * @param  array $fields\n\t *\n\t * @return array\n\t */\n\tpublic function fields( $fields ) {\n\t\treturn $fields;\n\t}\n\n\t/**\n\t * Get admin body css classes.\n\t *\n\t * Use `body_classes` method to add custom admin body classes.\n\t *\n\t * @return array\n\t */\n\tpublic function get_body_classes() {\n\t\t$arr = $this->body_classes();\n\t\t$arr = is_string( $arr ) ? [$arr] : $arr;\n\t\t$arr = is_array( $arr ) ? $arr : [];\n\n\t\treturn array_merge( $arr, [] );\n\t}\n\n\t/**\n\t * Get boxes from the page type.\n\t *\n\t * @return array\n\t */\n\tpublic function get_boxes() {\n\t\tif ( empty( $this->boxes ) && $this->load_boxes === false ) {\n\t\t\tif ( ! method_exists( $this, 'register' ) ) {\n\t\t\t\treturn [];\n\t\t\t}\n\n\t\t\t$this->load_boxes = true;\n\n\t\t\t$this->call_parent_register();\n\t\t\t$this->register();\n\t\t}\n\n\t\t// Merge boxes, e.g a parent box with a child box.\n\t\t$this->boxes = $this->merge_boxes( $this->boxes );\n\n\t\t/**\n\t\t * Modify boxes array.\n\t\t *\n\t\t * @param array  $boxes\n\t\t * @param string $id\n\t\t */\n\t\t$this->boxes = apply_filters( 'papi/get_boxes', $this->boxes, $this->get_id() );\n\t\t$this->boxes = is_array( $this->boxes ) ? $this->boxes : [];\n\n\t\t// Go through all boxes and only add boxes\n\t\t// that is a array and remove boxes that isn't\n\t\t// a instance of core box class.\n\t\tforeach ( $this->boxes as $index => $box ) {\n\t\t\tif ( is_array( $box ) ) {\n\t\t\t\tif ( is_string( $index ) ) {\n\t\t\t\t\t$box['title'] = $index;\n\t\t\t\t}\n\n\t\t\t\t$this->box( $box );\n\n\t\t\t\tunset( $this->boxes[$index] );\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\t// Remove boxes that isn't a instanceof core box class.\n\t\t\tif ( $box instanceof Papi_Core_Box === false ) {\n\t\t\t\tunset( $this->boxes[$index] );\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\t// Remove box if site id isn't zero and isn't the current blog id.\n\t\t\tif ( $box->site_id !== 0 && $box->site_id !== get_current_blog_id() ) {\n\t\t\t\tunset( $this->boxes[$index] );\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t}\n\n\t\treturn papi_sort_order( array_reverse( $this->boxes ) );\n\t}\n\n\t/**\n\t * Get labels that should be changed\n\t * when using `fill_labels` option.\n\t *\n\t * @return array\n\t */\n\tpublic function get_labels() {\n\t\treturn [];\n\t}\n\n\t/**\n\t * Get menu to render entry type on.\n\t *\n\t * @return string\n\t */\n\tpublic function get_menu() {\n\t\treturn $this->menu;\n\t}\n\n\t/**\n\t * Get property from entry type.\n\t *\n\t * @param  string $slug\n\t * @param  string $child_slug\n\t *\n\t * @return Papi_Property\n\t */\n\tpublic function get_property( $slug, $child_slug = '' ) {\n\t\t$boxes = $this->get_boxes();\n\t\t$parts = preg_match( '/\\[\\d+\\]/', $slug ) ? preg_split( '/\\[\\d+\\]/', $slug ) : explode( '[', $slug );\n\t\t$parts = array_map( function ( $part ) {\n\t\t\treturn preg_replace( '/(\\[|\\])/', '', $part );\n\t\t}, $parts );\n\n\t\tif ( count( $parts ) > 1 ) {\n\t\t\t$property = null;\n\n\t\t\tfor ( $i = 0, $l = count( $parts ); $i < $l; $i++ ) {\n\t\t\t\t$child    = isset( $parts[$i + 1] ) ? $parts[$i + 1] : '';\n\t\t\t\t$property = $this->get_property( $parts[$i], $child );\n\n\t\t\t\tif ( isset( $parts[$i + 1] ) ) {\n\t\t\t\t\t$i++;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t/**\n\t\t\t * Modify property.\n\t\t\t *\n\t\t\t * @param  Papi_Core_Property $property\n\t\t\t */\n\t\t\treturn apply_filters( 'papi/get_property', $property );\n\t\t}\n\n\t\tforeach ( $boxes as $box ) {\n\t\t\tforeach ( $box->properties as $property ) {\n\t\t\t\t$property = papi_get_property_type( $property );\n\n\t\t\t\tif ( papi_is_property( $property ) && $property->match_slug( $slug ) ) {\n\t\t\t\t\tif ( empty( $child_slug ) ) {\n\t\t\t\t\t\t/**\n\t\t\t\t\t\t * Modify property.\n\t\t\t\t\t\t *\n\t\t\t\t\t\t * @param  Papi_Core_Property $property\n\t\t\t\t\t\t */\n\t\t\t\t\t\treturn apply_filters( 'papi/get_property', $property );\n\t\t\t\t\t}\n\n\t\t\t\t\t$property = $property->get_child_property( $child_slug );\n\n\t\t\t\t\tif ( papi_is_property( $property ) ) {\n\t\t\t\t\t\t/**\n\t\t\t\t\t\t * Modify property.\n\t\t\t\t\t\t *\n\t\t\t\t\t\t * @param  Papi_Core_Property $property\n\t\t\t\t\t\t */\n\t\t\t\t\t\treturn apply_filters( 'papi/get_property', $property );\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * Get root properties.\n\t *\n\t * @return array\n\t */\n\tpublic function get_properties() {\n\t\t$boxes = $this->get_boxes();\n\t\t$list  = [];\n\n\t\tforeach ( $boxes as $box ) {\n\t\t\tforeach ( $box->properties as $property ) {\n\t\t\t\t$list[] = $property;\n\t\t\t}\n\t\t}\n\n\t\treturn $list;\n\t}\n\n\t/**\n\t * Add admin help tabs.\n\t *\n\t * Example:\n\t *   'My custom title' => 'My custom content'\n\t *\n\t * @return array\n\t */\n\tpublic function help() {\n\t\treturn [];\n\t}\n\n\t/**\n\t * Add help sidebar content.\n\t *\n\t * By default we return false to disable\n\t * the sidebar content.\n\t *\n\t * @return bool\n\t */\n\tpublic function help_sidebar() {\n\t\treturn false;\n\t}\n\n\t/**\n\t * Merge boxes with same title.\n\t *\n\t * @param  array $boxes\n\t *\n\t * @return array\n\t */\n\tprotected function merge_boxes( array $boxes ) {\n\t\t$result = [];\n\n\t\tforeach ( $boxes as $box ) {\n\t\t\tif ( ! isset( $result[$box->id] ) ) {\n\t\t\t\t$result[$box->id] = $box;\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tforeach ( $box->properties as $property ) {\n\t\t\t\t$result[$box->id]->properties[] = $property;\n\t\t\t}\n\n\t\t\t$result[$box->id]->properties = array_unique( $result[$box->id]->properties );\n\t\t}\n\n\t\treturn array_values( $result );\n\t}\n\n\t/**\n\t * Add new property to the page using array or rendering property template file.\n\t *\n\t * @param  array|string $file_or_options\n\t * @param  array $values\n\t *\n\t * @return null|Papi_Property\n\t */\n\tprotected function property( $file_or_options = [], $values = [] ) {\n\t\treturn papi_property( $file_or_options, $values );\n\t}\n\n\t/**\n\t * Check if the entry type is a singleton.\n\t *\n\t * @return bool\n\t */\n\tpublic function singleton() {\n\t\treturn false;\n\t}\n\n\t/**\n\t * Setup entry type.\n\t */\n\tpublic function setup() {\n\t\tadd_action( 'in_admin_header', [$this, 'add_help_tabs'] );\n\t\tadd_filter( 'screen_options_show_screen', function () {\n\t\t\treturn $this->show_screen_options;\n\t\t} );\n\n\t\t// @codeCoverageIgnoreStart\n\t\tif ( ! method_exists( $this, 'register' ) ) {\n\t\t\treturn;\n\t\t}\n\t\t// @codeCoverageIgnoreEnd\n\n\t\t$this->register();\n\n\t\tforeach ( $this->get_boxes() as $box ) {\n\t\t\tnew Papi_Admin_Meta_Box( $box );\n\t\t}\n\t}\n\n\t/**\n\t * Add a new tab.\n\t *\n\t * @param  mixed $file_or_options\n\t * @param  array $properties\n\t *\n\t * @return null|Papi_Core_Tab\n\t */\n\tprotected function tab( $file_or_options = [], $properties = [] ) {\n\t\tif ( ! is_string( $file_or_options ) && ! is_array( $file_or_options ) ) {\n\t\t\treturn;\n\t\t}\n\n\t\treturn papi_tab( $file_or_options, $properties );\n\t}\n\n\t/**\n\t * Load template file.\n\t *\n\t * @param  string $file\n\t * @param  array  $values\n\t *\n\t * @return array\n\t */\n\tprotected function template( $file, $values = [] ) {\n\t\treturn papi_template( $file, $values );\n\t}\n}\n"
  },
  {
    "path": "src/types/class-papi-front-page-type.php",
    "content": "<?php\n\nclass Papi_Front_Page_Type extends Papi_Page_Type {\n\n\t/**\n\t * The type name.\n\t *\n\t * @var string\n\t */\n\tpublic $type = 'front-page';\n}\n"
  },
  {
    "path": "src/types/class-papi-module-type.php",
    "content": "<?php\n\n/**\n * Papi type that handle all post types except attachment.\n * All page types should extend this class.\n */\nclass Papi_Module_Type extends Papi_Page_Type {\n\n\t/**\n\t * The post types to register the page type with.\n\t *\n\t * @var array\n\t */\n\tpublic $post_type = ['module'];\n}\n"
  },
  {
    "path": "src/types/class-papi-option-type.php",
    "content": "<?php\n\n/**\n * Papi type that handle option, option data\n * and rendering. All option types should extend this\n * class.\n */\nclass Papi_Option_Type extends Papi_Entry_Type {\n\n\t/**\n\t * Capability.\n\t *\n\t * @var array\n\t */\n\tpublic $capability = 'manage_options';\n\n\t/**\n\t * The description of the option type.\n\t *\n\t * @var string\n\t */\n\tpublic $description = '';\n\n\t/**\n\t * The menu to register the option type on.\n\t *\n\t * @var string\n\t */\n\tpublic $menu = '';\n\n\t/**\n\t * The type name.\n\t *\n\t * @var string\n\t */\n\tpublic $type = 'option';\n\n\t/**\n\t * Render option page type.\n\t */\n\tpublic function render() {\n\t\t?>\n\t\t<div class=\"wrap\">\n\t\t\t<h2><?php echo esc_html( $this->name ); ?></h2>\n\t\t\t<?php echo wpautop( papi_nl2br( $this->description ) ); // wpcs: xss ok ?>\n\t\t\t<form id=\"post\" method=\"post\" name=\"post\">\n\t\t\t\t<div id=\"papi-hidden-editor\" class=\"hide-if-js\">\n\t\t\t\t\t<?php wp_nonce_field( 'papi_save_data', 'papi_meta_nonce' ); ?>\n\t\t\t\t\t<?php wp_editor( '', 'papiHiddenEditor' ); ?>\n\t\t\t\t</div>\n\t\t\t\t<div id=\"poststuff\">\n\t\t\t\t\t<div id=\"post-body\">\n\t\t\t\t\t\t<?php\n\t\t\t\t\t\tforeach ( $this->boxes as $box ) {\n\t\t\t\t\t\t\tdo_meta_boxes( $box->id, 'normal', null );\n\t\t\t\t\t\t}\n\t\t\t\t\t\t?>\n\t\t\t\t\t\t<?php submit_button(); ?>\n\t\t\t\t\t</div>\n\t\t\t\t</div>\n\t\t\t</form>\n\t\t</div>\n\t\t<?php\n\t}\n}\n"
  },
  {
    "path": "src/types/class-papi-page-type.php",
    "content": "<?php\n\n/**\n * Papi type that handle all post types except attachment.\n * All page types should extend this class.\n */\nclass Papi_Page_Type extends Papi_Entry_Type {\n\n\t/**\n\t * Capabilities list.\n\t *\n\t * @var array\n\t */\n\tpublic $capabilities = [];\n\n\t/**\n\t * The page types that lives under this page type.\n\t *\n\t * @var array\n\t */\n\tpublic $child_types = [];\n\n\t/**\n\t * The description of the page type.\n\t *\n\t * @var string\n\t */\n\tpublic $description = '';\n\n\t/**\n\t * Fill labels.\n\t *\n\t * @var bool\n\t */\n\tpublic $fill_labels = false;\n\n\t/**\n\t * Labels, the same labels that post type object uses.\n\t *\n\t * Plus some special:\n\t * - `title_placeholder` that can change main title input placeholder.\n\t *\n\t * @var array\n\t */\n\tpublic $labels = [];\n\n\t/**\n\t * The post types to register the page type with.\n\t *\n\t * @var array\n\t */\n\tpublic $post_type = ['page'];\n\n\t/**\n\t * Remove meta boxes.\n\t *\n\t * @var array\n\t */\n\tprotected $remove_meta_boxes = [];\n\n\t/**\n\t * Show standard page type or not.\n\t *\n\t * @var bool\n\t */\n\tpublic $standard_type = false;\n\n\t/**\n\t * Show permalink edit box.\n\t *\n\t * @var bool\n\t */\n\tpublic $show_permalink = true;\n\n\t/**\n\t * Show page attributes box.\n\t *\n\t * @var bool\n\t */\n\tpublic $show_page_attributes = true;\n\n\t/**\n\t * Show page template dropdown.\n\t *\n\t * @var bool\n\t */\n\tpublic $show_page_template = false;\n\n\t/**\n\t * Show page type switcher.\n\t *\n\t * @var boolean\n\t */\n\tpublic $switcher = true;\n\n\t/**\n\t * The template of the page type.\n\t *\n\t * @var string\n\t */\n\tpublic $template = '';\n\n\t/**\n\t * The page type thumbnail.\n\t *\n\t * @var string\n\t */\n\tpublic $thumbnail = '';\n\n\t/**\n\t * The type name.\n\t *\n\t * @var string\n\t */\n\tpublic $type = 'page';\n\n\t/**\n\t * The constructor.\n\t *\n\t * Load a page type by the file.\n\t *\n\t * @param string $file_path\n\t */\n\tpublic function __construct( $file_path = '' ) {\n\t\tparent::__construct( $file_path );\n\t\t$this->setup_post_types();\n\t\t$this->setup_page_templates();\n\t}\n\n\t/**\n\t * Determine if the page type is allowed by capabilities and post type.\n\t *\n\t * @return bool\n\t */\n\tpublic function allowed() {\n\t\t$args = func_get_args();\n\n\t\tif ( empty( $args ) ) {\n\t\t\treturn parent::allowed();\n\t\t}\n\n\t\treturn papi_current_user_is_allowed( $this->capabilities ) && isset( $args[0] ) && in_array( $args[0], $this->post_type, true );\n\t}\n\n\t/**\n\t * Should the Page Type be displayed in WordPress admin or not?\n\t *\n\t * @param  string $post_type\n\t *\n\t * @return bool\n\t */\n\tpublic function display( $post_type ) {\n\t\treturn true;\n\t}\n\n\t/**\n\t * Get body css classes.\n\t *\n\t * @return array\n\t */\n\tpublic function get_body_classes() {\n\t\t$classes = parent::get_body_classes();\n\n\t\tif ( ! $this->show_permalink ) {\n\t\t\t$classes[] = 'papi-hide-edit-slug-box';\n\t\t}\n\n\t\tif ( ! $this->show_page_attributes ) {\n\t\t\t$classes[] = 'papi-hide-pageparentdiv';\n\t\t}\n\n\t\treturn $classes;\n\t}\n\n\t/**\n\t * Get child page types that lives under the current page type.\n\t *\n\t * @return array\n\t */\n\tpublic function get_child_types() {\n\t\t$child_types = [];\n\n\t\tforeach ( papi_to_array( $this->child_types ) as $id ) {\n\t\t\t$child_type = papi_get_entry_type_by_id( $id );\n\n\t\t\tif ( $child_type instanceof Papi_Page_Type ) {\n\t\t\t\t$child_types[] = $child_type;\n\t\t\t}\n\t\t}\n\n\t\treturn $child_types;\n\t}\n\n\t/**\n\t * Get labels that should be changed\n\t * when using `fill_labels` option.\n\t *\n\t * @return array\n\t */\n\tpublic function get_labels() {\n\t\tif ( ! $this->fill_labels ) {\n\t\t\treturn $this->labels;\n\t\t}\n\n\t\treturn array_merge( $this->labels, [\n\t\t\t'add_new_item' => sprintf( '%s %s', __( 'Add New', 'papi' ), $this->name ),\n\t\t\t'edit_item'    => sprintf( '%s %s', __( 'Edit', 'papi' ), $this->name ),\n\t\t\t'view_item'    => sprintf( '%s %s', __( 'View', 'papi' ), $this->name )\n\t\t] );\n\t}\n\n\t/**\n\t * Get post type.\n\t *\n\t * @return string\n\t */\n\tpublic function get_post_type() {\n\t\treturn papi_get_post_type();\n\t}\n\n\t/**\n\t * Get post type supports that will be removed.\n\t *\n\t * @return array\n\t */\n\tprotected function get_post_type_supports() {\n\t\t$supports = ['custom-fields'];\n\n\t\tif ( method_exists( $this, 'remove' ) ) {\n\t\t\t$output   = $this->remove();\n\t\t\t$output   = is_string( $output ) ? [$output] : $output;\n\t\t\t$output   = is_array( $output ) ? $output : [];\n\t\t\t$output   = array_filter( $output, 'is_string' );\n\t\t\t$supports = array_merge( $supports, $output );\n\t\t}\n\n\t\t$parent_class  = get_parent_class( $this );\n\t\t$parent_remove = method_exists( $parent_class, 'remove' );\n\n\t\twhile ( $parent_remove ) {\n\t\t\t$parent        = new $parent_class();\n\t\t\t$output        = $parent->remove();\n\t\t\t$output        = is_string( $output ) ? [$output] : $output;\n\t\t\t$output        = is_array( $output ) ? $output : [];\n\t\t\t$output        = array_filter( $output, 'is_string' );\n\t\t\t$supports      = array_merge( $supports, $output );\n\t\t\t$parent_class  = get_parent_class( $parent_class );\n\t\t\t$parent_remove = method_exists( $parent_class, 'remove' );\n\t\t}\n\n\t\treturn $supports;\n\t}\n\n\t/**\n\t * Get page type image thumbnail.\n\t *\n\t * @return string\n\t */\n\tpublic function get_thumbnail() {\n\t\tif ( empty( $this->thumbnail ) ) {\n\t\t\treturn '';\n\t\t}\n\n\t\treturn $this->thumbnail;\n\t}\n\n\t/**\n\t * Check if the given post is allowed to use the page type.\n\t *\n\t * @param string $post_type\n\t *\n\t * @return bool\n\t */\n\tpublic function has_post_type( $post_type ) {\n\t\treturn in_array( $post_type, $this->post_type, true );\n\t}\n\n\t/**\n\t * Remove post type support action.\n\t */\n\tpublic function remove_post_type_support() {\n\t\tglobal $_wp_post_type_features;\n\n\t\t$post_type = $this->get_post_type();\n\n\t\tif ( empty( $post_type ) ) {\n\t\t\treturn;\n\t\t}\n\n\t\t$post_type_supports = $this->get_post_type_supports();\n\n\t\tforeach ( $post_type_supports as $key => $value ) {\n\t\t\tif ( is_numeric( $key ) ) {\n\t\t\t\t$key = $value;\n\t\t\t\t$value = '';\n\t\t\t}\n\n\t\t\tif ( isset( $_wp_post_type_features[$post_type], $_wp_post_type_features[$post_type][$key] ) ) {\n\t\t\t\tunset( $_wp_post_type_features[$post_type][$key] );\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tif ( in_array( strtolower( $key ), ['all', 'normal', 'side', 'advanced'], true ) ) {\n\t\t\t\t$value = strtolower( $key );\n\t\t\t} else if ( empty( $value ) ) {\n\t\t\t\t$value = 'normal';\n\t\t\t}\n\n\t\t\t$this->remove_meta_boxes[] = [$key, $value];\n\t\t}\n\n\t\tadd_action( 'add_meta_boxes', [$this, 'remove_meta_boxes'], 999 );\n\t}\n\n\t/**\n\t * Remove meta boxes.\n\t */\n\tpublic function remove_meta_boxes() {\n\t\tglobal $wp_meta_boxes;\n\n\t\t$post_type = $this->get_post_type();\n\t\t$context = [];\n\n\t\tforeach ( $this->remove_meta_boxes as $item ) {\n\t\t\tif ( $item[0] !== $item[1] ) {\n\t\t\t\tremove_meta_box( $item[0], $post_type, $item[1] );\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\t$context = $item[0];\n\n\t\t\t// Our special context.\n\t\t\tif ( $context === 'all' ) {\n\t\t\t\t$context = ['normal', 'side', 'advanced'];\n\t\t\t} else {\n\t\t\t\t$context = [strtolower( $context )];\n\t\t\t}\n\n\t\t\tforeach ( $context as $ctx ) {\n\t\t\t\tif ( ! isset( $wp_meta_boxes[$post_type], $wp_meta_boxes[$post_type][$ctx] ) ) {\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\n\t\t\t\t$meta_boxes = $wp_meta_boxes[$post_type][$ctx];\n\n\t\t\t\tif ( ! is_array( $meta_boxes ) ) {\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\n\t\t\t\tforeach ( $meta_boxes as $level ) {\n\t\t\t\t\tforeach ( $level as $id => $box ) {\n\t\t\t\t\t\t// Don't allow removing of papi boxes.\n\t\t\t\t\t\tif ( strpos( $id, '_papi' ) !== false ) {\n\t\t\t\t\t\t\tcontinue;\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\t// Don't allow removing of submitdiv.\n\t\t\t\t\t\tif ( $id === 'submitdiv' ) {\n\t\t\t\t\t\t\tcontinue;\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tremove_meta_box( $id, $post_type, $ctx );\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t}\n\t\t}\n\n\t\t// Special for editor in normal context.\n\t\tif ( in_array( 'normal', $context, true ) ) {\n\t\t\tremove_post_type_support( 'page', 'editor' );\n\t\t}\n\t}\n\n\t/**\n\t * Setup page type.\n\t */\n\tpublic function setup() {\n\t\tparent::setup();\n\n\t\t// Remove post type support and meta boxes.\n\t\t$this->remove_post_type_support();\n\n\t\t// Add support for displaying information in publish box from a page type.\n\t\tif ( method_exists( $this, 'publish_box' ) ) {\n\t\t\tadd_action( 'post_submitbox_misc_actions', [$this, 'publish_box'] );\n\t\t}\n\n\t\t// Hide page template dropdown if it shouldn't be showed.\n\t\tif ( ! $this->show_page_template ) {\n\t\t\tadd_filter( 'theme_page_templates', '__return_empty_array' );\n\t\t}\n\n\t\t// Main title input placeholder.\n\t\tif ( ! empty( $this->labels['title_placeholder'] ) ) {\n\t\t\tadd_filter( 'enter_title_here', function () {\n\t\t\t\treturn $this->labels['title_placeholder'];\n\t\t\t} );\n\t\t}\n\t}\n\n\t/**\n\t * Setup post types array.\n\t */\n\tprotected function setup_post_types() {\n\t\t$this->post_type = papi_to_array( $this->post_type );\n\n\t\t// Set a default value to post types array\n\t\t// if we don't have a array or a empty array.\n\t\tif ( empty( $this->post_type ) ) {\n\t\t\t$this->post_type = ['page'];\n\t\t}\n\n\t\tif ( count( $this->post_type ) === 1 && $this->post_type[0] === 'any' ) {\n\t\t\t$this->post_type = get_post_types( '', 'names' );\n\t\t\t$this->post_type = array_values( $this->post_type );\n\t\t}\n\t}\n\n\t/**\n\t * Setup page templates if templates is a array.\n\t */\n\tprotected function setup_page_templates() {\n\t\tif ( ! is_array( $this->template ) || ! $this->has_post_type( papi_get_post_type() ) ) {\n\t\t\treturn;\n\t\t}\n\n\t\t$this->show_page_template = true;\n\t\tforeach ( $this->post_type as $post_type ) {\n\t\t\t// Can't use `theme_templates` filter since it's added in 4.9.6.\n\t\t\tadd_filter( \"theme_{$post_type}_templates\", function( $templates ) {\n\t\t\t\tforeach ( $this->template as $template ) {\n\t\t\t\t\tif ( ! is_array( $template ) || ! isset( $template['template'], $template['label'] ) ) {\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\n\t\t\t\t\t$templates[$template['template']] = $template['label'];\n\t\t\t\t}\n\n\t\t\t\treturn $templates;\n\t\t\t} );\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "src/types/class-papi-taxonomy-type.php",
    "content": "<?php\n\n/**\n * Papi type that handle taxonomies.\n */\nclass Papi_Taxonomy_Type extends Papi_Entry_Type {\n\n\t/**\n\t * Fill labels.\n\t *\n\t * @var bool\n\t */\n\tpublic $fill_labels = false;\n\n\t/**\n\t * Labels, the same labels that taxonomy type object uses.\n\t *\n\t * @var array\n\t */\n\tpublic $labels = [];\n\n\t/**\n\t * Redirect after create.\n\t *\n\t * @var bool\n\t */\n\tpublic $redirect_after_create = false;\n\n\t/**\n\t * The taxonomy.\n\t *\n\t * @var array\n\t */\n\tpublic $taxonomy = [];\n\n\t/**\n\t * The type name.\n\t *\n\t * @var string\n\t */\n\tpublic $type = 'taxonomy';\n\n\t/**\n\t * Determine if the taxonomy type is allowed by taxonomy type.\n\t *\n\t * @return bool\n\t */\n\tpublic function allowed() {\n\t\t$args = func_get_args();\n\n\t\tif ( empty( $args ) ) {\n\t\t\treturn parent::allowed();\n\t\t}\n\n\t\treturn isset( $args[0] ) && in_array( $args[0], $this->taxonomy, true );\n\t}\n\n\t/**\n\t * Should the Taxonomy Type be displayed in WordPress admin or not?\n\t *\n\t * @param  string $taxonomy\n\t *\n\t * @return bool\n\t */\n\tpublic function display( $taxonomy ) {\n\t\treturn true;\n\t}\n\n\t/**\n\t * Get labels that should be changed\n\t * when using `fill_labels` option.\n\t *\n\t * @return array\n\t */\n\tpublic function get_labels() {\n\t\tif ( ! $this->fill_labels ) {\n\t\t\treturn $this->labels;\n\t\t}\n\n\t\treturn array_merge( $this->labels, [\n\t\t\t'add_new_item' => sprintf( '%s %s', __( 'Add New', 'papi' ), $this->name ),\n\t\t\t'edit_item'    => sprintf( '%s %s', __( 'Edit', 'papi' ), $this->name ),\n\t\t\t'view_item'    => sprintf( '%s %s', __( 'View', 'papi' ), $this->name )\n\t\t] );\n\t}\n\n\t/**\n\t * Setup actions.\n\t */\n\tprotected function setup_actions() {\n\t\tforeach ( papi_to_array( $this->taxonomy ) as $taxonomy ) {\n\t\t\tif ( is_string( $taxonomy ) && taxonomy_exists( $taxonomy ) ) {\n\t\t\t\tadd_action( $taxonomy . '_edit_form', [$this, 'edit_form'] );\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * Render term edit form.\n\t */\n\tpublic function edit_form() {\n\t\t?>\n\t\t<div id=\"papi-hidden-editor\" class=\"hide-if-js\">\n\t\t\t<?php wp_editor( '', 'papiHiddenEditor' ); ?>\n\t\t</div>\n\t\t<div id=\"poststuff\">\n\t\t\t<div id=\"post-body\">\n\t\t\t\t<?php\n\t\t\t\tforeach ( $this->boxes as $box ) {\n\t\t\t\t\tdo_meta_boxes(\n\t\t\t\t\t\t$box->id,\n\t\t\t\t\t\t'normal',\n\t\t\t\t\t\tnull\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t\t?>\n\t\t\t</div>\n\t\t</div>\n\t\t<?php\n\t}\n\n\t/**\n\t * Setup meta data.\n\t */\n\tprotected function setup_meta_data() {\n\t\tparent::setup_meta_data();\n\t\t$this->taxonomy = papi_to_array( $this->taxonomy );\n\t}\n}\n"
  },
  {
    "path": "tests/Dockerfile",
    "content": "FROM jitesoft/composer:7.4\n\nRUN apk --no-cache add bash mysql-client zlib-dev libjpeg-turbo-dev libpng-dev freetype-dev subversion\n\nRUN docker-php-ext-install gd\n"
  },
  {
    "path": "tests/README.md",
    "content": "# Papi Tests\n\n## Run tests with VVV\n\nThis guide will describe how to use [VVV](https://github.com/varying-vagrant-vagrants/vvv/) to run Papi tests.\n\n1. Download [VVV](https://github.com/varying-vagrant-vagrants/vvv/) which is an evolving Vagrant configuration focused on WordPress development.\n\n2. Clone Papi to `path/to/vvv/www/wordpress-default/wp-content/plugins/`.\n\n2. Go to Run  `path/to/vvv/www/wordpress-default/wp-content/plugins/papi/` and run `composer install` on your host machine. If you have configured Composer right then you can run this in your VVV machine.\n\n3. Log in to your vagrant machine and go to `/srv/www/wordpress-default/wp-content/plugins/papi/`, `/srv/` is equal to your VVV directory.\n\n4. Run `vendor/bin/phpunit` (PHPUnit is installed with Composer) and PHPUnit will start to test Papi.\n\n## VVV tips\n\nIf using VVV you can run `xdebug_on` to turn Xdebug on so you can generate code coverage. You can turn it off by running `xdebug_off`\n\n## PHPUnit tips\n\nYou can run specific tests by providing the path and filename to the test class:\n\n```\n$ vendor/bin/phpunit tests/cases/admin\n```\n\nYou can run specific test method by using `--filter`:\n\n```\n$ vendor/bin/phpunit --filter test_save_meta_boxes\n```\n\nA text code coverage summary can be displayed using the `--coverage-text` option:\n\n```\n$ vendor/bin/phpunit --coverage-text\n```\n\n## Writing Tests\n\n* Each test file should roughly correspond to an associated source file, e.g `src/admin/class-papi-admin-ajax.php` should have a test file named `tests/cases/admin/class-papi-admin-ajax-test.php`\n* Each test method should cover a single method or function with one or more assertions\n* A single method or function can have multiple associated test methods if it's a large or complex method\n* For code that cannot be tested or should not be tested use `// @codeCoverageIgnoreStart` and `// @codeCoverageIgnoreEnd` before and after the code.\n* In addition to covering each line of a method/function, make sure to test common input and edge cases. When resolving a issue you should create a test for it.\n* Prefer `assertsSame()` where possible as it tests equality. When testing objects you should use `assertEquals()` since object's can be different but with same data.\n* Remember that only methods prefixed with `test` will be run.\n* Remember that files that test code should end with `-test.php` prefix.\n* Filters persist between test cases so be sure to remove them in your test method or in the `tearDown()` method.\n\n## Automated Tests\n\nTests are automatically run with [Travis-CI](https://travis-ci.org) for each commit and pull request. You can check the current test status [here](https://travis-ci.org/wp-papi/papi).\n\nThis is the current build status for `master` branch:\n\n[![Build Status](https://travis-ci.org/wp-papi/papi.svg?branch=master)](https://travis-ci.org/wp-papi/papi)\n\n## Code Coverage\n\nCode coverage is available on [Codecov](https://codecov.io/) which receives updated data after each Travis build. You can check the current code coverage [here](https://codecov.io/github/wp-papi/papi/).\n\nThis is the current code coverage for `master` branch:\n\n[![Coverage Status](https://img.shields.io/codecov/c/github/wp-papi/papi.svg?style=flat)](https://codecov.io/github/wp-papi/papi)\n"
  },
  {
    "path": "tests/bin/install-cp-tests.sh",
    "content": "#!/usr/bin/env bash\n\nif [ $# -lt 3 ]; then\n\techo \"usage: $0 <db-name> <db-user> <db-pass> [db-host] [wp-version] [skip-database-creation]\"\n\texit 1\nfi\n\nDB_NAME=$1\nDB_USER=$2\nDB_PASS=$3\nDB_HOST=${4-localhost}\nWP_VERSION=${5-master}\nSKIP_DB_CREATE=${6-false}\n\nTMPDIR=${TMPDIR-/tmp}\n\n# need wp directories to support our test suite.\nWP_TESTS_DIR=${WP_TESTS_DIR-/tmp/wordpress-tests-lib}\nWP_CORE_DIR=${WP_CORE_DIR-/tmp/wordpress/}\n\ndownload() {\n    if [ `which curl` ]; then\n        curl -L -s \"$1\" > \"$2\";\n    elif [ `which wget` ]; then\n        wget -nv -O \"$2\" \"$1\"\n    fi\n}\nset -ex\n\ninstall_wp() {\n  echo \"download and install ClassicPress ${WP_VERSION}\"\n  if [ ! -d $WP_CORE_DIR  ]\n  then\n    mkdir -p $WP_CORE_DIR\n    download https://github.com/ClassicPress/ClassicPress-release/archive/${WP_VERSION}.tar.gz $TMPDIR/cp.tar.gz\n    tar --strip-components=1 -zxmf $TMPDIR/cp.tar.gz -C $WP_CORE_DIR\n  fi\n}\n\ninstall_test_suite() {\n  echo \"download test-suite\"\n\t# portable in-place argument for both GNU sed and Mac OSX sed\n\tif [[ $(uname -s) == 'Darwin' ]]; then\n\t\tlocal ioption='-i .bak'\n\telse\n\t\tlocal ioption='-i'\n\tfi\n\n\t# set up testing suite if it doesn't yet exist\n\tif [ ! -d $WP_TESTS_DIR ]; then\n\t\t# set up testing suite\n\t\tmkdir -p $WP_TESTS_DIR\n    TMP_STR=$$;\n    git clone --depth 1 --branch $WP_VERSION https://github.com/ClassicPress/ClassicPress.git /tmp/cp-core-${TMP_STR}\n    mv -v /tmp/cp-core-${TMP_STR}/tests/phpunit/includes $WP_TESTS_DIR/includes\n    mv -v /tmp/cp-core-${TMP_STR}/tests/phpunit/data $WP_TESTS_DIR/data\n\tfi\n\n\tif [ ! -f wp-tests-config.php ]; then\n\t\tdownload https://raw.githubusercontent.com/ClassicPress/ClassicPress/${WP_VERSION}/wp-tests-config-sample.php \"$WP_TESTS_DIR\"/wp-tests-config.php\n\t\t# remove all forward slashes in the end\n\t\tWP_CORE_DIR=$(echo $WP_CORE_DIR | sed \"s:/\\+$::\")\n\t\tsed $ioption \"s:dirname( __FILE__ ) . '/src/':'$WP_CORE_DIR/':\" \"$WP_TESTS_DIR\"/wp-tests-config.php\n\t\tsed $ioption \"s/youremptytestdbnamehere/$DB_NAME/\" \"$WP_TESTS_DIR\"/wp-tests-config.php\n\t\tsed $ioption \"s/yourusernamehere/$DB_USER/\" \"$WP_TESTS_DIR\"/wp-tests-config.php\n\t\tsed $ioption \"s/yourpasswordhere/$DB_PASS/\" \"$WP_TESTS_DIR\"/wp-tests-config.php\n\t\tsed $ioption \"s|localhost|${DB_HOST}|\" \"$WP_TESTS_DIR\"/wp-tests-config.php\n\tfi\n}\n\ninstall_db() {\n\tif [ ${SKIP_DB_CREATE} = \"true\" ]; then\n\t\treturn 0\n\tfi\n  echo \"initializing DB\"\n\n\t# parse DB_HOST for port or socket references\n\tlocal PARTS=(${DB_HOST//\\:/ })\n\tlocal DB_HOSTNAME=${PARTS[0]};\n\tlocal DB_SOCK_OR_PORT=${PARTS[1]};\n\tlocal EXTRA=\"\"\n\n\tif ! [ -z $DB_HOSTNAME ] ; then\n\t\tif [ $(echo $DB_SOCK_OR_PORT | grep -e '^[0-9]\\{1,\\}$') ]; then\n\t\t\tEXTRA=\" --host=$DB_HOSTNAME --port=$DB_SOCK_OR_PORT --protocol=tcp\"\n\t\telif ! [ -z $DB_SOCK_OR_PORT ] ; then\n\t\t\tEXTRA=\" --socket=$DB_SOCK_OR_PORT\"\n\t\telif ! [ -z $DB_HOSTNAME ] ; then\n\t\t\tEXTRA=\" --host=$DB_HOSTNAME --protocol=tcp\"\n\t\tfi\n\tfi\n\n\t# create database\n\tmysqladmin create $DB_NAME --user=\"$DB_USER\" --password=\"$DB_PASS\"$EXTRA\n}\n\ninstall_wp\ninstall_test_suite\ninstall_db\n"
  },
  {
    "path": "tests/bootstrap.php",
    "content": "<?php\n\n// Load Composer autoload.\nrequire __DIR__ . '/../vendor/autoload.php';\n\n// Define fixtures directory constant\ndefine( 'PAPI_FIXTURE_DIR', __DIR__ . '/data' );\n\n// Load Papi loader file as plugin.\nWP_Test_Suite::load_plugins( __DIR__ . '/../papi-loader.php' );\n\n// Load our helpers file.\nWP_Test_Suite::load_files( [\n\t__DIR__ . '/framework/helpers.php',\n\t__DIR__\t. '/framework/class-papi-property-test-case.php'\n] );\n\n// Run the WordPress test suite.\nWP_Test_Suite::run( function () {\n\trequire_once __DIR__ . '/framework/functions.php';\n} );\n"
  },
  {
    "path": "tests/cases/admin/class-papi-admin-ajax-test.php",
    "content": "<?php\n\n/**\n * @group ajax\n */\nclass Papi_Admin_Ajax_Test extends WP_UnitTestCase {\n\n\tpublic function setUp() {\n\t\tparent::setUp();\n\t\t$_GET  = [];\n\t\t$_POST = [];\n\t\t$this->ajax = new Papi_Admin_Ajax();\n\t\tadd_filter( 'wp_die_ajax_handler', [$this, 'get_wp_die_handler'], 1, 1 );\n\t}\n\n\tpublic function tearDown() {\n\t\tparent::tearDown();\n\t\tunset( $_GET, $_POST, $this->ajax );\n\t\tremove_filter( 'wp_die_ajax_handler', [$this, 'get_wp_die_handler'], 1, 1 );\n\t}\n\n\tpublic function wp_die_handler( $message ) {\n\t}\n\n\tpublic function test_actions() {\n\t\t$this->assertSame( 10, has_action( 'init', [$this->ajax, 'add_endpoint'] ) );\n\t\t$this->assertSame( 10, has_action( 'parse_request', [$this->ajax, 'handle_papi_ajax'] ) );\n\t\t$this->assertSame( 10, has_action( 'admin_enqueue_scripts', [$this->ajax, 'ajax_url'] ) );\n\t\t$this->assertSame( 10, has_action( 'papi/ajax/get_property', [$this->ajax, 'get_property'] ) );\n\t\t$this->assertSame( 10, has_action( 'papi/ajax/get_properties', [$this->ajax, 'get_properties'] ) );\n\t}\n\n\tpublic function test_endpoint() {\n\t\t$ajax = new Papi_Admin_Ajax();\n\t\t$ajax->add_endpoint();\n\n\t\tglobal $wp_rewrite;\n\t\t$this->assertTrue( ! isset( $wp_rewrite->extra_rules_top['papi-ajax/([^/]*)/?'] ) );\n\n\t\tadd_filter( 'pre_option_permalink_structure', function() {\n\t\t\treturn 'not empty';\n\t\t} );\n\n\t\t$ajax = new Papi_Admin_Ajax();\n\t\t$ajax->add_endpoint();\n\n\t\t$this->assertNotNull( $wp_rewrite->extra_rules_top['papi-ajax/([^/]*)/?'] );\n\t\t$this->assertSame( 'index.php?action=$matches[1]', $wp_rewrite->extra_rules_top['papi-ajax/([^/]*)/?'] );\n\t}\n\n\tpublic function test_handle_papi_ajax_doing_ajax() {\n\t\t$this->assertNull( $this->ajax->handle_papi_ajax() );\n\t}\n\n\tpublic function test_handle_papi_ajax_action() {\n\t\t$_GET = [\n\t\t\t'action' => 'get_property',\n\t\t\t'slug'   => 'hello',\n\t\t\t'type'   => 'string'\n\t\t];\n\n\t\t$user_id = $this->factory->user->create( [ 'role' => 'administrator' ] );\n\t\twp_set_current_user( $user_id );\n\n\t\t$this->ajax->handle_papi_ajax();\n\t\twp_set_current_user( 0 );\n\n\t\t$this->expectOutputRegex( '/.*\\S.*/' );\n\t\t$this->expectOutputRegex( '/papi\\_hello/' );\n\t}\n\n\tpublic function test_handle_papi_ajax() {\n\t\t$this->assertNull( $this->ajax->handle_papi_ajax() );\n\t\t$this->expectOutputRegex( '//' );\n\t}\n\n\tpublic function test_get_entry_type_fail() {\n\t\tif ( ! defined( 'DOING_AJAX' ) ) {\n\t\t\tdefine( 'DOING_AJAX', true );\n\t\t}\n\n\t\t$_GET = [\n\t\t\t'entry_type' => 'properties-page-type'\n\t\t];\n\n\t\tdo_action( 'papi/ajax/get_entry_type' );\n\n\t\t$this->expectOutputRegex( '/\\{\\\"error\\\"\\:\\\"No entry type found\\\"\\}/' );\n\t}\n\n\tpublic function test_get_entry_type_success() {\n\t\tif ( ! defined( 'DOING_AJAX' ) ) {\n\t\t\tdefine( 'DOING_AJAX', true );\n\t\t}\n\n\t\t$_GET = [\n\t\t\t'entry_type' => 'properties-page-type'\n\t\t];\n\n\t\tadd_filter( 'papi/settings/directories', function () {\n\t\t\treturn PAPI_FIXTURE_DIR . '/page-types';\n\t\t} );\n\n\t\tdo_action( 'papi/ajax/get_entry_type' );\n\n\t\t$this->expectOutputRegex( '/properties\\-page/' );\n\t}\n\n\tpublic function test_get_posts() {\n\t\tif ( ! defined( 'DOING_AJAX' ) ) {\n\t\t\tdefine( 'DOING_AJAX', true );\n\t\t}\n\n\t\t$post_id = $this->factory->post->create();\n\n\t\tdo_action( 'papi/ajax/get_posts' );\n\n\t\t$this->expectOutputRegex( '/.*\\S.*/' );\n\t\t$this->expectOutputRegex( sprintf( '/\\\"ID\\\"\\:%d\\,/', $post_id ) );\n\t}\n\n\tpublic function test_get_posts_fields() {\n\t\tif ( ! defined( 'DOING_AJAX' ) ) {\n\t\t\tdefine( 'DOING_AJAX', true );\n\t\t}\n\n\t\t$post_id = $this->factory->post->create();\n\n\t\t$_GET = [\n\t\t\t'fields' => ['ID']\n\t\t];\n\n\t\tdo_action( 'papi/ajax/get_posts' );\n\n\t\t$this->expectOutputRegex( '/.*\\S.*/' );\n\t\t$this->expectOutputRegex( sprintf( '/\\{\\\"ID\\\"\\:%d\\}/', $post_id ) );\n\t}\n\n\tpublic function test_get_posts_fake_post_type() {\n\t\tif ( ! defined( 'DOING_AJAX' ) ) {\n\t\t\tdefine( 'DOING_AJAX', true );\n\t\t}\n\n\t\t$_GET = [\n\t\t\t'query' => [\n\t\t\t\t'post_type' => 'get_posts'\n\t\t\t]\n\t\t];\n\n\t\tdo_action( 'papi/ajax/get_posts' );\n\n\t\t$this->expectOutputRegex( '/.*\\S.*/' );\n\t\t$this->expectOutputRegex( '/\\[\\]/' );\n\t}\n\n\tpublic function test_get_property() {\n\t\tif ( ! defined( 'DOING_AJAX' ) ) {\n\t\t\tdefine( 'DOING_AJAX', true );\n\t\t}\n\n\t\t$_GET = [\n\t\t\t'slug' => 'hello',\n\t\t\t'type' => 'string'\n\t\t];\n\n\t\tdo_action( 'papi/ajax/get_property' );\n\n\t\t$this->expectOutputRegex( '/.*\\S.*/' );\n\t\t$this->expectOutputRegex( '/papi\\_hello/' );\n\t}\n\n\tpublic function test_get_property_fail() {\n\t\tif ( ! defined( 'DOING_AJAX' ) ) {\n\t\t\tdefine( 'DOING_AJAX', true );\n\t\t}\n\n\t\t$_GET = [\n\t\t\t'slug' => 'hello',\n\t\t\t'type' => 'fake'\n\t\t];\n\n\t\tdo_action( 'papi/ajax/get_property' );\n\n\t\t$this->expectOutputRegex( '/.*\\S.*/' );\n\t\t$this->expectOutputRegex( '/\\{\\\"error\\\"\\:\\\"No property found\\\"\\}/' );\n\t}\n\n\tpublic function test_get_properties() {\n\t\tif ( ! defined( 'DOING_AJAX' ) ) {\n\t\t\tdefine( 'DOING_AJAX', true );\n\t\t}\n\n\t\t$property = papi_get_property_type( [\n\t\t\t'slug' => 'name',\n\t\t\t'type' => 'string'\n\t\t] );\n\n\t\t$_POST = [\n\t\t\t'properties' => json_encode( [\n\t\t\t\t$property->get_options(),\n\t\t\t\t[\n\t\t\t\t\t'type'  => 'string',\n\t\t\t\t\t'title'\t=> 'nyckel'\n\t\t\t\t]\n\t\t\t] )\n\t\t];\n\n\t\tdo_action( 'papi/ajax/get_properties' );\n\n\t\t$this->expectOutputRegex( '/.*\\S.*/' );\n\t\t$this->expectOutputRegex( '/papi\\_name/' );\n\t\t$this->expectOutputRegex( '/papi\\_nyckel/' );\n\t}\n\n\tpublic function test_get_properties_fail() {\n\t\tif ( ! defined( 'DOING_AJAX' ) ) {\n\t\t\tdefine( 'DOING_AJAX', true );\n\t\t}\n\n\t\t$_POST = [\n\t\t\t'properties' => json_encode( [\n\t\t\t\t[\n\t\t\t\t\t'type'  => 'fake',\n\t\t\t\t\t'title'\t=> 'nyckel'\n\t\t\t\t]\n\t\t\t] )\n\t\t];\n\n\t\tdo_action( 'papi/ajax/get_properties' );\n\n\t\t$this->expectOutputRegex( '/.*\\S.*/' );\n\t\t$this->expectOutputRegex( '/\\{\\\"error\\\"\\:\\\"No properties found\\\"\\}/' );\n\t}\n\n\tpublic function test_get_properties_fail_2() {\n\t\tif ( ! defined( 'DOING_AJAX' ) ) {\n\t\t\tdefine( 'DOING_AJAX', true );\n\t\t}\n\n\t\t$_POST = [];\n\n\t\tdo_action( 'papi/ajax/get_properties' );\n\n\t\t$this->expectOutputRegex( '/.*\\S.*/' );\n\t\t$this->expectOutputRegex( '/\\{\\\"error\\\"\\:\\\"No properties found\\\"\\}/' );\n\t}\n\n\tpublic function test_get_properties_fail_3() {\n\t\tif ( ! defined( 'DOING_AJAX' ) ) {\n\t\t\tdefine( 'DOING_AJAX', true );\n\t\t}\n\n\t\t$_POST = [\n\t\t\t'properties' => json_encode( [] )\n\t\t];\n\n\t\tdo_action( 'papi/ajax/get_properties' );\n\n\t\t$this->expectOutputRegex( '/.*\\S.*/' );\n\t\t$this->expectOutputRegex( '/\\{\\\"error\\\"\\:\\\"No properties found\\\"\\}/' );\n\t}\n\n\tpublic function test_get_rules_result_success() {\n\t\t$_GET = [\n\t\t\t'page_type' => 'rule-page-type',\n\t\t\t'post'      => $this->factory->post->create()\n\t\t];\n\t\t$_POST = [\n\t\t\t'data' => json_encode( [\n\t\t\t\t'rules' => [\n\t\t\t\t\t[\n\t\t\t\t\t\t'operator' => '=',\n\t\t\t\t\t\t'slug'     => 'rules1',\n\t\t\t\t\t\t'source'   => '',\n\t\t\t\t\t\t'value'\t   => 123\n\t\t\t\t\t\t]\n\t\t\t\t\t],\n\t\t\t\t'slug'  => 'rules_1'\n\t\t\t] )\n\t\t];\n\n\t\tadd_filter( 'papi/settings/directories', function () {\n\t\t\treturn [1,  PAPI_FIXTURE_DIR . '/page-types'];\n\t\t} );\n\n\t\tdo_action( 'papi/ajax/get_rules_result' );\n\n\t\t$this->expectOutputRegex( '/.*\\S.*/' );\n\t\t$this->expectOutputRegex( '/\\{\\\"render\\\"\\:false\\}/' );\n\t}\n\n\tpublic function test_get_rules_result_success_2() {\n\t\t$_GET = [\n\t\t\t'page_type' => 'rule-page-type',\n\t\t\t'post'      => $this->factory->post->create()\n\t\t];\n\t\t$_POST = [\n\t\t\t'data' => json_encode( [\n\t\t\t\t'rules' => [\n\t\t\t\t\t[\n\t\t\t\t\t\t'operator' => 'NOT EXISTS',\n\t\t\t\t\t\t'slug'     => 'rules2',\n\t\t\t\t\t\t'source'   => ''\n\t\t\t\t\t]\n\t\t\t\t],\n\t\t\t\t'slug'  => 'rules_2'\n\t\t\t] )\n\t\t];\n\n\t\tadd_filter( 'papi/settings/directories', function () {\n\t\t\treturn [1,  PAPI_FIXTURE_DIR . '/page-types'];\n\t\t} );\n\n\t\tdo_action( 'papi/ajax/get_rules_result' );\n\n\t\t$this->expectOutputRegex( '/.*\\S.*/' );\n\t\t$this->expectOutputRegex( '/\\{\\\"render\\\"\\:true\\}/' );\n\t}\n\n\tpublic function test_get_rules_result_success_3() {\n\t\t$_GET = [\n\t\t\t'page_type' => 'rule-page-type',\n\t\t\t'post'      => $this->factory->post->create()\n\t\t];\n\t\t$_POST = [\n\t\t\t'data' => json_encode( [\n\t\t\t\t'rules' => [\n\t\t\t\t\t[\n\t\t\t\t\t\t'operator' => '=',\n\t\t\t\t\t\t'slug'     => 'rules_3',\n\t\t\t\t\t\t'source'   => 'hello',\n\t\t\t\t\t\t'value'    => 'hello'\n\t\t\t\t\t]\n\t\t\t\t],\n\t\t\t\t'slug'  => 'rules_3'\n\t\t\t] )\n\t\t];\n\n\t\tadd_filter( 'papi/settings/directories', function () {\n\t\t\treturn [1,  PAPI_FIXTURE_DIR . '/page-types'];\n\t\t} );\n\n\t\tif ( ! defined( 'DOING_PAPI_AJAX' ) ) {\n\t\t\tdefine( 'DOING_PAPI_AJAX', true );\n\t\t}\n\n\t\tdo_action( 'papi/ajax/get_rules_result' );\n\n\t\t$this->expectOutputRegex( '/.*\\S.*/' );\n\t\t$this->expectOutputRegex( '/\\{\\\"render\\\"\\:true\\}/' );\n\t}\n\n\tpublic function test_get_rules_result_fail() {\n\t\t$_GET = [\n\t\t\t'slug' => 'name'\n\t\t];\n\n\t\tdo_action( 'papi/ajax/get_rules_result' );\n\n\t\t$this->expectOutputRegex( '/.*\\S.*/' );\n\t\t$this->expectOutputRegex( '/\\{\\\"error\\\"\\:\\\"No rule found\\\"\\}/' );\n\t}\n\n\tpublic function test_get_rules_result_fail_2() {\n\t\t$_GET = [\n\t\t\t'page_type' => 'name'\n\t\t];\n\n\t\tdo_action( 'papi/ajax/get_rules_result' );\n\n\t\t$this->expectOutputRegex( '/.*\\S.*/' );\n\t\t$this->expectOutputRegex( '/\\{\\\"error\\\"\\:\\\"No rule found\\\"\\}/' );\n\t}\n\n\tpublic function test_get_rules_result_fail_3() {\n\t\t$_GET = [\n\t\t\t'slug'      => 'name',\n\t\t\t'page_type' => 'fake'\n\t\t];\n\n\t\tdo_action( 'papi/ajax/get_rules_result' );\n\n\t\t$this->expectOutputRegex( '/.*\\S.*/' );\n\t\t$this->expectOutputRegex( '/\\{\\\"error\\\"\\:\\\"No rule found\\\"\\}/' );\n\t}\n\n\tpublic function test_get_rules_result_fail_4() {\n\t\t$_GET = [\n\t\t\t'page_type' => 'simple-page-type'\n\t\t];\n\n\t\t$_POST = [\n\t\t\t'data' => json_encode( [\n\t\t\t\t'slug' => 'fake'\n\t\t\t] )\n\t\t];\n\n\t\tadd_filter( 'papi/settings/directories', function () {\n\t\t\treturn [1,  PAPI_FIXTURE_DIR . '/page-types'];\n\t\t} );\n\n\t\tif ( ! defined( 'DOING_PAPI_AJAX' ) ) {\n\t\t\tdefine( 'DOING_PAPI_AJAX', true );\n\t\t}\n\n\t\tdo_action( 'papi/ajax/get_rules_result' );\n\n\t\t$this->expectOutputRegex( '/.*\\S.*/' );\n\t\t$this->expectOutputRegex( '/\\{\\\"error\\\"\\:\\\"No rule found\\\"\\}/' );\n\t}\n\n\tpublic function test_get_rules_result_fail_5() {\n\t\t$_GET = [\n\t\t\t'page_type' => 'simple-page-type'\n\t\t];\n\n\t\t$_POST = [\n\t\t\t'data' => json_encode( [] )\n\t\t];\n\n\t\tadd_filter( 'papi/settings/directories', function () {\n\t\t\treturn [1,  PAPI_FIXTURE_DIR . '/page-types'];\n\t\t} );\n\n\t\tif ( ! defined( 'DOING_PAPI_AJAX' ) ) {\n\t\t\tdefine( 'DOING_PAPI_AJAX', true );\n\t\t}\n\n\t\tdo_action( 'papi/ajax/get_rules_result' );\n\n\t\t$this->expectOutputRegex( '/.*\\S.*/' );\n\t\t$this->expectOutputRegex( '/\\{\\\"error\\\"\\:\\\"No rule found\\\"\\}/' );\n\t}\n\n\tpublic function test_get_rules_result_fail_6() {\n\t\t$_GET = [\n\t\t\t'page_type' => 'rule-page-type',\n\t\t\t'post'      => $this->factory->post->create()\n\t\t];\n\t\t$_POST = [\n\t\t\t'data' => json_encode( [\n\t\t\t\t'rules' => [\n\t\t\t\t\t[\n\t\t\t\t\t\t'operator' => '=',\n\t\t\t\t\t\t'slug'     => 'rules_4',\n\t\t\t\t\t\t'source'   => 'fredrik',\n\t\t\t\t\t\t'value'    => 'hello'\n\t\t\t\t\t]\n\t\t\t\t],\n\t\t\t\t'slug'  => 'rules_3'\n\t\t\t] )\n\t\t];\n\n\t\tadd_filter( 'papi/settings/directories', function () {\n\t\t\treturn [1,  PAPI_FIXTURE_DIR . '/page-types'];\n\t\t} );\n\n\t\tif ( ! defined( 'DOING_PAPI_AJAX' ) ) {\n\t\t\tdefine( 'DOING_PAPI_AJAX', true );\n\t\t}\n\n\t\tdo_action( 'papi/ajax/get_rules_result' );\n\n\t\t$this->expectOutputRegex( '/.*\\S.*/' );\n\t\t$this->expectOutputRegex( '/\\{\\\"render\\\"\\:false\\}/' );\n\t}\n\n\tpublic function test_get_rules_result_fail_7() {\n\t\t$_GET = [\n\t\t\t'page_type' => 'rule-page-type',\n\t\t\t'post'      => $this->factory->post->create()\n\t\t];\n\t\t$_POST = [\n\t\t\t'data' => json_encode( [\n\t\t\t\t'rules' => [\n\t\t\t\t\t[\n\t\t\t\t\t\t'operator' => '=',\n\t\t\t\t\t\t'slug'     => 'rules_4',\n\t\t\t\t\t\t'source'   => 'fredrik',\n\t\t\t\t\t\t'value'    => 'hello'\n\t\t\t\t\t]\n\t\t\t\t],\n\t\t\t\t'slug'  => 'rules_3[]'\n\t\t\t] )\n\t\t];\n\n\t\tadd_filter( 'papi/settings/directories', function () {\n\t\t\treturn [1,  PAPI_FIXTURE_DIR . '/page-types'];\n\t\t} );\n\n\t\tif ( ! defined( 'DOING_PAPI_AJAX' ) ) {\n\t\t\tdefine( 'DOING_PAPI_AJAX', true );\n\t\t}\n\n\t\tdo_action( 'papi/ajax/get_rules_result' );\n\n\t\t$this->expectOutputRegex( '/.*\\S.*/' );\n\t\t$this->expectOutputRegex( '/\\{\\\"render\\\"\\:false\\}/' );\n\t}\n\n\tpublic function test_get_rules_result_fail_8() {\n\t\t$_GET = [\n\t\t\t'page_type' => 'rule-page-type',\n\t\t\t'post'      => $this->factory->post->create()\n\t\t];\n\t\t$_POST = [\n\t\t\t'data' => json_encode( [\n\t\t\t\t'rules' => [\n\t\t\t\t\t[\n\t\t\t\t\t\t'operator' => '=',\n\t\t\t\t\t\t'slug'     => 'rules_fake',\n\t\t\t\t\t\t'source'   => 'fredrik',\n\t\t\t\t\t\t'value'    => 'hello'\n\t\t\t\t\t]\n\t\t\t\t],\n\t\t\t\t'slug'  => 'rules_4'\n\t\t\t] )\n\t\t];\n\n\t\tadd_filter( 'papi/settings/directories', function () {\n\t\t\treturn [1,  PAPI_FIXTURE_DIR . '/page-types'];\n\t\t} );\n\n\t\tif ( ! defined( 'DOING_PAPI_AJAX' ) ) {\n\t\t\tdefine( 'DOING_PAPI_AJAX', true );\n\t\t}\n\n\t\tdo_action( 'papi/ajax/get_rules_result' );\n\n\t\t$this->expectOutputRegex( '/.*\\S.*/' );\n\t\t$this->expectOutputRegex( '/\\{\\\"error\\\"\\:\\\"No rule found\\\"\\}/' );\n\t}\n\n\tpublic function test_get_shortcode_fail() {\n\t\tif ( ! defined( 'DOING_AJAX' ) ) {\n\t\t\tdefine( 'DOING_AJAX', true );\n\t\t}\n\n\t\t$_GET = ['action' => 'get_shortcode'];\n\n\t\tdo_action( 'papi/ajax/get_shortcode' );\n\n\t\t$this->expectOutputRegex( '/{\\\"html\\\"\\:\\\"\\\"\\}/' );\n\t}\n\n\tpublic function test_get_shortcode_success() {\n\t\tif ( ! defined( 'DOING_AJAX' ) ) {\n\t\t\tdefine( 'DOING_AJAX', true );\n\t\t}\n\n\t\tadd_shortcode( 'hello', function () {\n\t\t\treturn 'hello';\n\t\t} );\n\n\t\t$_GET = ['action' => 'get_shortcode', 'shortcode' => '[hello]'];\n\n\t\tdo_action( 'papi/ajax/get_shortcode' );\n\n\t\t$this->expectOutputRegex( '/\\\"hello\\\"/' );\n\t}\n\n\tpublic function test_get_terms() {\n\t\tif ( ! defined( 'DOING_AJAX' ) ) {\n\t\t\tdefine( 'DOING_AJAX', true );\n\t\t}\n\n\t\tregister_taxonomy( 'test_taxonomy', 'post' );\n\n\t\t$term_id = $this->factory->term->create( [\n\t\t\t'taxonomy'    => 'test_taxonomy',\n\t\t\t'name'        => 'Apple',\n\t\t\t'description' => 'A yummy apple'\n\t\t] );\n\n\t\t$_GET = [\n\t\t\t'taxonomy' => 'test_taxonomy'\n\t\t];\n\n\t\t$post_id = $this->factory->post->create();\n\t\twp_set_object_terms( $post_id, $term_id, 'test_taxonomy' );\n\n\t\tdo_action( 'papi/ajax/get_terms' );\n\n\t\t$this->expectOutputRegex( '/.*\\S.*/' );\n\t\t$this->expectOutputRegex( sprintf( '/{\\\"%s\\\"\\:\\\"Apple\\\"\\}/', $term_id ) );\n\t}\n\n\tpublic function test_render_error() {\n\t\tif ( ! defined( 'DOING_AJAX' ) ) {\n\t\t\tdefine( 'DOING_AJAX', true );\n\t\t}\n\n\t\t$this->ajax->render_error( 'No property found' );\n\t\t$this->expectOutputRegex( '/.*\\S.*/' );\n\t\t$this->expectOutputRegex( '/\\{\\\"error\\\"\\:\\\"No property found\\\"\\}/' );\n\t}\n}\n"
  },
  {
    "path": "tests/cases/admin/class-papi-admin-assets-test.php",
    "content": "<?php\n\n/**\n * @group admin\n */\nclass Papi_Admin_Assets_Test extends WP_UnitTestCase {\n\n\tpublic function setUp() {\n\t\tparent::setUp();\n\t\t$this->assets = new Papi_Admin_Assets;\n\t}\n\n\tpublic function tearDown() {\n\t\tparent::tearDown();\n\t\tunset( $this->assets );\n\t}\n\n\tpublic function test_actions() {\n\t\tglobal $current_screen;\n\n\t    $current_screen = WP_Screen::get( 'admin_init' );\n\n\t\t$assets = new Papi_Admin_Assets;\n\n\t\t$this->assertGreaterThan( 0, has_action( 'admin_enqueue_scripts', [$assets, 'enqueue_css'] ) );\n\t\t$this->assertGreaterThan( 0, has_action( 'admin_enqueue_scripts', [$assets, 'enqueue_js'] ) );\n\t\t$this->assertGreaterThan( 0, has_action( 'admin_enqueue_scripts', [$assets, 'enqueue_locale'] ) );\n\n\t\t$current_screen = null;\n\t}\n\n\tpublic function test_enqueue_css() {\n\t\t$this->assertNull( $this->assets->enqueue_css() );\n\t}\n\n\tpublic function test_enqueue_js() {\n\t\t$_SERVER['REQUEST_URI'] = 'plugins.php';\n\t\t$this->assertNull( $this->assets->enqueue_js() );\n\t\t$_SERVER['REQUEST_URI'] = '';\n\t\t$this->assertNull( $this->assets->enqueue_js() );\n\t}\n\n\tpublic function test_enqueue_locale() {\n\t\t$this->assertNull( $this->assets->enqueue_locale() );\n\t}\n}\n"
  },
  {
    "path": "tests/cases/admin/class-papi-admin-columns-test.php",
    "content": "<?php\n\n/**\n * @group admin\n */\nclass Papi_Admin_Columns_Test extends WP_UnitTestCase {\n\n\tpublic function setUp() {\n\t\tparent::setUp();\n\t\t$_GET = [];\n\t\t$this->post_id = $this->factory->post->create();\n\t\t$this->term_id = $this->factory->term->create();\n\n\t\tadd_filter( 'papi/settings/directories', function () {\n\t\t\treturn [1,  PAPI_FIXTURE_DIR . '/page-types', PAPI_FIXTURE_DIR . '/taxonomy-types'];\n\t\t} );\n\t}\n\n\tpublic function tearDown() {\n\t\tparent::tearDown();\n\t\tunset( $_GET, $this->post_id, $this->term_id );\n\t}\n\n\tpublic function test_manage_page_type_posts_columns() {\n\t\t$_GET['post_type'] = 'page';\n\t\t$admin = new Papi_Admin_Columns;\n\t\t$arr = $admin->manage_page_type_posts_columns( [] );\n\t\t$this->assertSame( ['entry_type' => 'Type'], $arr );\n\t\t$_GET['post_type'] = 'fake';\n\t\t$admin = new Papi_Admin_Columns;\n\t\t$arr = $admin->manage_page_type_posts_columns( [] );\n\t\t$this->assertEmpty( $arr );\n\t\tunset( $_GET['post_type'] );\n\t}\n\n\tpublic function test_manage_page_type_posts_columns_hide_filter() {\n\t\t$_GET['post_type'] = 'page';\n\t\t$admin = new Papi_Admin_Columns;\n\t\tadd_filter( 'papi/settings/column_hide_page', '__return_true' );\n\t\t$arr = $admin->manage_page_type_posts_columns( [] );\n\t\t$this->assertFalse( isset( $arr['entry_type'] ) );\n\t\tunset( $_GET['post_type'] );\n\t}\n\n\tpublic function test_manage_page_type_sortable_columns() {\n\t\t$_GET['post_type'] = 'page';\n\t\t$admin = new Papi_Admin_Columns;\n\t\t$arr = $admin->manage_page_type_sortable_columns( [] );\n\t\t$this->assertSame( ['entry_type' => 'entry_type'], $arr );\n\t}\n\n\tpublic function test_manage_page_type_posts_columns_title_filter() {\n\t\t$_GET['post_type'] = 'page';\n\t\t$admin = new Papi_Admin_Columns;\n\t\t$arr = $admin->manage_page_type_posts_columns( [] );\n\t\t$this->assertSame( ['entry_type' => 'Type'], $arr );\n\n\t\tadd_filter( 'papi/settings/column_title_page', function () {\n\t\t\treturn 'Typer';\n\t\t} );\n\n\t\t$arr = $admin->manage_page_type_posts_columns( [] );\n\t\t$this->assertSame( ['entry_type' => 'Typer'], $arr );\n\t\tunset( $_GET['post_type'] );\n\t}\n\n\tpublic function test_manage_page_type_posts_custom_column_standard() {\n\t\t$_GET['post_type'] = 'page';\n\t\t$admin = new Papi_Admin_Columns;\n\t\t$admin->manage_page_type_posts_custom_column( 'entry_type', $this->post_id );\n\t\t$this->expectOutputRegex( '/Standard\\sPage/' );\n\t\tunset( $_GET['post_type'] );\n\t}\n\n\tpublic function test_manage_page_type_posts_custom_column_empty() {\n\t\t$_GET['post_type'] = 'page';\n\t\t$admin = new Papi_Admin_Columns;\n\t\t$admin->manage_page_type_posts_custom_column( '', $this->post_id );\n\t\t$this->expectOutputRegex( '//' );\n\t\tunset( $_GET['post_type'] );\n\t}\n\n\tpublic function test_manage_page_type_posts_custom_column_auto() {\n\t\t$_GET['post_type'] = 'page';\n\t\tupdate_post_meta( $this->post_id, papi_get_page_type_key(), 'simple-page-type' );\n\t\t$admin = new Papi_Admin_Columns;\n\t\t$admin->manage_page_type_posts_custom_column( 'entry_type', $this->post_id );\n\t\t$this->expectOutputRegex( '/Simple page/' );\n\t\tunset( $_GET['post_type'] );\n\t}\n\n\tpublic function test_manage_page_type_posts_custom_column_fake() {\n\t\t$post_id = $this->factory->post->create();\n\t\t$_GET['post_type'] = 'fake';\n\t\t$admin = new Papi_Admin_Columns;\n\t\t$admin->manage_page_type_posts_custom_column( 'entry_type', $post_id );\n\t\t$this->expectOutputRegex( '//' );\n\t\tunset( $_GET['post_type'] );\n\t}\n\n\tpublic function test_manage_taxonomy_posts_custom_column_empty() {\n\t\t$_GET['taxonomy'] = 'category';\n\t\t$admin = new Papi_Admin_Columns;\n\t\t$admin->manage_page_type_posts_custom_column( '', '', $this->term_id );\n\t\t$this->expectOutputRegex( '//' );\n\t\tunset( $_GET['taxonomy'] );\n\t}\n\n\tpublic function test_manage_page_type_posts_custom_column_hide_filter() {\n\t\t$_GET['post_type'] = 'page';\n\t\t$admin = new Papi_Admin_Columns;\n\t\tupdate_post_meta( $this->post_id, papi_get_page_type_key(), 'simple-page-type' );\n\t\tadd_filter( 'papi/settings/column_hide_page', '__return_true' );\n\n\t\t$admin->manage_page_type_posts_custom_column( 'entry_type', $this->post_id );\n\t\t$this->expectOutputRegex( '//' );\n\t\tunset( $_GET['post_type'] );\n\t}\n\n\tpublic function test_pre_get_posts() {\n\t\tglobal $pagenow;\n\t\t$pagenow = 'edit.php';\n\t\t$admin = new Papi_Admin_Columns;\n\n\t\t$_GET['page_type'] = 'simple-page-type';\n\t\t$query = $admin->pre_get_posts( new WP_Query() );\n\t\t$this->assertSame( [\n\t\t\t'meta_key'   => papi_get_page_type_key(),\n\t\t\t'meta_value' => 'simple-page-type'\n\t\t], $query->query_vars );\n\n\t\t$_GET['page_type'] = 'papi-standard-page';\n\t\t$query = $admin->pre_get_posts( new WP_Query() );\n\t\t$this->assertSame( [\n\t\t\t[\n\t\t\t\t'key'     => papi_get_page_type_key(),\n\t\t\t\t'compare' => 'NOT EXISTS'\n\t\t\t]\n\t\t], $query->query_vars['meta_query'] );\n\t\tunset( $_GET['page_type'] );\n\n\t\t$_GET['page_type'] = 'papi-standard-page';\n\t\t$query = new WP_Query();\n\t\t$query->set( 'orderby', 'entry_type' );\n\t\t$query = $admin->pre_get_posts( $query );\n\t\t$this->assertSame( papi_get_page_type_key(), $query->query_vars['meta_key'] );\n\t\t$this->assertSame( 'meta_value', $query->query_vars['orderby'] );\n\t\tunset( $_GET['page_type'] );\n\t}\n\n\tpublic function test_restrict_page_types() {\n\t\t$_GET['post_type'] = '';\n\t\t$admin = new Papi_Admin_Columns;\n\t\t$admin->restrict_page_types();\n\t\t$this->expectOutputRegex( '//' );\n\t\tunset( $_GET['post_type'] );\n\t}\n\n\tpublic function test_restrict_page_types_2() {\n\t\t$_GET['post_type'] = 'page';\n\t\tadd_filter( 'papi/settings/show_standard_page_type_in_filter_page', '__return_true' );\n\t\t$admin = new Papi_Admin_Columns;\n\t\t$admin->restrict_page_types();\n\t\t$this->expectOutputRegex( '/.*\\S.*/' );\n\t\tunset( $_GET['post_type'] );\n\t}\n\n\tpublic function test_setup_actions() {\n\t\t$_GET['post_type'] = 'page';\n\t\t$admin = new Papi_Admin_Columns;\n\t\t$this->assertSame( 10, has_action( 'restrict_manage_posts', [$admin, 'restrict_page_types'] ) );\n\t\tunset( $_GET['post_type'] );\n\t}\n\n\tpublic function test_setup_filters() {\n\t\t$_GET['post_type'] = 'page';\n\t\t$admin = new Papi_Admin_Columns;\n\t\t$this->assertSame( 10, has_filter( 'pre_get_posts', [$admin, 'pre_get_posts'] ) );\n\t\t$this->assertSame( 10, has_filter( 'manage_page_posts_columns', [$admin, 'manage_page_type_posts_columns'] ) );\n\t\t$this->assertSame( 10, has_filter( 'manage_page_posts_custom_column', [$admin, 'manage_page_type_posts_custom_column'] ) );\n\t\tunset( $_GET['post_type'] );\n\t}\n}\n"
  },
  {
    "path": "tests/cases/admin/class-papi-admin-entry-post-test.php",
    "content": "<?php\n\n/**\n * @group admin\n */\nclass Papi_Admin_Entry_Post_Test extends WP_UnitTestCase {\n\n\tpublic function setUp() {\n\t\tparent::setUp();\n\t\tpapi()->reset();\n\n\t\t$this->admin = new Papi_Admin_Entry_Post;\n\n\t\tadd_filter( 'papi/settings/directories', function () {\n\t\t\treturn [1,  PAPI_FIXTURE_DIR . '/page-types'];\n\t\t} );\n\t}\n\n\tpublic function tearDown() {\n\t\tparent::tearDown();\n\n\t\tunset( $_GET );\n\t}\n\n\tpublic function test_hidden_meta_boxes() {\n\t\t$_GET['post_type'] = 'page';\n\t\t$admin = new Papi_Admin_Entry_Post;\n\t\t$this->assertNull( $admin->hidden_meta_boxes() );\n\t\tdo_meta_boxes( 'papi-hidden-editor', 'normal', null );\n\t\t$this->expectOutputRegex( '/.*\\S.*/' );\n\t}\n\n\tpublic function test_hidden_meta_boxes_2() {\n\t\t$_GET['post_type'] = 'fake';\n\t\t$admin = new Papi_Admin_Entry_Post;\n\t\t$this->assertNull( $admin->hidden_meta_boxes() );\n\t\tdo_meta_boxes( 'papi-hidden-editor', 'normal', null );\n\t\t$this->expectOutputRegex( '//' );\n\t}\n\n\tpublic function test_hidden_meta_box_editor() {\n\t\t$this->admin->hidden_meta_box_editor();\n\t\t$this->expectOutputRegex( '/wp\\-editor\\-wrap/' );\n\t}\n\n\tpublic function test_load_post_new() {\n\t\t$_GET['post_type'] = '';\n\t\t$admin = new Papi_Admin_Entry_Post;\n\t\t$admin->load_post_new();\n\t\t$this->expectOutputRegex( '//' );\n\t}\n\n\tpublic function test_load_post_new_2() {\n\t\t$_SERVER['REQUEST_URI'] = 'http://site.com/wp-admin/post-new.php?post_type=page';\n\n\t\tadd_filter( 'wp_redirect', function( $location ) {\n\t\t\t$this->assertTrue( strpos( $location, 'edit.php?post_type=page&page=papi-add-new-page,page') !== false );\n\t\t\treturn false;\n\t\t} );\n\n\t\t$_GET['post_type'] = 'page';\n\t\t$admin = new Papi_Admin_Entry_Post;\n\t\t$admin->load_post_new();\n\t}\n\n\tpublic function test_load_post_new_3() {\n\t\t$_SERVER['REQUEST_URI'] = 'http://site.com/wp-admin/post-new.php?post_type=page';\n\n\t\tadd_filter( 'wp_redirect', function( $location ) {\n\t\t\t$this->assertTrue( strpos( $location, 'post-new.php?page_type=simple-page-type&post_type=page') !== false );\n\t\t\treturn false;\n\t\t} );\n\n\t\tadd_filter( 'papi/settings/only_page_type_page', function () {\n\t\t\treturn 'simple-page-type';\n\t\t} );\n\n\t\t$_GET['post_type'] = 'page';\n\t\t$admin = new Papi_Admin_Entry_Post;\n\t\t$admin->load_post_new();\n\t}\n\n\tpublic function test_load_post_new_4() {\n\t\tpapi_test_register_book_post_type();\n\n\t\tadd_filter( 'papi/settings/show_standard_page_type_book', '__return_false' );\n\n\t\t$_SERVER['REQUEST_URI'] = 'http://site.com/wp-admin/post-new.php?post_type=book';\n\t\tadd_filter( 'wp_redirect', function( $location ) {\n\t\t\t$this->assertTrue( strpos( $location, 'post-new.php?page_type=book-page-type&post_type=book') !== false );\n\t\t\treturn false;\n\t\t} );\n\t\t$_GET['post_type'] = 'book';\n\t\t$admin = new Papi_Admin_Entry_Post;\n\t\t$admin->load_post_new();\n\t}\n\n\tpublic function test_setup_actions() {\n\t\tglobal $current_screen;\n\n\t    $current_screen = WP_Screen::get( 'admin_init' );\n\n\t\t$admin = new Papi_Admin_Entry_Post;\n\n\t\t$this->assertSame( 10, has_action( 'load-post-new.php', [$admin, 'load_post_new'] ) );\n\t\t$this->assertSame( 10, has_action( 'add_meta_boxes', [$admin, 'hidden_meta_boxes'] ) );\n\t\t$this->assertSame( 10, has_action( 'redirect_post_location', [$admin, 'redirect_post_location'] ) );\n\n\t\t$current_screen = null;\n\t}\n}\n"
  },
  {
    "path": "tests/cases/admin/class-papi-admin-entry-taxonomy-test.php",
    "content": "<?php\n\n/**\n * @group admin\n */\nclass Papi_Admin_Entry_Taxonomy_Test extends WP_UnitTestCase {\n\n\tpublic function setUp() {\n\t\tparent::setUp();\n\t\tpapi()->reset();\n\t}\n\n\tpublic function tearDown() {\n\t\tparent::tearDown();\n\n\t\tunset( $_GET );\n\t}\n\n\tpublic function test_add_form_fields_empty() {\n\t\t$admin = new Papi_Admin_Entry_Taxonomy;\n\t\t$admin->add_form_fields();\n\t\t$this->expectOutputRegex( '//' );\n\t}\n\n\tpublic function test_add_form_fields_single() {\n\t\tadd_filter( 'papi/settings/directories', function () {\n\t\t\treturn [1,  PAPI_FIXTURE_DIR . '/taxonomy-types'];\n\t\t} );\n\n\t\t$_GET['taxonomy'] = 'category';\n\n\t\t$admin = new Papi_Admin_Entry_Taxonomy;\n\t\t$admin->setup_taxonomies_hooks();\n\t\t$admin->add_form_fields();\n\t\t$this->expectOutputRegex( '/input.*name\\=\\\"\\_papi\\_page\\_type\\\"/' );\n\t}\n\n\tpublic function test_add_form_fields_single_plus_standard() {\n\t\tadd_filter( 'papi/settings/directories', function () {\n\t\t\treturn [1,  PAPI_FIXTURE_DIR . '/taxonomy-types'];\n\t\t} );\n\n\t\tadd_filter( 'papi/settings/show_standard_taxonomy_type_category', '__return_true' );\n\n\t\t$_GET['taxonomy'] = 'category';\n\n\t\t$admin = new Papi_Admin_Entry_Taxonomy;\n\t\t$admin->setup_taxonomies_hooks();\n\t\t$admin->add_form_fields();\n\t\t$this->expectOutputRegex( '/select.*name\\=\\\"\\_papi\\_page\\_type\\\"/' );\n\t}\n\n\tpublic function test_add_form_fields_several() {\n\t\tadd_filter( 'papi/settings/directories', function () {\n\t\t\treturn [1,  PAPI_FIXTURE_DIR . '/taxonomy-types'];\n\t\t} );\n\n\t\t$_GET['taxonomy'] = 'post_tag';\n\n\t\t$admin = new Papi_Admin_Entry_Taxonomy;\n\t\t$admin->setup_taxonomies_hooks();\n\t\t$admin->add_form_fields();\n\t\t$this->expectOutputRegex( '/select.*name\\=\\\"\\_papi\\_page\\_type\\\"/' );\n\t}\n\n\tpublic function test_setup_actions() {\n\t\t$admin = new Papi_Admin_Entry_Taxonomy;\n\t\t$this->assertSame( 10, has_action( 'admin_init', [$admin, 'setup_taxonomies_hooks'] ) );\n\t\t$this->assertFalse( has_action( 'category_add_form_fields', [$admin, 'add_form_fields'] ) );\n\n\t\tadd_filter( 'papi/settings/directories', function () {\n\t\t\treturn [1,  PAPI_FIXTURE_DIR . '/taxonomy-types'];\n\t\t} );\n\n\t\t$admin->setup_taxonomies_hooks();\n\t\t$this->assertSame( 10, has_action( 'category_add_form_fields', [$admin, 'add_form_fields'] ) );\n\t}\n}\n"
  },
  {
    "path": "tests/cases/admin/class-papi-admin-menu-test.php",
    "content": "<?php\n\n/**\n * @group admin\n */\nclass Papi_Admin_Menu_Test extends WP_UnitTestCase {\n\n\tpublic function setUp() {\n\t\tparent::setUp();\n\n\t\tpapi()->reset();\n\n\t\t$_GET  = [];\n\t\t$_POST = [];\n\t\t$this->menu = new Papi_Admin_Menu();\n\n\t\tadd_filter( 'papi/settings/directories', function () {\n\t\t\treturn [1,  PAPI_FIXTURE_DIR . '/page-types', PAPI_FIXTURE_DIR . '/taxonomy-types'];\n\t\t} );\n\t}\n\n\tpublic function tearDown() {\n\t\tparent::tearDown();\n\t\tunset( $_GET, $_POST, $this->menu );\n\t}\n\n\tpublic function test_admin_bar_menu() {\n\t\tglobal $wp_post_types;\n\t\t$post_type = 'page';\n\t\t$labels = $wp_post_types[$post_type]->labels;\n\n\t\t$_GET['page_type'] = 1;\n\t\t$this->menu->admin_bar_menu();\n\t\t$this->assertSame( 'Add New Page', $labels->add_new_item );\n\t\t$this->assertSame( 'Edit Page', $labels->edit_item );\n\t\t$this->assertSame( 'View Page', $labels->view_item );\n\n\t\t$_GET['page_type'] = 'faq-page-type';\n\t\t$this->menu->admin_bar_menu();\n\t\t$this->assertSame( 'Add New Page', $labels->add_new_item );\n\t\t$this->assertSame( 'Edit Page', $labels->edit_item );\n\t\t$this->assertSame( 'View Page', $labels->view_item );\n\n\t\t$_GET['post_type'] = $post_type;\n\t\t$this->menu->admin_bar_menu();\n\t\t$this->assertSame( 'Add New FAQ page', $labels->add_new_item );\n\t\t$this->assertSame( 'Edit FAQ page', $labels->edit_item );\n\t\t$this->assertSame( 'View FAQ page', $labels->view_item );\n\t}\n\n\tpublic function test_admin_bar_menu_taxonomy() {\n\t\tglobal $wp_taxonomies;\n\n\t\tpapi_test_register_faq_taxonomy();\n\n\t\t$taxonomy = 'faq';\n\t\t$labels = $wp_taxonomies[$taxonomy]->labels;\n\n\t\t$_GET['taxonomy'] = 1;\n\t\t$this->menu->admin_bar_menu();\n\t\t$this->assertSame( 'Add New FAQ', $labels->add_new_item );\n\t\t$this->assertSame( 'Edit FAQ', $labels->edit_item );\n\t\t$this->assertSame( 'View FAQ', $labels->view_item );\n\n\t\t$_GET['taxonomy'] = 'faq';\n\t\t$_GET['entry_type'] = 'faq-taxonomy-type';\n\t\t$this->menu->admin_bar_menu();\n\t\t$this->assertSame( 'Add New FAQ taxonomy', $labels->add_new_item );\n\t\t$this->assertSame( 'Edit FAQ taxonomy', $labels->edit_item );\n\t\t$this->assertSame( 'View FAQ taxonomy', $labels->view_item );\n\t}\n\n\tpublic function test_admin_bar_menu_2() {\n\t\tglobal $wp_post_types;\n\t\t$post_type = 'post';\n\t\t$labels = $wp_post_types[$post_type]->labels;\n\t\t$_SERVER['REQUEST_URI'] = 'http://site.com/?page=papi/options/header-option-type';\n\n\t\t$_GET['post_type'] = $post_type;\n\t\t$this->menu->admin_bar_menu();\n\t\t$this->assertSame( 'Add New Post', $labels->add_new_item );\n\t\t$this->assertSame( 'Edit Post', $labels->edit_item );\n\t\t$this->assertSame( 'View Post', $labels->view_item );\n\t\t$_SERVER['REQUEST_URI'] = '';\n\t}\n\n\tpublic function test_page_items_menu() {\n\t\t$this->assertNull( $this->menu->page_items_menu() );\n\t}\n\n\tpublic function test_post_types_menu() {\n\t\tglobal $submenu;\n\t\t$submenu = [];\n\t\t$submenu['edit.php'] = [\n\t\t\t5  => [\n\t\t\t\t'All Posts',\n\t\t\t\t'edit_posts',\n\t\t\t\t'edit.php'\n\t\t\t],\n\t\t\t10 => [\n\t\t\t\t'Add New',\n\t\t\t\t'edit_posts',\n\t\t\t\t'post-new.php'\n\t\t\t]\n\t\t];\n\t\t$submenu['edit.php?post_type=page'] = [\n\t\t\t5  => [\n\t\t\t\t'All Pages',\n\t\t\t\t'edit_pages',\n\t\t\t\t'edit.php?post_type=page'\n\t\t\t],\n\t\t\t10 => [\n\t\t\t\t'Add New',\n\t\t\t\t'edit_pages',\n\t\t\t\t'post-new.php?post_type=page'\n\t\t\t]\n\t\t];\n\n\t\t$this->assertNull( $this->menu->post_types_menu() );\n\t\t$this->assertSame( 'post-new.php?page_type=post-page-type&post_type=post', $submenu['edit.php'][10][2] );\n\t\t$this->assertSame( 'edit.php?post_type=page&page=papi-add-new-page,page', $submenu['edit.php?post_type=page'][10][2] );\n\t}\n\n\tpublic function test_post_types_menu_2() {\n\t\tglobal $submenu;\n\t\t$submenu = [];\n\t\t$submenu['edit.php'] = [\n\t\t\t5  => [\n\t\t\t\t'All Posts',\n\t\t\t\t'edit_posts',\n\t\t\t\t'edit.php'\n\t\t\t],\n\t\t\t10 => [\n\t\t\t\t'Add New',\n\t\t\t\t'edit_posts',\n\t\t\t\t'post-new.php'\n\t\t\t]\n\t\t];\n\t\t$submenu['edit.php?post_type=page'] = [\n\t\t\t5  => [\n\t\t\t\t'All Pages',\n\t\t\t\t'edit_pages',\n\t\t\t\t'edit.php?post_type=page'\n\t\t\t],\n\t\t\t10 => [\n\t\t\t\t'Add New',\n\t\t\t\t'edit_pages',\n\t\t\t\t'post-new.php?post_type=page'\n\t\t\t]\n\t\t];\n\t\t$submenu['edit.php?post_type=book'] = [\n\t\t\t5  => [\n\t\t\t\t'All Pages',\n\t\t\t\t'edit_pages',\n\t\t\t\t'edit.php?post_type=book'\n\t\t\t],\n\t\t\t10 => [\n\t\t\t\t'Add New',\n\t\t\t\t'edit_pages',\n\t\t\t\t'post-new.php?post_type=book'\n\t\t\t]\n\t\t];\n\n\t\tadd_filter( 'papi/settings/only_page_type_post', function () {\n\t\t\treturn 'post-page-type';\n\t\t} );\n\n\t\tadd_filter( 'papi/settings/show_standard_page_type_hidden', '__return_true' );\n\n\t\tpapi_test_register_book_post_type();\n\t\tpapi_test_register_hidden_post_type();\n\n\t\t$this->assertNull( $this->menu->post_types_menu() );\n\t\t$this->assertSame( 'post-new.php?page_type=post-page-type&post_type=post', $submenu['edit.php'][10][2] );\n\t\t$this->assertSame( 'edit.php?post_type=page&page=papi-add-new-page,page', $submenu['edit.php?post_type=page'][10][2] );\n\t\t$this->assertSame( 'post-new.php?page_type=book-page-type&post_type=book', $submenu['edit.php?post_type=book'][10][2] );\n\t\t$this->assertSame( 'edit.php?post_type=hidden&page=papi-add-new-page,hidden', $submenu['edit.php?post_type=hidden'][10][2] );\n\t}\n\n\tpublic function test_post_types_menu_hidden_2() {\n\t\tglobal $submenu;\n\t\t$submenu = [];\n\n\t\tadd_filter( 'papi/settings/only_page_type_hidden', function () {\n\t\t\treturn 'hidden2-page-type';\n\t\t} );\n\n\t\tadd_filter( 'papi/settings/show_standard_page_type_hidden', '__return_false' );\n\n\t\t$this->assertNull( $this->menu->post_types_menu() );\n\t\t$this->assertSame( 'post-new.php?page_type=hidden2-page-type&post_type=hidden', $submenu['edit.php?post_type=hidden'][10][2] );\n\t}\n\n\tpublic function test_render_view() {\n\t\t$_GET['page'] = '';\n\t\t$this->menu->render_view();\n\t\t$this->expectOutputRegex( '/\\<h1\\>Papi\\s\\-\\s404\\<\\/h1\\>/' );\n\n\t\t$_GET['page'] = 'papi-add-new-page,page';\n\t\t$this->menu->render_view();\n\t\t$this->expectOutputRegex( '/Add New Page/' );\n\t}\n\n\tpublic function test_setup_actions_admin() {\n\t\tglobal $current_screen;\n\n\t\t$this->assertNull( $current_screen );\n\n\t\t$current_screen = WP_Screen::get( 'admin_init' );\n\n\t\t$menu = new Papi_Admin_Menu();\n\n\t\t$this->assertSame( 10, has_action( 'admin_init', [$menu, 'admin_bar_menu'] ) );\n\t\t$this->assertSame( 10, has_action( 'admin_menu', [$menu, 'page_items_menu'] ) );\n\t\t$this->assertSame( 10, has_action( 'admin_menu', [$menu, 'post_types_menu'] ) );\n\n\t\t$current_screen = null;\n\t}\n\n\tpublic function test_setup_actions() {\n\t\t$this->assertSame( 10, has_action( 'admin_bar_menu', [$this->menu, 'admin_bar_menu'] ) );\n\t}\n}\n"
  },
  {
    "path": "tests/cases/admin/class-papi-admin-meta-box-tabs-test.php",
    "content": "<?php\n\n/**\n * @group admin\n */\nclass Papi_Admin_Meta_Box_Tabs_Test extends WP_UnitTestCase {\n\n\tpublic function setUp() {\n\t\tparent::setUp();\n\n\t\t$this->tabs = [\n\t\t\tpapi_tab( [ 'title' => 'Content' ] ),\n\t\t\tpapi_tab( [ 'title' => 'More', 'sort_order' => 1 ], [\n\t\t\t\tpapi_property( [\n\t\t\t\t\t'type'  => 'string',\n\t\t\t\t\t'title' => 'Name'\n\t\t\t\t] )\n\t\t\t] )\n\t\t];\n\t}\n\n\tpublic function tearDown() {\n\t\tparent::tearDown();\n\t\tunset( $this->tabs );\n\t}\n\n\tpublic function test_construct() {\n\t\t$class = new Papi_Admin_Meta_Box_Tabs();\n\t\t$this->assertEmpty( $class->get_tabs() );\n\t}\n\n\tpublic function test_papi_admin_meta_box_tab_class() {\n\t\t$class = new Papi_Admin_Meta_Box_Tabs( $this->tabs, false );\n\t\t$tabs  = $class->get_tabs();\n\n\t\t$this->assertSame( 'More', $tabs[0]->title );\n\t\t$this->assertSame( 1, $tabs[0]->sort_order );\n\t\t$this->assertSame( 'string', $tabs[0]->properties[0]->type );\n\t\t$this->assertSame( 'Name', $tabs[0]->properties[0]->title );\n\t\t$this->assertSame( 'Content', $tabs[1]->title );\n\t\t$this->assertSame( 1000, $tabs[1]->sort_order );\n\t\t$this->assertEmpty( $tabs[1]->properties );\n\t}\n\n\tpublic function test_render() {\n\t\tpapi_render_properties( $this->tabs );\n\t\t$this->expectOutputRegex( '/.*\\S.*/' );\n\t}\n\n\tpublic function test_tabs() {\n\t\t$this->assertSame( 'Content', $this->tabs[0]->title );\n\t\t$this->assertEmpty( $this->tabs[0]->properties );\n\t\t$this->assertSame( 'More', $this->tabs[1]->title );\n\t\t$this->assertSame( 'string', $this->tabs[1]->properties[0]->type );\n\t\t$this->assertSame( 'Name', $this->tabs[1]->properties[0]->title );\n\t}\n}\n"
  },
  {
    "path": "tests/cases/admin/class-papi-admin-meta-box-test.php",
    "content": "<?php\n\n/**\n * @group admin\n */\nclass Papi_Admin_Meta_Box_Test extends WP_UnitTestCase {\n\n\t public function test_add_property() {\n \t\t$property = papi_property( [\n \t\t\t'type'  => 'string',\n \t\t\t'title' => 'Name'\n \t\t] );\n\t\t$class = new Papi_Admin_Meta_Box(\n\t\t\tnew Papi_Core_Box( [], [$property] )\n\t\t);\n\t\t$properties = function ( Papi_Admin_Meta_Box $box ) {\n\t\t\treturn $box->box->properties;\n\t\t};\n\t\t$properties = Closure::bind( $properties, null, $class );\n\t\t$this->assertSame( [$property], $properties( $class ) );\n\t}\n\n\tpublic function test_after_title() {\n\t\t$box = new Papi_Core_Box( [\n\t\t\t'title' => 'Content'\n\t\t] );\n\t\t$class = new Papi_Admin_Meta_Box( $box );\n\t\t$this->assertFalse( has_action( 'edit_form_after_title', [$class, 'move_meta_box_after_title'] ) );\n\n\t\t$_GET['post_type'] = 'page';\n\t\t$box = new Papi_Core_Box( [\n\t\t\t'context' => 'after_title',\n\t\t\t'title'   => 'Content'\n\t\t] );\n\t\t$class = new Papi_Admin_Meta_Box( $box );\n\t\t$this->assertSame( 10, has_action( 'edit_form_after_title', [$class, 'move_meta_box_after_title'] ) );\n\t\tunset( $_GET['post_type'] );\n\t}\n\n\tpublic function test_admin_meta_box_construct() {\n\t\t$box = new Papi_Core_Box( [\n\t\t\t'title' => 'Content'\n\t\t] );\n\t\t$class = new Papi_Admin_Meta_Box( $box );\n\n\t\t$class->setup_meta_box();\n\t\tdo_meta_boxes( '_papi_content', 'normal', null );\n\t\t$this->expectOutputRegex( '//' );\n\t}\n\n\tpublic function test_admin_meta_box_hidden_box() {\n\t\t$_GET['post_type'] = 'post';\n\n\t\t$box = new Papi_Core_Box( [\n\t\t\t'title' => 'Hidden'\n\t\t] );\n\n\t\t$class = new Papi_Admin_Meta_Box( $box );\n\t\t$this->assertSame( 10, has_action( 'add_meta_boxes', [$class, 'setup_meta_box'] ) );\n\n\t\t$box = new Papi_Core_Box( [\n\t\t\t'title'   => 'Hidden',\n\t\t\t'display' => false\n\t\t] );\n\n\t\t$class = new Papi_Admin_Meta_Box( $box );\n\t\t$this->assertFalse( has_action( 'add_meta_boxes', [$class, 'setup_meta_box'] ) );\n\t}\n\n\tpublic function test_admin_meta_box_capabilities() {\n\t\t$box = new Papi_Core_Box( [\n\t\t\t'title' => 'Content',\n\t\t\t'capabilities' => ['admin']\n\t\t] );\n\t\t$class = new Papi_Admin_Meta_Box( $box );\n\t\t$this->assertTrue( ! isset( $class->box ) );\n\t}\n\n\tpublic function test_move_meta_box_after_title() {\n\t\tglobal $post, $wp_meta_boxes, $pagenow;\n\t\t$pagenow = 'post-new.php';\n\t\t$post_id = $this->factory->post->create( [ 'post_type' => 'page' ] );\n\t\t$post    = get_post( $post_id );\n\t\t$box     = new Papi_Core_Box( [\n\t\t\t'title' => 'Content'\n\t\t] );\n\t\t$class   = new Papi_Admin_Meta_Box( $box );\n\t\tset_current_screen( $pagenow );\n\t\t$class->move_meta_box_after_title();\n\t\t$this->assertFalse( isset( $wp_meta_boxes['page']['normal'] ) );\n\t\t$this->expectOutputRegex( '/.*\\S.*/' );\n\t\t$GLOBALS['current_screen'] = null;\n\t}\n\n\tpublic function test_meta_box_css_classes() {\n\t\t$box = new Papi_Core_Box;\n\t\t$class = new Papi_Admin_Meta_Box( $box );\n\t\t$this->assertSame( ['papi-box'], $class->meta_box_css_classes( [] ) );\n\t}\n\n\tpublic function test_render_meta_box() {\n\t\t$box = new Papi_Core_Box( [\n\t\t\t'title' => 'Content'\n\t\t] );\n\t\t$class = new Papi_Admin_Meta_Box( $box );\n\n\t\t$this->assertNull( $class->render_meta_box( null, [] ) );\n\n\t\t$property = papi_property( [\n\t\t\t'type'  => 'string',\n\t\t\t'title' => 'Name'\n\t\t] );\n\n\t\t$class->render_meta_box( null, [\n\t\t\t'args' => [$property]\n\t\t] );\n\n\t\t$this->expectOutputRegex( '/.*\\S.*/' );\n\t}\n\n\tpublic function test_required() {\n\t\t$property = papi_property( [\n\t\t\t'type'     => 'string',\n\t\t\t'title'    => 'Name',\n\t\t\t'required' => true\n\t\t] );\n\n\t\t$box = new Papi_Core_Box( [\n\t\t\t'title' => 'Content'\n\t\t], [$property] );\n\t\t$box->set_option( 'required', true );\n\n\t\t$class = new Papi_Admin_Meta_Box( $box );\n\n\t\t$class->setup_meta_box();\n\t\t$title = function ( Papi_Admin_Meta_Box $box ) {\n\t\t\treturn $box->get_title();\n\t\t};\n\t\t$title = Closure::bind( $title, null, $class );\n\t\t$this->assertSame( sprintf(\n\t\t\t'Content <span class=\"papi-rq\" data-property-name=\"%s\" data-property-id=\"%s\">%s</span>',\n\t\t\t$property->title,\n\t\t\t$property->slug,\n\t\t\t'(required field)'\n\t\t), $title( $class ) );\n\t}\n}\n"
  },
  {
    "path": "tests/cases/admin/class-papi-admin-meta-handler-test.php",
    "content": "<?php\n\n/**\n * @group admin\n */\nclass Papi_Admin_Meta_Handler_Test extends WP_UnitTestCase {\n\n\tpublic function setUp() {\n\t\tparent::setUp();\n\n\t\t$this->handler = new Papi_Admin_Meta_Handler;\n\n\t\tadd_filter( 'papi/settings/directories', function () {\n\t\t\treturn [1,  PAPI_FIXTURE_DIR . '/page-types', PAPI_FIXTURE_DIR . '/taxonomy-types'];\n\t\t} );\n\n\t\t$this->term_id = $this->factory->term->create();\n\t\t$this->post_id = $this->factory->post->create();\n\n\t\t$_GET = [];\n\t\t$_GET['post'] = $this->post_id;\n\n\t\tupdate_post_meta( $this->post_id, papi_get_page_type_key(), 'properties-page-type' );\n\t\tupdate_term_meta( $this->term_id, papi_get_page_type_key(), 'properties-taxonomy-type' );\n\n\t\t$this->page_type       = papi_get_entry_type_by_id( 'properties-page-type' );\n\t\t$this->extra_page_type = papi_get_entry_type_by_id( 'extra-page-type' );\n\t\t$this->taxonomy_type   = papi_get_entry_type_by_id( 'properties-taxonomy-type' );\n\t}\n\n\tpublic function tearDown() {\n\t\tparent::tearDown();\n\t\tunset(\n\t\t\t$_GET,\n\t\t\t$_POST,\n\t\t\t$this->handler,\n\t\t\t$this->post_id,\n\t\t\t$this->term_id,\n\t\t\t$this->page_type,\n\t\t\t$this->extra_page_type,\n\t\t\t$this->taxonomy_type\n\t\t);\n\t}\n\n\tpublic function test_actions() {\n\t\t$this->assertSame( 1, has_action( 'save_post', [$this->handler, 'save_meta_boxes'] ) );\n\t\t$this->assertSame( 1, has_action( 'created_term', [$this->handler, 'save_meta_boxes'] ) );\n\t\t$this->assertSame( 1, has_action( 'edit_term', [$this->handler, 'save_meta_boxes'] ) );\n\t}\n\n\tpublic function test_save_meta_boxes() {\n\t\t$property = $this->page_type->get_property( 'string_test' );\n\n\t\t$_POST = papi_test_create_property_post_data( [\n\t\t\t'slug'  => $property->slug,\n\t\t\t'type'  => $property,\n\t\t\t'value' => 'Hello, world!'\n\t\t], $_POST );\n\n\t\t$user_id = $this->factory->user->create( [ 'role' => 'administrator' ] );\n\t\twp_set_current_user( $user_id );\n\n\t\t$_POST['papi_meta_nonce'] = wp_create_nonce( 'papi_save_data' );\n\t\t$_POST['post_ID'] = $this->post_id;\n\n\t\t$this->handler->save_meta_boxes( $this->post_id, get_post( $this->post_id ) );\n\t\twp_set_current_user( 0 );\n\n\t\t$this->assertSame( 'Hello, world!', papi_get_field( $this->post_id, $property->slug ) );\n\t}\n\n\tpublic function test_save_meta_boxes_2() {\n\t\t$property = $this->page_type->get_property( 'bool_test' );\n\n\t\t$_POST = papi_test_create_property_post_data( [\n\t\t\t'slug'  => $property->slug,\n\t\t\t'type'  => $property,\n\t\t\t'value' => 'on'\n\t\t], $_POST );\n\n\t\t$user_id = $this->factory->user->create( [ 'role' => 'administrator' ] );\n\t\twp_set_current_user( $user_id );\n\n\t\t$_POST['papi_meta_nonce'] = wp_create_nonce( 'papi_save_data' );\n\t\t$_POST['post_ID'] = $this->post_id;\n\n\t\t$this->handler->save_meta_boxes( $this->post_id, get_post( $this->post_id ) );\n\t\twp_set_current_user( 0 );\n\n\t\t$this->assertTrue( papi_get_field( $this->post_id, $property->slug ) );\n\t}\n\n\tpublic function test_save_meta_boxes_3() {\n\t\t$post_id  = $this->factory->post->create();\n\t\t$revs_id  = wp_save_post_revision( $post_id );\n\t\t$property = $this->page_type->get_property( 'string_test' );\n\n\t\t$_POST = papi_test_create_property_post_data( [\n\t\t\t'slug'  => $property->slug,\n\t\t\t'type'  => $property,\n\t\t\t'value' => 'Hello, world!'\n\t\t], $_POST );\n\n\t\t$user_id = $this->factory->user->create( [ 'role' => 'administrator' ] );\n\t\twp_set_current_user( $user_id );\n\n\t\t$_POST['papi_meta_nonce'] = wp_create_nonce( 'papi_save_data' );\n\t\t$_POST['post_ID'] = $revs_id;\n\n\t\t$this->handler->save_meta_boxes( $revs_id, get_post( $revs_id ) );\n\t\twp_set_current_user( 0 );\n\n\t\t$this->assertSame( 'Hello, world!', get_post_meta( $revs_id, unpapify( $property->slug ), true ) );\n\t}\n\n\t/**\n\t * Autosave.\n\t */\n\tpublic function test_save_meta_boxes_4() {\n\t\t$post_id  = $this->factory->post->create();\n\t\t$property = $this->page_type->get_property( 'string_test' );\n\t\t$revs_id  = wp_save_post_revision( $post_id );\n\t\t$_POST = papi_test_create_property_post_data( [\n\t\t\t'slug'  => $property->slug,\n\t\t\t'type'  => $property,\n\t\t\t'value' => 'Hello, world!'\n\t\t], $_POST );\n\n\t\t$user_id = $this->factory->user->create( [ 'role' => 'administrator' ] );\n\t\twp_set_current_user( $user_id );\n\n\t\t$_POST['papi_meta_nonce'] = wp_create_nonce( 'papi_save_data' );\n\t\t$_POST['data'] = ['wp_autosave' => ['post_id' => $revs_id]];\n\n\t\t$this->handler->save_meta_boxes( $post_id, get_post( $post_id ) );\n\t\twp_set_current_user( 0 );\n\n\t\t$this->assertSame( 'Hello, world!', get_post_meta( $post_id, unpapify( $property->slug ), true ) );\n\t}\n\n\t/**\n\t * Autosave draft.\n\t */\n\tpublic function test_save_meta_boxes_5() {\n\t\t$post_id  = $this->factory->post->create();\n\t\t$property = $this->page_type->get_property( 'string_test' );\n\n\t\t$_POST = papi_test_create_property_post_data( [\n\t\t\t'slug'  => $property->slug,\n\t\t\t'type'  => $property,\n\t\t\t'value' => 'Hello, world!'\n\t\t], $_POST );\n\n\t\t$user_id = $this->factory->user->create( [ 'role' => 'administrator' ] );\n\t\twp_set_current_user( $user_id );\n\n\t\t$_POST['papi_meta_nonce'] = wp_create_nonce( 'papi_save_data' );\n\t\t$_POST['data'] = ['wp_autosave' => ['post_id' => $post_id, 'auto_draft' => '1']];\n\n\t\t$this->handler->save_meta_boxes( $post_id, get_post( $post_id ) );\n\t\twp_set_current_user( 0 );\n\n\t\t$this->assertSame( 'Hello, world!', get_post_meta( $post_id, unpapify( $property->slug ), true ) );\n\t}\n\n\t/**\n\t * @issue 126\n\t */\n\tpublic function test_save_meta_boxes_apostrophe() {\n\t\t$property = $this->page_type->get_property( 'string_test' );\n\n\t\t$_POST = papi_test_create_property_post_data( [\n\t\t\t'slug'  => $property->slug,\n\t\t\t'type'  => $property,\n\t\t\t'value' => \"I'm home\"\n\t\t], $_POST );\n\n\t\t$user_id = $this->factory->user->create( [ 'role' => 'administrator' ] );\n\t\twp_set_current_user( $user_id );\n\n\t\t$_POST['papi_meta_nonce'] = wp_create_nonce( 'papi_save_data' );\n\t\t$_POST['post_ID'] = $this->post_id;\n\n\t\t$this->handler->save_meta_boxes( $this->post_id, get_post( $this->post_id ) );\n\t\twp_set_current_user( 0 );\n\n\t\t$this->assertSame( \"I'm home\", papi_get_field( $this->post_id, $property->slug ) );\n\t}\n\n\t/**\n\t * Wrong nonce.\n\t */\n\tpublic function test_save_meta_boxes_fail_1() {\n\t\t$property = $this->page_type->get_property( 'string_test' );\n\n\t\t$_POST = papi_test_create_property_post_data( [\n\t\t\t'slug'  => $property->slug,\n\t\t\t'type'  => $property,\n\t\t\t'value' => 'Hello, world!'\n\t\t], $_POST );\n\n\t\t$user_id = $this->factory->user->create( [ 'role' => 'administrator' ] );\n\t\twp_set_current_user( $user_id );\n\n\t\t$_POST['papi_meta_nonce'] = '';\n\t\t$_POST['post_ID'] = $this->post_id;\n\n\t\t$this->handler->save_meta_boxes( $this->post_id, get_post( $this->post_id ) );\n\t\twp_set_current_user( 0 );\n\n\t\t$this->assertNull( papi_get_field( $this->post_id, $property->slug ) );\n\t}\n\n\t/**\n\t * Wrong post id.\n\t */\n\tpublic function test_save_meta_boxes_fail_2() {\n\t\t$property = $this->page_type->get_property( 'string_test' );\n\n\t\t$_POST = papi_test_create_property_post_data( [\n\t\t\t'slug'  => $property->slug,\n\t\t\t'type'  => $property,\n\t\t\t'value' => 'Hello, world!'\n\t\t], $_POST );\n\n\t\t$user_id = $this->factory->user->create( [ 'role' => 'administrator' ] );\n\t\twp_set_current_user( $user_id );\n\n\t\t$_POST['papi_meta_nonce'] = wp_create_nonce( 'papi_save_data' );\n\n\t\t$this->handler->save_meta_boxes( 0, null );\n\t\twp_set_current_user( 0 );\n\n\t\t$this->assertNull( papi_get_field( $this->post_id, $property->slug ) );\n\t}\n\n\t/**\n\t * Wrong capability.\n\t */\n\tpublic function test_save_meta_boxes_fail_3() {\n\t\t$property = $this->page_type->get_property( 'string_test' );\n\n\t\t$_POST = papi_test_create_property_post_data( [\n\t\t\t'slug'  => $property->slug,\n\t\t\t'type'  => $property,\n\t\t\t'value' => 'Hello, world!'\n\t\t], $_POST );\n\n\t\t$user_id = $this->factory->user->create( [ 'role' => 'read' ] );\n\t\twp_set_current_user( $user_id );\n\n\t\t$_POST['papi_meta_nonce'] = wp_create_nonce( 'papi_save_data' );\n\t\t$_POST['post_ID'] = $this->post_id;\n\n\t\t$this->handler->save_meta_boxes( $this->post_id, get_post( $this->post_id ) );\n\t\twp_set_current_user( 0 );\n\n\t\t$this->assertNull( papi_get_field( $this->post_id, $property->slug ) );\n\t}\n\n\tpublic function test_save_meta_boxes_fail_4() {\n\t\t$property = $this->page_type->get_property( 'bool_test' );\n\n\t\t$_POST = papi_test_create_property_post_data( [\n\t\t\t'slug'  => $property->slug,\n\t\t\t'type'  => 'kvacker',\n\t\t\t'value' => 'on'\n\t\t], $_POST );\n\n\t\t$user_id = $this->factory->user->create( [ 'role' => 'administrator' ] );\n\t\twp_set_current_user( $user_id );\n\n\t\t$_POST['papi_meta_nonce'] = wp_create_nonce( 'papi_save_data' );\n\t\t$_POST['post_ID'] = $this->post_id;\n\n\t\t$this->handler->save_meta_boxes( $this->post_id, get_post( $this->post_id ) );\n\t\twp_set_current_user( 0 );\n\n\t\t$this->assertNull( papi_get_field( $this->post_id, $property->slug ) );\n\t}\n\n\tpublic function test_save_properties() {\n\t\t$property = $this->page_type->get_property( 'string_test' );\n\t\t$_POST = papi_test_create_property_post_data( [\n\t\t\t'slug'  => $property->slug,\n\t\t\t'type'  => $property,\n\t\t\t'value' => 'Hello, world!'\n\t\t], $_POST );\n\n\t\t$this->handler->save_properties( $this->post_id );\n\n\t\t$value = papi_get_field( $this->post_id, $property->slug );\n\n\t\t$this->assertSame( 'Hello, world!', $value );\n\n\t\t$property = $this->page_type->get_property( 'number_test' );\n\t\t$_POST = papi_test_create_property_post_data( [\n\t\t\t'slug'  => $property->slug,\n\t\t\t'type'  => $property,\n\t\t\t'value' => 42\n\t\t], $_POST );\n\n\t\t$this->handler->save_properties( 0 );\n\n\t\t$value = papi_get_field( 0, $property->slug );\n\n\t\t$this->assertNull( $value );\n\t}\n\n\tpublic function test_pre_data() {\n\t\t$_POST = [\n\t\t\t'_papi_item'     => 'Item 42',\n\t\t\t'_papi_item_2'   => '',\n\t\t\t'_papi_sections' => [\n\t\t\t\t0 => [\n\t\t\t\t\t'sort_order' => 'down'\n\t\t\t\t]\n\t\t\t]\n\t\t];\n\n\t\t$this->handler->save_properties( $this->post_id );\n\n\t\t$this->assertSame( 'Item 42', get_post_meta( $this->post_id, '_papi_item', true ) );\n\t\t$this->assertEmpty( get_post_meta( $this->post_id, '_papi_item_2', true ) );\n\t\t$this->assertSame( 'down', get_post_meta( $this->post_id, '_papi_sections_0_sort_order', true ) );\n\t}\n\n\tpublic function test_overwrite() {\n\t\t$post_id  = $this->factory->post->create();\n\t\t$property = $this->extra_page_type->get_property( 'post_content' );\n\t\t$_POST    = papi_test_create_property_post_data( [\n\t\t\t'slug'  => $property->slug,\n\t\t\t'type'  => $property,\n\t\t\t'value' => 'Hello, world!'\n\t\t], $_POST );\n\n\t\t$_GET['post'] = $post_id;\n\t\tupdate_post_meta( $post_id, papi_get_page_type_key(), 'extra-page-type' );\n\t\t$this->handler->save_properties( $post_id );\n\n\t\t$this->flush_cache();\n\t\t$value = papi_get_field( $post_id, $property->slug );\n\t\t$this->assertSame( '<p>Hello, world!</p>', trim( $value ) );\n\n\t\t$this->flush_cache();\n\t\t$post = get_post( $post_id );\n\t\t$this->assertSame( 'Hello, world!', trim( $post->post_content ) );\n\t}\n\n\tpublic function test_save_meta_boxes_taxonomy() {\n\t\t$property = $this->taxonomy_type->get_property( 'string_test' );\n\n\t\t$_POST = papi_test_create_property_post_data( [\n\t\t\t'slug'  => $property->slug,\n\t\t\t'type'  => $property,\n\t\t\t'value' => 'Hello, world!'\n\t\t], $_POST );\n\n\t\t$user_id = $this->factory->user->create( [ 'role' => 'administrator' ] );\n\t\twp_set_current_user( $user_id );\n\n\t\t$_POST['papi_meta_nonce'] = wp_create_nonce( 'papi_save_data' );\n\t\t$_POST['term_id'] = $this->term_id;\n\t\t$_GET['meta_type'] = 'term';\n\n\t\t$this->handler->save_meta_boxes( $this->term_id );\n\t\twp_set_current_user( 0 );\n\n\t\t$this->assertSame( 'Hello, world!', papi_get_term_field( $this->term_id, $property->slug ) );\n\t}\n\n\t/**\n\t * Wrong nonce.\n\t */\n\tpublic function test_save_meta_boxes_taxonomy_fail_1() {\n\t\t$property = $this->taxonomy_type->get_property( 'string_test' );\n\n\t\t$_POST = papi_test_create_property_post_data( [\n\t\t\t'slug'  => $property->slug,\n\t\t\t'type'  => $property,\n\t\t\t'value' => 'Hello, world!'\n\t\t], $_POST );\n\n\t\t$user_id = $this->factory->user->create( [ 'role' => 'administrator' ] );\n\t\twp_set_current_user( $user_id );\n\n\t\t$_POST['papi_meta_nonce'] = '';\n\t\t$_POST['term_id'] = $this->term_id;\n\t\t$_GET['meta_type'] = 'term';\n\n\t\t$this->handler->save_meta_boxes( $this->term_id );\n\t\twp_set_current_user( 0 );\n\n\t\t$this->assertNull( papi_get_term_field( $this->term_id, $property->slug ) );\n\t}\n\n\t/**\n\t * Wrong term id.\n\t */\n\tpublic function test_save_meta_boxes_taxonomy_fail_2() {\n\t\t$property = $this->taxonomy_type->get_property( 'string_test' );\n\n\t\t$_POST = papi_test_create_property_post_data( [\n\t\t\t'slug'  => $property->slug,\n\t\t\t'type'  => $property,\n\t\t\t'value' => 'Hello, world!'\n\t\t], $_POST );\n\n\t\t$user_id = $this->factory->user->create( [ 'role' => 'administrator' ] );\n\t\twp_set_current_user( $user_id );\n\n\t\t$_POST['papi_meta_nonce'] = wp_create_nonce( 'papi_save_data' );\n\t\t$_GET['meta_type'] = 'term';\n\n\t\t$this->handler->save_meta_boxes( 0, null );\n\t\twp_set_current_user( 0 );\n\n\t\t$this->assertNull( papi_get_field( $this->term_id, $property->slug ) );\n\t}\n\n\t/**\n\t * Wrong capability.\n\t */\n\tpublic function test_save_meta_boxes_taxonomy_fail_3() {\n\t\t$property = $this->taxonomy_type->get_property( 'string_test' );\n\n\t\t$_POST = papi_test_create_property_post_data( [\n\t\t\t'slug'  => $property->slug,\n\t\t\t'type'  => $property,\n\t\t\t'value' => 'Hello, world!'\n\t\t], $_POST );\n\n\t\t$user_id = $this->factory->user->create( [ 'role' => 'read' ] );\n\t\twp_set_current_user( $user_id );\n\n\t\t$_POST['papi_meta_nonce'] = wp_create_nonce( 'papi_save_data' );\n\t\t$_POST['term_id'] = $this->term_id;\n\t\t$_GET['meta_type'] = 'term';\n\n\t\t$this->handler->save_meta_boxes( $this->term_id );\n\t\twp_set_current_user( 0 );\n\n\t\t$this->assertNull( papi_get_term_field( $this->term_id, $property->slug ) );\n\t}\n\n\tpublic function test_restore_post_revision() {\n\t\t$post_id  = $this->factory->post->create();\n\t\tupdate_post_meta( $post_id, papi_get_page_type_key(), 'properties-page-type' );\n\n\t\t$revs_id  = wp_save_post_revision( $post_id );\n\t\t$property = $this->page_type->get_property( 'string_test' );\n\n\t\t$_POST = papi_test_create_property_post_data( [\n\t\t\t'slug'  => $property->slug,\n\t\t\t'type'  => $property,\n\t\t\t'value' => 'Hello, world!'\n\t\t], $_POST );\n\n\t\t$user_id = $this->factory->user->create( [ 'role' => 'administrator' ] );\n\t\twp_set_current_user( $user_id );\n\n\t\t$_POST['papi_meta_nonce'] = wp_create_nonce( 'papi_save_data' );\n\t\t$_POST['post_ID'] = $revs_id;\n\t\t$_POST[papi_get_page_type_key()] = 'properties-page-type';\n\n\t\t$this->handler->save_meta_boxes( $revs_id, get_post( $revs_id ) );\n\t\twp_set_current_user( 0 );\n\n\t\t$this->assertSame( 'Hello, world!', get_post_meta( $revs_id, unpapify( $property->slug ), true ) );\n\n\t\t$this->handler->restore_post_revision( $post_id, $revs_id );\n\n\t\t$this->assertSame( 'Hello, world!', get_post_meta( $post_id, unpapify( $property->slug ), true ) );\n\t}\n\n\tpublic function test_save_revision() {\n\t\t$post_id = $this->factory->post->create();\n\t\tupdate_post_meta( $post_id, papi_get_page_type_key(), 'properties-page-type' );\n\n\t\t$revs_id  = wp_save_post_revision( $post_id );\n\t\t$property = $this->page_type->get_property( 'string_test' );\n\n\t\tupdate_post_meta( $post_id, 'string_test', 'Hello, world!' );\n\n\t\t$_POST['papi_meta_nonce'] = wp_create_nonce( 'papi_save_data' );\n\n\t\t$this->handler->save_revision( $revs_id );\n\n\t\t$this->assertSame( 'Hello, world!', get_post_meta( $revs_id, 'string_test', true ) );\n\t}\n}\n"
  },
  {
    "path": "tests/cases/admin/class-papi-admin-option-handler-test.php",
    "content": "<?php\n\n/**\n * @group admin\n */\nclass Papi_Admin_Option_Handler_Test extends WP_UnitTestCase {\n\n\tpublic function setUp() {\n\t\tparent::setUp();\n\n\t\tadd_filter( 'papi/settings/directories', function () {\n\t\t\treturn [1,  PAPI_FIXTURE_DIR . '/page-types'];\n\t\t} );\n\n\t\tadd_filter( 'papi/is_admin', '__return_true' );\n\n\t\t$this->page_type = papi_get_entry_type_by_id( 'options/header-option-type' );\n\t\t$this->property  = $this->page_type->get_property( 'name' );\n\t}\n\n\tpublic function tearDown() {\n\t\tparent::tearDown();\n\n\t\tremove_filter( 'papi/is_admin', '__return_true' );\n\n\t\tunset(\n\t\t\t$_POST,\n\t\t\t$_SERVER['REQUEST_METHOD'],\n\t\t\t$this->handler,\n\t\t\t$this->property,\n\t\t\t$this->page_type\n\t\t);\n\t}\n\n\tpublic function test_actions() {\n\t\t$handler = new Papi_Admin_Option_Handler;\n\t\t$this->assertGreaterThan( 0, has_action( 'admin_init', [$handler, 'save_properties'] ) );\n\t}\n\n\tpublic function test_save_options_without_nonce() {\n\t\t$_POST = papi_test_create_property_post_data( [\n\t\t\t'slug'  => $this->property->slug,\n\t\t\t'type'  => $this->property,\n\t\t\t'value' => 'Hello, world!'\n\t\t], $_POST );\n\n\t\t$_SERVER['REQUEST_METHOD'] = 'POST';\n\t\t$_SERVER['REQUEST_URI'] = 'http://site.com/wp-admin/options-general.php?page=papi/options/header-option-type';\n\n\t\t( new Papi_Admin_Option_Handler )->save_properties();\n\n\t\t$this->assertNull( papi_get_option( $this->property->slug ) );\n\t}\n\n\tpublic function test_save_options_with_nonce() {\n\t\t$_POST = papi_test_create_property_post_data( [\n\t\t\t'slug'  => $this->property->slug,\n\t\t\t'type'  => $this->property,\n\t\t\t'value' => 'Hello, world!'\n\t\t], $_POST );\n\n\t\t$_POST['papi_meta_nonce'] = wp_create_nonce( 'papi_save_data' );\n\n\t\t$_SERVER['REQUEST_METHOD'] = 'POST';\n\t\t$_SERVER['REQUEST_URI'] = 'http://site.com/wp-admin/options-general.php?page=papi/options/header-option-type';\n\n\t\t( new Papi_Admin_Option_Handler )->save_properties();\n\n\t\t$this->assertSame( 'Hello, world!', papi_get_option( $this->property->slug ) );\n\t}\n\n\tpublic function test_save_properties_fail() {\n\t\t$_POST = papi_test_create_property_post_data( [\n\t\t\t'slug'  => $this->property->slug,\n\t\t\t'type'  => $this->property,\n\t\t\t'value' => 'Hello, world!'\n\t\t], $_POST );\n\n\t\t$old_request_uri = $_SERVER['REQUEST_URI'];\n\n\t\t$_SERVER['REQUEST_METHOD'] = 'POST';\n\t\t$_SERVER['REQUEST_URI'] = 'http://site.com/wp-admin/options-general.php?page=papi/options/header-option-type';\n\n\t\t( new Papi_Admin_Option_Handler )->save_properties();\n\n\t\t$_SERVER['REQUEST_URI'] = $old_request_uri;\n\n\t\t$this->assertNull( papi_get_option( $this->property->slug ) );\n\t}\n}\n"
  },
  {
    "path": "tests/cases/admin/class-papi-admin-page-type-switcher.php",
    "content": "<?php\n\n/**\n * @group admin\n */\nclass Papi_Admin_Page_Type_Switcher_Test extends WP_UnitTestCase {\n\n\tpublic function setUp() {\n\t\tparent::setUp();\n\n\t\tadd_filter( 'papi/settings/directories', function () {\n\t\t\treturn PAPI_FIXTURE_DIR . '/page-types';\n\t\t} );\n\t}\n\n\tpublic function tearDown() {\n\t\tparent::tearDown();\n\n\t\tunset(\n\t\t\t$_GET\n\t\t);\n\t}\n\n\tpublic function test_admin_init() {\n\t\t$switcher = new Papi_Admin_Page_Type_Switcher;\n\t\t$switcher->admin_init();\n\t\t$this->assertSame( 10, has_action( 'post_submitbox_misc_actions', [$switcher, 'metabox'] ) );\n\t\t$this->assertSame( 10, has_action( 'save_post', [$switcher, 'save_post'] ) );\n\t}\n\n\tpublic function test_metabox_1() {\n\t\t$switcher = new Papi_Admin_Page_Type_Switcher;\n\t\t$switcher->metabox();\n\t\t$this->expectOutputRegex( '//' );\n\t}\n\n\tpublic function test_metabox_2() {\n\t\t$switcher = new Papi_Admin_Page_Type_Switcher;\n\n\t\t$_GET = [\n\t\t\t'post_type' => 'page',\n\t\t\t'page_type' => 'properties-page-type'\n\t\t];\n\n\t\t$switcher->metabox();\n\t\t$this->expectOutputRegex( '/\\<div/' );\n\t}\n\n\tpublic function test_save_post() {\n\t\t$switcher = new Papi_Admin_Page_Type_Switcher;\n\t\t$post_id  = $this->factory->post->create( ['post_type' => 'page'] );\n\t\t$post     = get_post( $post_id );\n\t\t$_POST    = [];\n\n\t\t// Bad values.\n\t\t$this->assertFalse( $switcher->save_post( 0, null ) );\n\t\t$this->assertFalse( $switcher->save_post( 0, null ) );\n\n\t\t// Nonce check.\n\t\t$this->assertFalse( $switcher->save_post( $post_id, $post ) );\n\n\t\t$_POST['papi_meta_nonce'] = wp_create_nonce( 'papi_save_data' );\n\n\t\t// Empty post values.\n\t\t$this->assertFalse( $switcher->save_post( $post_id, $post ) );\n\n\t\t$_POST[papi_get_page_type_key()] = 'properties-page-type';\n\t\t$_POST[papi_get_page_type_key( 'switch' )] = 'properties-page-type';\n\n\t\t// Same post type ids.\n\t\t$this->assertFalse( $switcher->save_post( $post_id, $post ) );\n\n\t\t$_POST[papi_get_page_type_key()] = 'properties-page-type';\n\t\t$_POST[papi_get_page_type_key( 'switch' )] = 'no-existing-page-type';\n\n\t\t// Bad page type and post type objects.\n\t\t$this->assertFalse( $switcher->save_post( $post_id, $post ) );\n\n\t\t$_POST[papi_get_page_type_key( 'switch' )] = 'post-page-type';\n\t\t$_POST['post_type'] = 'page';\n\n\t\t// Bad post type.\n\t\t$this->assertFalse( $switcher->save_post( $post_id, $post ) );\n\n\t\t$_POST[papi_get_page_type_key( 'switch' )] = 'display-not-page-type';\n\n\t\t// Bad capabilities.\n\t\t$this->assertFalse( $switcher->save_post( $post_id, $post ) );\n\n\t\t$_POST[papi_get_page_type_key( 'switch' )] = 'simple-page-type';\n\n\t\t// Bad capabilities.\n\t\t$this->assertFalse( $switcher->save_post( $post_id, $post ) );\n\n\t\tupdate_post_meta( $post_id, papi_get_page_type_key(), 'properties-page-type' );\n\t\tupdate_post_meta( $post_id, 'string_test', 'Fredrik' );\n\t\tupdate_post_meta( $post_id, 'hidden_test', 'Fredrik' );\n\n\t\t$this->assertSame( 'Fredrik', papi_get_field( $post_id, 'string_test' ) );\n\t\t$this->assertSame( 'Fredrik', papi_get_field( $post_id, 'hidden_test' ) );\n\n\t\t$user_id = $this->factory->user->create( [ 'role' => 'administrator' ] );\n\t\twp_set_current_user( $user_id );\n\n\t\t// Create new nonce because of new user.\n\t\t$_POST['papi_meta_nonce'] = wp_create_nonce( 'papi_save_data' );\n\t\t$_POST[papi_get_page_type_key( 'switch' )] = 'simple-page-type';\n\n\t\t// Success!\n\t\t$this->assertTrue( $switcher->save_post( $post_id, $post ) );\n\n\t\t$this->assertSame( 'Fredrik', papi_get_field( $post_id, 'string_test' ) );\n\t\t$this->assertNull( papi_get_field( $post_id, 'hidden_test' ) );\n\n\t\twp_set_current_user( 0 );\n\t}\n\n\tpublic function test_save_post_standard_type() {\n\t\t$switcher = new Papi_Admin_Page_Type_Switcher;\n\t\t$post_id  = $this->factory->post->create( ['post_type' => 'page'] );\n\t\t$post     = get_post( $post_id );\n\t\t$_POST    = [];\n\n\t\t// Bad values.\n\t\t$this->assertFalse( $switcher->save_post( 0, null ) );\n\t\t$this->assertFalse( $switcher->save_post( 0, null ) );\n\n\t\t// Nonce check.\n\t\t$this->assertFalse( $switcher->save_post( $post_id, $post ) );\n\n\t\t$_POST['papi_meta_nonce'] = wp_create_nonce( 'papi_save_data' );\n\n\t\t// Empty post values.\n\t\t$this->assertFalse( $switcher->save_post( $post_id, $post ) );\n\n\t\t$_POST[papi_get_page_type_key()] = 'properties-page-type';\n\t\t$_POST[papi_get_page_type_key( 'switch' )] = papi_get_standard_page_type_id( $post->post_type );\n\n\t\t// Same post type ids.\n\t\t$this->assertFalse( $switcher->save_post( $post_id, $post ) );\n\n\t\t$_POST[papi_get_page_type_key()] = 'properties-page-type';\n\t\t$_POST[papi_get_page_type_key( 'switch' )] = papi_get_standard_page_type_id( $post->post_type );\n\n\t\t// Bad page type and post type objects.\n\t\t$this->assertFalse( $switcher->save_post( $post_id, $post ) );\n\n\t\t$_POST[papi_get_page_type_key( 'switch' )] = 'post-page-type';\n\t\t$_POST['post_type'] = 'page';\n\n\t\t// Bad post type.\n\t\t$this->assertFalse( $switcher->save_post( $post_id, $post ) );\n\n\t\t$_POST[papi_get_page_type_key( 'switch' )] = papi_get_standard_page_type_id( $post->post_type );\n\n\t\t// Bad capabilities.\n\t\t$this->assertFalse( $switcher->save_post( $post_id, $post ) );\n\n\t\t$_POST[papi_get_page_type_key( 'switch' )] = papi_get_standard_page_type_id( $post->post_type );\n\n\t\t// Bad capabilities.\n\t\t$this->assertFalse( $switcher->save_post( $post_id, $post ) );\n\n\t\tupdate_post_meta( $post_id, papi_get_page_type_key(), 'properties-page-type' );\n\t\tupdate_post_meta( $post_id, 'string_test', 'Fredrik' );\n\t\tupdate_post_meta( $post_id, 'hidden_test', 'Fredrik' );\n\n\t\t$this->assertSame( 'Fredrik', papi_get_field( $post_id, 'string_test' ) );\n\n\t\t$user_id = $this->factory->user->create( [ 'role' => 'administrator' ] );\n\t\twp_set_current_user( $user_id );\n\n\t\t// Create new nonce because of new user.\n\t\t$_POST['papi_meta_nonce'] = wp_create_nonce( 'papi_save_data' );\n\t\t$_POST[papi_get_page_type_key( 'switch' )] = papi_get_standard_page_type_id( $post->post_type );\n\n\t\t// Success!\n\t\t$this->assertTrue( $switcher->save_post( $post_id, $post ) );\n\n\t\t$this->assertEmpty( papi_get_field( $post_id, 'string_test' ) );\n\n\t\twp_set_current_user( 0 );\n\t}\n\n\tpublic function test_save_post_revision() {\n\t\t$switcher = new Papi_Admin_Page_Type_Switcher;\n\t\t$post_id  = $this->factory->post->create( ['post_type' => 'revision'] );\n\t\t$post     = get_post( $post_id );\n\t\t$_POST    = [];\n\t\t$user_id  = $this->factory->user->create( [ 'role' => 'administrator' ] );\n\t\twp_set_current_user( $user_id );\n\n\t\t$_POST['post_type'] = 'page';\n\t\t$_POST[papi_get_page_type_key( 'switch' )] = 'simple-page-type';\n\t\t$_POST[papi_get_page_type_key()] = 'properties-page-type';\n\t\t$_POST['papi_meta_nonce'] = wp_create_nonce( 'papi_save_data' );\n\n\t\t$this->assertFalse( $switcher->save_post( $post_id, $post ) );\n\n\t\t$post_id  = $this->factory->post->create( ['post_type' => 'revision', 'post_parent' => $post_id] );\n\t\t$post     = get_post( $post_id );\n\n\t\t$this->assertFalse( $switcher->save_post( $post_id, $post ) );\n\t}\n\n\tpublic function test_save_post_autosave() {\n\t\t$switcher = new Papi_Admin_Page_Type_Switcher;\n\t\t$post_id  = $this->factory->post->create( ['post_type' => 'page'] );\n\t\t$post     = get_post( $post_id );\n\t\t$_POST    = [];\n\t\t$user_id  = $this->factory->user->create( [ 'role' => 'administrator' ] );\n\t\twp_set_current_user( $user_id );\n\n\t\t$_POST['post_type'] = 'page';\n\t\t$_POST[papi_get_page_type_key( 'switch' )] = 'simple-page-type';\n\t\t$_POST[papi_get_page_type_key()] = 'properties-page-type';\n\t\t$_POST['papi_meta_nonce'] = wp_create_nonce( 'papi_save_data' );\n\n\t\t$post_id  = $this->factory->post->create( ['post_type' => 'revision', 'post_parent' => $post_id, 'post_name' => $post_id . '-autosave'] );\n\t\t$post     = get_post( $post_id );\n\n\t\t$this->assertFalse( $switcher->save_post( $post_id, $post ) );\n\t}\n}\n"
  },
  {
    "path": "tests/cases/admin/class-papi-admin-test.php",
    "content": "<?php\n\n/**\n * @group admin\n */\nclass Papi_Admin_Test extends WP_UnitTestCase {\n\n\t/**\n\t * @var Papi_Admin\n\t */\n\tprotected $admin;\n\n\t/**\n\t * @var int\n\t */\n\tprotected $post_id;\n\n\tpublic function setUp() {\n\t\tparent::setUp();\n\t\t$this->admin = new Papi_Admin;\n\t\t$this->post_id = $this->factory->post->create( [\n\t\t\t'post_type' => 'page'\n\t\t] );\n\n\t\tadd_filter( 'papi/settings/directories', function () {\n\t\t\treturn [1,  PAPI_FIXTURE_DIR . '/page-types'];\n\t\t} );\n\t}\n\n\tpublic function tearDown() {\n\t\tparent::tearDown();\n\t\tunset( $this->admin, $this->post_id, $_GET );\n\t}\n\n\tpublic function register_template_paths( $new_templates ) {\n\t\t$cache_key = 'page_templates-' . md5( get_theme_root() . '/' . get_stylesheet() );\n\n\t\t$templates = wp_get_theme()->get_page_templates();\n\t\tif ( empty( $templates ) ) {\n\t\t\t$templates = [];\n\t\t}\n\n\t\twp_cache_delete( $cache_key , 'themes' );\n\t\t$templates = array_merge( $templates, $new_templates );\n\t\twp_cache_add( $cache_key, $templates, 'themes', 1800 );\n\n\t\treturn $new_templates;\n\t}\n\n\tpublic function test_admin_body_class() {\n\t\tpapi()->reset();\n\t\t$_GET['post'] = $this->factory->post->create();\n\t\t$_GET['post_type'] = 'page';\n\t\t$_GET['page'] = 'papi/page/simple-page-type';\n\n\t\t$classes = $this->admin->admin_body_class( '' );\n\t\t$this->assertTrue( (bool) preg_match( '/\\spapi\\-body papi\\-meta\\-type\\-post/', $classes ) );\n\t}\n\n\tpublic function test_admin_body_class_with_entry_type_body_classes() {\n\t\tpapi()->reset();\n\t\t$_GET['post'] = $this->factory->post->create();\n\t\t$_GET['post_type'] = 'page';\n\t\t$_GET['page'] = 'papi/page/simple-page-type';\n\t\t$admin = new Papi_Admin;\n\t\t$classes = $admin->admin_body_class( '' );\n\t\t$this->assertTrue( (bool) preg_match( '/\\ssimple\\-page\\-type/', $classes ) );\n\t}\n\n\tpublic function test_admin_init() {\n\t\t$admin = new Papi_Admin;\n\t\t$this->assertNull( $admin->admin_init() );\n\n\t\t$_GET['post'] = $this->factory->post->create();\n\t\t$_GET['post_type'] = 'page';\n\t\t$_GET['page'] = 'papi/page/simple-page-type';\n\t\t$admin = new Papi_Admin;\n\t\t$admin->admin_init();\n\t}\n\n\tpublic function test_edit_form_after_title() {\n\t\t$this->admin->edit_form_after_title();\n\t\t$this->expectOutputRegex( '/papi\\_meta\\_nonce/' );\n\t}\n\n\tpublic function test_edit_form_after_title_2() {\n\t\t$_GET['entry_type'] = 'test';\n\t\t$this->admin->edit_form_after_title();\n\t\t$this->expectOutputRegex( '/name\\=\\\"\\_papi\\_page\\_type\\\"/' );\n\t}\n\n\tpublic function test_plugin_row_meta() {\n\t\t$output = $this->admin->plugin_row_meta( [], 'fake/fake.php' );\n\t\t$this->assertEmpty( $output );\n\n\t\t$testroot = basename( dirname( PAPI_PLUGIN_DIR ) );\n\t\t$output = $this->admin->plugin_row_meta( [], $testroot . '/papi-loader.php' );\n\t\t$this->assertArrayHasKey( 'docs', $output );\n\t}\n\n\tpublic function test_setup_actions() {\n\t\tglobal $current_screen;\n\n\t    $current_screen = WP_Screen::get( 'admin_init' );\n\n\t\t$admin = new Papi_Admin;\n\n\t\t$this->assertSame( 10, has_action( 'admin_init', [$admin, 'admin_init'] ) );\n\n\t\t$_GET['taxonomy'] = 'post_tag';\n\t\t$admin = new Papi_Admin;\n\n\t\t$this->assertSame( 10, has_action( 'post_tag_add_form', [$admin, 'edit_form_after_title'] ) );\n\t\t$this->assertSame( 10, has_action( 'post_tag_edit_form', [$admin, 'edit_form_after_title'] ) );\n\n\t\t$current_screen = null;\n\t}\n\n\tpublic function test_setup_filters() {\n\t\tglobal $current_screen;\n\n\t    $current_screen = WP_Screen::get( 'admin_init' );\n\t\t$admin = new Papi_Admin;\n\n\t\t$this->assertSame( 10, has_filter( 'admin_body_class', [$admin, 'admin_body_class'] ) );\n\n\t\t$current_screen = null;\n\t}\n\n\tpublic function test_update_front_page() {\n\t\t$admin = new Papi_Admin;\n\n\t\tpapi_set_page_type_id( $this->post_id, 'book-page-type' );\n\n\t\t$admin->update_front_page( $this->post_id, 'page_on_front' );\n\n\t\t$this->assertSame( 'front-page-type', papi_get_page_type_id( $this->post_id ) );\n\t}\n\n\tpublic function test_wp_link_query() {\n\t\t$admin = new Papi_Admin;\n\t\t$post  = [\n\t\t\t'ID'   => $this->post_id,\n\t\t\t'info' => 'Page'\n\t\t];\n\t\t$post2 = [\n\t\t\t'ID'   => $this->post_id,\n\t\t\t'info' => 'Standard Page'\n\t\t];\n\t\t$results = $admin->wp_link_query( [$post] );\n\t\t$this->assertSame( [$post2], $results );\n\n\t\tadd_filter( 'papi/settings/directories', function () {\n\t\t\treturn [1,  PAPI_FIXTURE_DIR . '/page-types'];\n\t\t} );\n\n\t\tupdate_post_meta( $this->post_id, papi_get_page_type_key(), 'simple-page-type' );\n\n\t\t$post3 = [\n\t\t\t'ID'   => $this->post_id,\n\t\t\t'info' => 'Simple page'\n\t\t];\n\t\t$results = $admin->wp_link_query( [$post] );\n\t\t$this->assertSame( [$post3], $results );\n\t}\n\n\tpublic function test_wp_refresh_nonces() {\n\t\t$admin = new Papi_Admin;\n\n\t\t$arr = [];\n\t\t$this->assertEmpty( $admin->wp_refresh_nonces( $arr ) );\n\n\t\t$arr = [\n\t\t\t'wp-refresh-post-nonces' => [\n\t\t\t\t'replace' => []\n\t\t\t]\n\t\t];\n\n\t\t$arr2 = $admin->wp_refresh_nonces( $arr );\n\n\t\t$this->assertArrayHasKey( 'papi_meta_nonce', $arr2['wp-refresh-post-nonces']['replace'] );\n\t}\n}\n"
  },
  {
    "path": "tests/cases/admin/class-papi-admin-view-test.php",
    "content": "<?php\n\n/**\n * @group admin\n */\nclass Papi_Admin_View_Test extends WP_UnitTestCase {\n\n\tpublic function setUp() {\n\t\t$this->view = new Papi_Admin_View();\n\t}\n\n\tpublic function tearDown() {\n\t\tunset( $this->view );\n\t}\n\n\tpublic function test_exists() {\n\t\t$this->assertFalse( $this->view->exists( 'empty' ) );\n\t\t$this->assertTrue( $this->view->exists( 'add-new-page' ) );\n\t}\n\n\tpublic function test_render() {\n\t\t$this->view->render( 'add-new-page' );\n\t\t$this->expectOutputRegex( '/.*\\S.*/' );\n\t}\n}\n"
  },
  {
    "path": "tests/cases/core/class-papi-conditional-rules-test.php",
    "content": "<?php\n\n/**\n * @group core\n */\nclass Papi_Core_Conditional_Rules_Test extends WP_UnitTestCase {\n\n\tpublic function setUp() {\n\t\tparent::setUp();\n\n\t\tadd_filter( 'papi/settings/directories', function () {\n\t\t\treturn [1,  PAPI_FIXTURE_DIR . '/page-types'];\n\t\t} );\n\n\t\t$this->post_id = $this->factory->post->create();\n\n\t\t$_GET = [];\n\t\t$_GET['post'] = $this->post_id;\n\n\t\tupdate_post_meta( $this->post_id, papi_get_page_type_key(), 'rule-page-type' );\n\t}\n\n\tpublic function tearDown() {\n\t\tparent::tearDown();\n\t\tunset(\n\t\t\t$this->post_id,\n\t\t\t$_GET\n\t\t);\n\t}\n\n\tprivate function save_properties( $property ) {\n\t\t$handler = new Papi_Admin_Meta_Handler();\n\n\t\t$_POST = papi_test_create_property_post_data( [\n\t\t\t'slug'  => $property->slug,\n\t\t\t'type'  => $property,\n\t\t\t'value' => $property->value\n\t\t], $_POST );\n\n\t\t$handler->save_properties( $this->post_id );\n\t}\n\n\tpublic function test_rule_equal_option() {\n\t\tglobal $current_screen;\n\n\t\t$current_screen = WP_Screen::get( 'admin_init' );\n\n\t\t$_SERVER['REQUEST_URI'] = 'http://site.com/?page=papi/options/header-option-type';\n\n\t\t$result = papi_filter_conditional_rule_allowed( [\n\t\t\t'operator' => 'NOT EXISTS',\n\t\t\t'slug'     => 'name'\n\t\t] );\n\n\t\t$this->assertTrue( $result );\n\n\t\t$result = papi_filter_conditional_rule_allowed( [\n\t\t\t'operator' => '=',\n\t\t\t'slug'     => 'name',\n\t\t\t'value'    => ''\n\t\t] );\n\n\t\t$this->assertFalse( $result );\n\n\t\tupdate_option( 'name', 'Fredrik' );\n\n\t\t$result = papi_filter_conditional_rule_allowed( [\n\t\t\t'operator' => '=',\n\t\t\t'slug'     => 'name',\n\t\t\t'value'    => 'Fredrik'\n\t\t] );\n\n\t\t$this->assertTrue( $result );\n\n\t\t$_SERVER['REQUEST_URI'] = '';\n\n\t\t$current_screen = null;\n\t}\n\n\tpublic function test_rule_equal() {\n\t\t$property = papi_property( [\n\t\t\t'title' => 'Name',\n\t\t\t'type'  => 'string',\n\t\t\t'value' => 'Fredrik'\n\t\t] );\n\n\t\t$this->save_properties( $property );\n\n\t\t$result = papi_filter_conditional_rule_allowed( [\n\t\t\t'operator' => '=',\n\t\t\t'slug'     => 'name',\n\t\t\t'value'    => ''\n\t\t] );\n\n\t\t$this->assertFalse( $result );\n\n\t\t$result = papi_filter_conditional_rule_allowed( [\n\t\t\t'operator' => '=',\n\t\t\t'slug'     => 'name',\n\t\t\t'value'    => 'Fredrik'\n\t\t] );\n\n\t\t$this->assertTrue( $result );\n\n\t\t$property = papi_property( [\n\t\t\t'title' => 'Number',\n\t\t\t'type'  => 'number',\n\t\t\t'slug'  => 'number',\n\t\t\t'value' => 1.1\n\t\t] );\n\n\t\t$this->save_properties( $property );\n\n\t\t$result = papi_filter_conditional_rule_allowed( [\n\t\t\t'operator' => '=',\n\t\t\t'slug'     => 'number',\n\t\t\t'value'    => 1.1\n\t\t] );\n\n\t\t$this->assertTrue( $result );\n\t}\n\n\tpublic function test_rule_equal_bool_true() {\n\t\t$property = papi_property( [\n\t\t\t'title' => 'Name',\n\t\t\t'type'  => 'string',\n\t\t\t'value' => 'true'\n\t\t] );\n\n\t\t$this->save_properties( $property );\n\n\t\t$result = papi_filter_conditional_rule_allowed( [\n\t\t\t'operator' => '=',\n\t\t\t'slug'     => 'name',\n\t\t\t'value'    => true\n\t\t] );\n\n\t\t$this->assertTrue( $result );\n\t}\n\n\tpublic function test_rule_equal_bool_false() {\n\t\t$property = papi_property( [\n\t\t\t'title' => 'Name',\n\t\t\t'type'  => 'string',\n\t\t\t'value' => 'false'\n\t\t] );\n\n\t\t$this->save_properties( $property );\n\n\t\t$result = papi_filter_conditional_rule_allowed( [\n\t\t\t'operator' => '=',\n\t\t\t'slug'     => 'name',\n\t\t\t'value'    => false\n\t\t] );\n\n\t\t$this->assertTrue( $result );\n\t}\n\n\tpublic function test_rule_not_equal() {\n\t\t$property = papi_property( [\n\t\t\t'title' => 'Name',\n\t\t\t'type'  => 'string',\n\t\t\t'slug'  => 'name',\n\t\t\t'value' => 'Fredrik'\n\t\t] );\n\n\t\t$this->save_properties( $property );\n\n\t\t$result = papi_filter_conditional_rule_allowed( [\n\t\t\t'operator' => '!=',\n\t\t\t'slug'     => 'name',\n\t\t\t'value'    => 'Fredrik'\n\t\t] );\n\n\t\t$this->assertFalse( $result );\n\n\t\t$result = papi_filter_conditional_rule_allowed( [\n\t\t\t'operator' => '!=',\n\t\t\t'slug'     => 'name',\n\t\t\t'value'    => ''\n\t\t] );\n\n\t\t$this->assertTrue( $result );\n\t}\n\n\tpublic function test_rule_not_equal_bool_true() {\n\t\t$property = papi_property( [\n\t\t\t'title' => 'Name',\n\t\t\t'type'  => 'string',\n\t\t\t'value' => 'false'\n\t\t] );\n\n\t\t$this->save_properties( $property );\n\n\t\t$result = papi_filter_conditional_rule_allowed( [\n\t\t\t'operator' => '!=',\n\t\t\t'slug'     => 'name',\n\t\t\t'value'    => true\n\t\t] );\n\n\t\t$this->assertTrue( $result );\n\t}\n\n\tpublic function test_rule_not_equal_bool_false() {\n\t\t$property = papi_property( [\n\t\t\t'title' => 'Name',\n\t\t\t'type'  => 'string',\n\t\t\t'value' => 'true'\n\t\t] );\n\n\t\t$this->save_properties( $property );\n\n\t\t$result = papi_filter_conditional_rule_allowed( [\n\t\t\t'operator' => '!=',\n\t\t\t'slug'     => 'name',\n\t\t\t'value'    => false\n\t\t] );\n\n\t\t$this->assertTrue( $result );\n\t}\n\n\tpublic function test_rule_greater_then() {\n\t\t$property = papi_property( [\n\t\t\t'title' => 'Number',\n\t\t\t'type'  => 'number',\n\t\t\t'slug'  => 'number',\n\t\t\t'value' => 1\n\t\t] );\n\n\t\t$this->save_properties( $property );\n\n\t\t$result = papi_filter_conditional_rule_allowed( [\n\t\t\t'operator' => '>',\n\t\t\t'slug'     => 'fake',\n\t\t\t'value'    => 1\n\t\t] );\n\n\t\t$this->assertFalse( $result );\n\n\t\t$result = papi_filter_conditional_rule_allowed( [\n\t\t\t'operator' => '>',\n\t\t\t'slug'     => 'number',\n\t\t\t'value'    => 1\n\t\t] );\n\n\t\t$this->assertFalse( $result );\n\n\t\t$result = papi_filter_conditional_rule_allowed( [\n\t\t\t'operator' => '>',\n\t\t\t'slug'     => 'number',\n\t\t\t'value'    => '1.1'\n\t\t] );\n\n\t\t$this->assertFalse( $result );\n\n\t\t$result = papi_filter_conditional_rule_allowed( [\n\t\t\t'operator' => '>',\n\t\t\t'slug'     => 'number',\n\t\t\t'value'    => 0\n\t\t] );\n\n\t\t$this->assertTrue( $result );\n\n\t\t$result = papi_filter_conditional_rule_allowed( [\n\t\t\t'operator' => '>',\n\t\t\t'slug'     => 'number',\n\t\t\t'value'    => '0.9'\n\t\t] );\n\n\t\t$this->assertTrue( $result );\n\n\t\t$result = papi_filter_conditional_rule_allowed( [\n\t\t\t'operator' => '>',\n\t\t\t'slug'     => 'number',\n\t\t\t'source'   => [1, 2],\n\t\t\t'value'    => 1\n\t\t] );\n\n\t\t$this->assertTrue( $result );\n\t}\n\n\tpublic function test_rule_greater_then_or_equal() {\n\t\t$property = papi_property( [\n\t\t\t'title' => 'Number',\n\t\t\t'type'  => 'number',\n\t\t\t'slug'  => 'number',\n\t\t\t'value' => 1\n\t\t] );\n\n\t\t$this->save_properties( $property );\n\n\t\t$result = papi_filter_conditional_rule_allowed( [\n\t\t\t'operator' => '>=',\n\t\t\t'slug'     => 'fake',\n\t\t\t'value'    => 1\n\t\t] );\n\n\t\t$this->assertFalse( $result );\n\n\t\t$result = papi_filter_conditional_rule_allowed( [\n\t\t\t'operator' => '>=',\n\t\t\t'slug'     => 'number',\n\t\t\t'value'    => 1\n\t\t] );\n\n\t\t$this->assertTrue( $result );\n\n\t\t$result = papi_filter_conditional_rule_allowed( [\n\t\t\t'operator' => '>=',\n\t\t\t'slug'     => 'number',\n\t\t\t'value'    => 0\n\t\t] );\n\n\t\t$this->assertTrue( $result );\n\n\t\t$result = papi_filter_conditional_rule_allowed( [\n\t\t\t'operator' => '>=',\n\t\t\t'slug'     => 'number',\n\t\t\t'value'    => '0'\n\t\t] );\n\n\t\t$this->assertTrue( $result );\n\n\t\t$result = papi_filter_conditional_rule_allowed( [\n\t\t\t'operator' => '>=',\n\t\t\t'slug'     => 'number',\n\t\t\t'source'   => [1, 2],\n\t\t\t'value'    => 2\n\t\t] );\n\n\t\t$this->assertTrue( $result );\n\t}\n\n\tpublic function test_rule_less_then() {\n\t\t$property = papi_property( [\n\t\t\t'title' => 'Number',\n\t\t\t'type'  => 'number',\n\t\t\t'slug'  => 'number',\n\t\t\t'value' => 1\n\t\t] );\n\n\t\t$this->save_properties( $property );\n\n\t\t$result = papi_filter_conditional_rule_allowed( [\n\t\t\t'operator' => '<',\n\t\t\t'slug'     => 'fake',\n\t\t\t'value'    => 1\n\t\t] );\n\n\t\t$this->assertFalse( $result );\n\n\t\t$result = papi_filter_conditional_rule_allowed( [\n\t\t\t'operator' => '<',\n\t\t\t'slug'     => 'number',\n\t\t\t'value'    => 1\n\t\t] );\n\n\t\t$this->assertFalse( $result );\n\n\t\t$result = papi_filter_conditional_rule_allowed( [\n\t\t\t'operator' => '<',\n\t\t\t'slug'     => 'number',\n\t\t\t'value'    => 2\n\t\t] );\n\n\t\t$this->assertTrue( $result );\n\n\t\t$result = papi_filter_conditional_rule_allowed( [\n\t\t\t'operator' => '<',\n\t\t\t'slug'     => 'number',\n\t\t\t'source'   => [1, 2],\n\t\t\t'value'    => 3\n\t\t] );\n\n\t\t$this->assertTrue( $result );\n\t}\n\n\tpublic function test_rule_less_then_or_equal() {\n\t\t$property = papi_property( [\n\t\t\t'title' => 'Number',\n\t\t\t'type'  => 'number',\n\t\t\t'slug'  => 'number',\n\t\t\t'value' => 1\n\t\t] );\n\n\t\t$this->save_properties( $property );\n\n\t\t$result = papi_filter_conditional_rule_allowed( [\n\t\t\t'operator' => '<=',\n\t\t\t'slug'     => 'fake',\n\t\t\t'value'    => 1\n\t\t] );\n\n\t\t$this->assertFalse( $result );\n\n\t\t$result = papi_filter_conditional_rule_allowed( [\n\t\t\t'operator' => '<=',\n\t\t\t'slug'     => 'number',\n\t\t\t'value'    => 1\n\t\t] );\n\n\t\t$this->assertTrue( $result );\n\n\t\t$result = papi_filter_conditional_rule_allowed( [\n\t\t\t'operator' => '<=',\n\t\t\t'slug'     => 'number',\n\t\t\t'value'    => 2\n\t\t] );\n\n\t\t$this->assertTrue( $result );\n\n\t\t$result = papi_filter_conditional_rule_allowed( [\n\t\t\t'operator' => '<=',\n\t\t\t'slug'     => 'number',\n\t\t\t'source'   => [1, 2],\n\t\t\t'value'    => 2\n\t\t] );\n\n\t\t$this->assertTrue( $result );\n\t}\n\n\tpublic function test_rule_in() {\n\t\t$property = papi_property( [\n\t\t\t'title' => 'Number',\n\t\t\t'type'  => 'number',\n\t\t\t'slug'  => 'number',\n\t\t\t'value' => 1\n\t\t] );\n\n\t\t$this->save_properties( $property );\n\n\t\t$result = papi_filter_conditional_rule_allowed( [\n\t\t\t'operator' => 'IN',\n\t\t\t'slug'     => 'number',\n\t\t\t'value'    => 10\n\t\t] );\n\n\t\t$this->assertFalse( $result );\n\n\t\t$result = papi_filter_conditional_rule_allowed( [\n\t\t\t'operator' => 'IN',\n\t\t\t'slug'     => 'number',\n\t\t\t'value'    => [10, 20]\n\t\t] );\n\n\t\t$this->assertFalse( $result );\n\n\t\t$result = papi_filter_conditional_rule_allowed( [\n\t\t\t'operator' => 'IN',\n\t\t\t'slug'     => 'number',\n\t\t\t'value'    => [1, 2]\n\t\t] );\n\n\t\t$this->assertTrue( $result );\n\t}\n\n\tpublic function test_rule_not_in() {\n\t\t$property = papi_property( [\n\t\t\t'title' => 'Number',\n\t\t\t'type'  => 'number',\n\t\t\t'slug'  => 'number',\n\t\t\t'value' => 1\n\t\t] );\n\n\t\t$this->save_properties( $property );\n\n\t\t$result = papi_filter_conditional_rule_allowed( [\n\t\t\t'operator' => 'NOT IN',\n\t\t\t'slug'     => 'number',\n\t\t\t'value'    => 10\n\t\t] );\n\n\t\t$this->assertFalse( $result );\n\n\t\t$result = papi_filter_conditional_rule_allowed( [\n\t\t\t'operator' => 'NOT IN',\n\t\t\t'slug'     => 'number',\n\t\t\t'value'    => [1, 2]\n\t\t] );\n\n\t\t$this->assertFalse( $result );\n\n\t\t$result = papi_filter_conditional_rule_allowed( [\n\t\t\t'operator' => 'NOT IN',\n\t\t\t'slug'     => 'number',\n\t\t\t'value'    => [1, null]\n\t\t] );\n\n\t\t$this->assertFalse( $result );\n\n\t\t$result = papi_filter_conditional_rule_allowed( [\n\t\t\t'operator' => 'NOT IN',\n\t\t\t'slug'     => 'number',\n\t\t\t'value'    => [10, 20]\n\t\t] );\n\n\t\t$this->assertTrue( $result );\n\n\t\t$result = papi_filter_conditional_rule_allowed( [\n\t\t\t'operator' => 'NOT IN',\n\t\t\t'slug'     => 'number',\n\t\t\t'value'    => ['10', '20']\n\t\t] );\n\n\t\t$this->assertTrue( $result );\n\t}\n\n\tpublic function test_rule_like() {\n\t\t$property = papi_property( [\n\t\t\t'title' => 'Name',\n\t\t\t'type'  => 'string',\n\t\t\t'slug'  => 'name',\n\t\t\t'value' => 'Fredrik'\n\t\t] );\n\n\t\t$this->save_properties( $property );\n\n\t\t$result = papi_filter_conditional_rule_allowed( [\n\t\t\t'operator' => 'LIKE',\n\t\t\t'slug'     => 'name',\n\t\t\t'value'    => 'Elli'\n\t\t] );\n\n\t\t$this->assertFalse( $result );\n\n\t\t$result = papi_filter_conditional_rule_allowed( [\n\t\t\t'operator' => 'LIKE',\n\t\t\t'slug'     => 'number',\n\t\t\t'value'    => ''\n\t\t] );\n\n\t\t$this->assertFalse( $result );\n\n\t\t$result = papi_filter_conditional_rule_allowed( [\n\t\t\t'operator' => 'LIKE',\n\t\t\t'slug'     => 'name',\n\t\t\t'value'    => 'rik'\n\t\t] );\n\n\t\t$this->assertTrue( $result );\n\n\t\t$property = papi_property( [\n\t\t\t'title' => 'Name',\n\t\t\t'type'  => 'string',\n\t\t\t'slug'  => 'name2',\n\t\t\t'value' => 124\n\t\t] );\n\n\t\t$this->save_properties( $property );\n\n\t\t$result = papi_filter_conditional_rule_allowed( [\n\t\t\t'operator' => 'LIKE',\n\t\t\t'slug'     => 'name2',\n\t\t\t'value'    => 1\n\t\t] );\n\n\t\t$this->assertTrue( $result );\n\t}\n\n\tpublic function test_rule_between() {\n\t\t$property = papi_property( [\n\t\t\t'title' => 'Number',\n\t\t\t'type'  => 'number',\n\t\t\t'slug'  => 'number',\n\t\t\t'value' => 1\n\t\t] );\n\n\t\t$this->save_properties( $property );\n\n\t\t$result = papi_filter_conditional_rule_allowed( [\n\t\t\t'operator' => 'BETWEEN',\n\t\t\t'slug'     => 'number',\n\t\t\t'value'    => ''\n\t\t] );\n\n\t\t$this->assertFalse( $result );\n\n\t\t$result = papi_filter_conditional_rule_allowed( [\n\t\t\t'operator' => 'BETWEEN',\n\t\t\t'slug'     => 'number',\n\t\t\t'value'    => [10, 20]\n\t\t] );\n\n\t\t$this->assertFalse( $result );\n\n\t\t$result = papi_filter_conditional_rule_allowed( [\n\t\t\t'operator' => 'BETWEEN',\n\t\t\t'slug'     => 'number',\n\t\t\t'value'    => ['10', '20']\n\t\t] );\n\n\t\t$this->assertFalse( $result );\n\n\t\t$result = papi_filter_conditional_rule_allowed( [\n\t\t\t'operator' => 'BETWEEN',\n\t\t\t'slug'     => 'number',\n\t\t\t'value'    => [1, null]\n\t\t] );\n\n\t\t$this->assertFalse( $result );\n\n\t\t$result = papi_filter_conditional_rule_allowed( [\n\t\t\t'operator' => 'BETWEEN',\n\t\t\t'slug'     => 'number',\n\t\t\t'value'    => [0, 2]\n\t\t] );\n\n\t\t$this->assertTrue( $result );\n\n\t\t$result = papi_filter_conditional_rule_allowed( [\n\t\t\t'operator' => 'BETWEEN',\n\t\t\t'slug'     => 'number',\n\t\t\t'value'    => ['0', '2']\n\t\t] );\n\n\t\t$this->assertTrue( $result );\n\t}\n\n\tpublic function test_rule_not_between() {\n\t\t$property = papi_property( [\n\t\t\t'title' => 'Number',\n\t\t\t'type'  => 'number',\n\t\t\t'slug'  => 'number',\n\t\t\t'value' => 1\n\t\t] );\n\n\t\t$this->save_properties( $property );\n\n\t\t$result = papi_filter_conditional_rule_allowed( [\n\t\t\t'operator' => 'NOT BETWEEN',\n\t\t\t'slug'     => 'number',\n\t\t\t'value'    => ''\n\t\t] );\n\n\t\t$this->assertFalse( $result );\n\n\t\t$result = papi_filter_conditional_rule_allowed( [\n\t\t\t'operator' => 'NOT BETWEEN',\n\t\t\t'slug'     => 'number',\n\t\t\t'value'    => [1, 2]\n\t\t] );\n\n\t\t$this->assertFalse( $result );\n\n\t\t$result = papi_filter_conditional_rule_allowed( [\n\t\t\t'operator' => 'NOT BETWEEN',\n\t\t\t'slug'     => 'number',\n\t\t\t'value'    => ['0', '2']\n\t\t] );\n\n\t\t$this->assertFalse( $result );\n\n\t\t$result = papi_filter_conditional_rule_allowed( [\n\t\t\t'operator' => 'NOT BETWEEN',\n\t\t\t'slug'     => 'number',\n\t\t\t'value'    => [1, null]\n\t\t] );\n\n\t\t$this->assertFalse( $result );\n\n\t\t$result = papi_filter_conditional_rule_allowed( [\n\t\t\t'operator' => 'NOT BETWEEN',\n\t\t\t'slug'     => 'number',\n\t\t\t'value'    => [0, 2]\n\t\t] );\n\n\t\t$this->assertFalse( $result );\n\n\t\t$result = papi_filter_conditional_rule_allowed( [\n\t\t\t'operator' => 'NOT BETWEEN',\n\t\t\t'slug'     => 'number',\n\t\t\t'value'    => ['10', '20']\n\t\t] );\n\n\t\t$this->assertTrue( $result );\n\n\t\t$result = papi_filter_conditional_rule_allowed( [\n\t\t\t'operator' => 'NOT BETWEEN',\n\t\t\t'slug'     => 'number',\n\t\t\t'value'    => [10, 20]\n\t\t] );\n\n\t\t$this->assertTrue( $result );\n\t}\n\n\tpublic function test_rule_exists() {\n\t\t$property = papi_property( [\n\t\t\t'title' => 'Number',\n\t\t\t'type'  => 'number',\n\t\t\t'slug'  => 'number',\n\t\t\t'value' => 1\n\t\t] );\n\n\t\t$this->save_properties( $property );\n\n\t\t$result = papi_filter_conditional_rule_allowed( [\n\t\t\t'operator' => 'EXISTS',\n\t\t\t'slug'     => 'fake'\n\t\t] );\n\n\t\t$this->assertFalse( $result );\n\n\t\t$result = papi_filter_conditional_rule_allowed( [\n\t\t\t'operator' => 'EXISTS',\n\t\t\t'slug'     => 'number'\n\t\t] );\n\n\t\t$this->assertTrue( $result );\n\t}\n\n\tpublic function test_rule_not_exists() {\n\t\t$property = papi_property( [\n\t\t\t'title' => 'Number',\n\t\t\t'type'  => 'number',\n\t\t\t'slug'  => 'number',\n\t\t\t'value' => 1\n\t\t] );\n\n\t\t$this->save_properties( $property );\n\n\t\t$result = papi_filter_conditional_rule_allowed( [\n\t\t\t'operator' => 'NOT EXISTS',\n\t\t\t'slug'     => 'number'\n\t\t] );\n\n\t\t$this->assertFalse( $result );\n\n\t\t$result = papi_filter_conditional_rule_allowed( [\n\t\t\t'operator' => 'NOT EXISTS',\n\t\t\t'slug'     => 'fake'\n\t\t] );\n\n\t\t$this->assertTrue( $result );\n\t}\n\n\tpublic function test_rule_empty() {\n\t\t$property = papi_property( [\n\t\t\t'title' => 'Number',\n\t\t\t'type'  => 'number',\n\t\t\t'slug'  => 'number',\n\t\t\t'value' => 1\n\t\t] );\n\n\t\t$result = papi_filter_conditional_rule_allowed( [\n\t\t\t'operator' => 'EMPTY',\n\t\t\t'slug'     => 'number'\n\t\t] );\n\n\t\t$this->assertTrue( $result );\n\n\t\t$this->save_properties( $property );\n\n\t\t$result = papi_filter_conditional_rule_allowed( [\n\t\t\t'operator' => 'EMPTY',\n\t\t\t'slug'     => 'number'\n\t\t] );\n\n\t\t$this->assertFalse( $result );\n\t}\n\n\tpublic function test_rule_not_empty() {\n\t\t$property = papi_property( [\n\t\t\t'title' => 'Number',\n\t\t\t'type'  => 'number',\n\t\t\t'slug'  => 'number',\n\t\t\t'value' => 1\n\t\t] );\n\n\t\t$result = papi_filter_conditional_rule_allowed( [\n\t\t\t'operator' => 'NOT EMPTY',\n\t\t\t'slug'     => 'number'\n\t\t] );\n\n\t\t$this->assertFalse( $result );\n\n\t\t$this->save_properties( $property );\n\n\t\t$result = papi_filter_conditional_rule_allowed( [\n\t\t\t'operator' => 'NOT EMPTY',\n\t\t\t'slug'     => 'number'\n\t\t] );\n\n\t\t$this->assertTrue( $result );\n\t}\n\n\tpublic function test_setup_filters() {\n\t\t$rules = new Papi_Core_Conditional_Rules();\n\t\t$this->assertNull( $rules->setup_filters() );\n\t}\n}\n"
  },
  {
    "path": "tests/cases/core/class-papi-core-box-test.php",
    "content": "<?php\n\n/**\n * @group core\n */\nclass Papi_Core_Box_Test extends WP_UnitTestCase {\n\n\tpublic function test_get_option_null() {\n\t\t$box = new Papi_Core_Box;\n\t\t$this->assertNull( $box->get_option( 'empty' ) );\n\t}\n\n\tpublic function test_option_value() {\n\t\t$box = new Papi_Core_Box;\n\t\t$box->set_option( 'name', 'Fredrik' );\n\t\t$this->assertSame( 'Fredrik', $box->get_option( 'name' ) );\n\t}\n\n\tpublic function test_options() {\n\t\t$box = new Papi_Core_Box( [\n\t\t\t'title' => 'Box'\n\t\t] );\n\t\t$this->assertSame( 'Box', $box->title );\n\t\t$this->assertSame( '_papi_box', $box->id );\n\t}\n\n\tpublic function test_excluded_options() {\n\t\t$box = new Papi_Core_Box( [\n\t\t\t'title'      => 'Box',\n\t\t\t'options'    => null,\n\t\t\t'properties' => null,\n\t\t] );\n\t\t$this->assertSame( 'Box', $box->title );\n\t\t$this->assertSame( '_papi_box', $box->id );\n\t\t$this->assertSame( [], $box->properties );\n\t}\n}\n"
  },
  {
    "path": "tests/cases/core/class-papi-core-conditional-rule-test.php",
    "content": "<?php\n\n/**\n * @group core\n */\nclass Papi_Core_Conditional_Rule_Test extends WP_UnitTestCase {\n\n\tpublic function setUp() {\n\t\tparent::setUp();\n\t\t$this->rule = new Papi_Core_Conditional_Rule( [\n\t\t\t'operator' => '=',\n\t\t\t'slug'     => 'name',\n\t\t\t'source'   => 'Elli',\n\t\t\t'value'    => 'Fredrik'\n\t\t] );\n\t}\n\n\tpublic function tearDown() {\n\t\tparent::tearDown();\n\t\tunset( $this->rule );\n\t}\n\n\tpublic function test_operator() {\n\t\t$this->assertSame( '=', $this->rule->operator );\n\t}\n\n\tpublic function test_slug() {\n\t\t$this->assertSame( 'papi_name', $this->rule->slug );\n\t}\n\n\tpublic function test_source() {\n\t\t$this->assertSame( 'Elli', $this->rule->get_source() );\n\t}\n\n\tpublic function test_source_callable_failied() {\n\t\t$rule = new Papi_Core_Conditional_Rule( [\n\t\t\t'operator' => '=',\n\t\t\t'slug'     => 'numbers',\n\t\t\t'source'   => [new stdClass, 'fake']\n\t\t] );\n\t\t$this->assertEmpty( $rule->get_source() );\n\n\t\t$rule = new Papi_Core_Conditional_Rule( [\n\t\t\t'operator' => '=',\n\t\t\t'slug'     => 'numbers',\n\t\t\t'source'   => [$this, 'fake']\n\t\t] );\n\t\t$this->assertEmpty( $rule->get_source() );\n\n\t\t$rule = new Papi_Core_Conditional_Rule( [\n\t\t\t'operator' => '=',\n\t\t\t'slug'     => 'numbers',\n\t\t\t'source'   => '#source_callable'\n\t\t] );\n\t\t$this->assertSame( '#source_callable', $rule->get_source() );\n\t}\n\n\tpublic function test_source_callable() {\n\t\t$rule = new Papi_Core_Conditional_Rule( [\n\t\t\t'operator' => '=',\n\t\t\t'slug'     => 'numbers',\n\t\t\t'source'   => [$this, 'source_callable']\n\t\t] );\n\t\t$this->assertSame( [1, 2], $rule->get_source() );\n\n\t\t$rule = new Papi_Core_Conditional_Rule( [\n\t\t\t'operator' => '=',\n\t\t\t'slug'     => 'name',\n\t\t\t'source'   => [$this, 'source_callable']\n\t\t] );\n\t\t$this->assertSame( 'Fredrik', $rule->get_source() );\n\n\t\t$rule = new Papi_Core_Conditional_Rule( [\n\t\t\t'operator' => '=',\n\t\t\t'slug'     => 'numbers',\n\t\t\t'source'   => 'source_callable'\n\t\t] );\n\t\t$this->assertSame( [1, 2], $rule->get_source() );\n\n\t\t$rule = new Papi_Core_Conditional_Rule( [\n\t\t\t'operator' => '=',\n\t\t\t'slug'     => 'name',\n\t\t\t'source'   => 'source_callable'\n\t\t] );\n\t\t$this->assertSame( 'Fredrik', $rule->get_source() );\n\t}\n\n\tpublic function test_source_closure() {\n\t\t$rule = new Papi_Core_Conditional_Rule( [\n\t\t\t'operator' => '=',\n\t\t\t'slug'     => 'numbers',\n\t\t\t'source'   => function ( $slug ) {\n\t\t\t\tif ( $slug === 'papi_numbers' ) {\n\t\t\t\t\treturn [1, 2];\n\t\t\t\t} else {\n\t\t\t\t\treturn 'Fredrik';\n\t\t\t\t}\n\t\t\t}\n\t\t] );\n\t\t$this->assertEmpty( $rule->get_source() );\n\t}\n\n\tpublic function source_callable( $slug ) {\n\t\tif ( $slug === 'papi_numbers' ) {\n\t\t\treturn [1, 2];\n\t\t} else {\n\t\t\treturn 'Fredrik';\n\t\t}\n\t}\n\n\tpublic function test_value() {\n\t\t$this->assertSame( 'Fredrik', $this->rule->value );\n\t}\n}\n\nfunction source_callable( $slug ) {\n\tif ( $slug === 'papi_numbers' ) {\n\t\treturn [1, 2];\n\t} else {\n\t\treturn 'Fredrik';\n\t}\n}\n"
  },
  {
    "path": "tests/cases/core/class-papi-core-conditional-test.php",
    "content": "<?php\n\n/**\n * @group core\n */\nclass Papi_Core_Conditional_Test extends WP_UnitTestCase {\n\n\tpublic function setUp() {\n\t\tparent::setUp();\n\t\t$_GET = [];\n\t\t$this->conditional = new Papi_Core_Conditional();\n\t}\n\n\tpublic function tearDown() {\n\t\tparent::tearDown();\n\t\tunset( $this->conditional, $_GET );\n\t}\n\n\tpublic function test_failed_display() {\n\t\t$result = $this->conditional->display( [\n\t\t\t[\n\t\t\t\t'operator' => '=',\n\t\t\t\t'slug'     => 'fake',\n\t\t\t\t'value'    => ''\n\t\t\t]\n\t\t] );\n\n\t\t$this->assertFalse( $result );\n\n\t\t$result = $this->conditional->display( [\n\t\t\t'relation' => 'AND',\n\t\t\t[\n\t\t\t\t'operator' => '=',\n\t\t\t\t'slug'     => 'fake',\n\t\t\t\t'value'    => ''\n\t\t\t],\n\t\t] );\n\n\t\t$this->assertFalse( $result );\n\n\t\t$result = $this->conditional->display( [\n\t\t\t[\n\t\t\t\t'operator' => '=',\n\t\t\t\t'slug'     => 'fake',\n\t\t\t\t'value'    => ''\n\t\t\t],\n\t\t\tfalse,\n\t\t\tnull\n\t\t] );\n\n\t\t$this->assertFalse( $result );\n\n\t\t$result = $this->conditional->display( [\n\t\t\t'relation' => 'AND',\n\t\t\t[\n\t\t\t\t'operator' => '=',\n\t\t\t\t'slug'     => 'fake',\n\t\t\t\t'value'    => ''\n\t\t\t],\n\t\t\tfalse,\n\t\t\tnull\n\t\t] );\n\n\t\t$this->assertFalse( $result );\n\t}\n\n\tpublic function test_success_display() {\n\t\t$result = $this->conditional->display( [] );\n\t\t$this->assertTrue( $result );\n\n\t\t$result = $this->conditional->display( [null] );\n\t\t$this->assertTrue( $result );\n\n\t\t$result = $this->conditional->display( [true] );\n\t\t$this->assertTrue( $result );\n\n\t\t$result = $this->conditional->display( [false] );\n\t\t$this->assertTrue( $result );\n\n\t\t$result = $this->conditional->display( ['hello'] );\n\t\t$this->assertTrue( $result );\n\n\t\t$result = $this->conditional->display( [ (object) []] );\n\t\t$this->assertTrue( $result );\n\n\t\t$result = $this->conditional->display( [\n\t\t\t'relation' => 'nil',\n\t\t\t[\n\t\t\t\t(object) []\n\t\t\t]\n\t\t] );\n\t\t$this->assertTrue( $result );\n\t}\n\n\tpublic function test_custom_display() {\n\t\tadd_filter( 'papi/conditional/rule/==', function ( $rule ) {\n\t\t\treturn $rule->value === 'hello';\n\t\t} );\n\n\t\t$result = $this->conditional->display( [\n\t\t\t[\n\t\t\t\t'operator' => '==',\n\t\t\t\t'slug'     => 'fake',\n\t\t\t\t'value'    => ''\n\t\t\t]\n\t\t] );\n\t\t$this->assertFalse( $result );\n\n\t\t$result = $this->conditional->display( [\n\t\t\t[\n\t\t\t\t'operator' => '==',\n\t\t\t\t'slug'     => 'fake',\n\t\t\t\t'value'    => 'hello'\n\t\t\t]\n\t\t] );\n\t\t$this->assertTrue( $result );\n\n\t\tadd_filter( 'papi/conditional/rule/===', function ( $rule ) {\n\t\t\treturn null;\n\t\t} );\n\n\t\t$result = $this->conditional->display( [\n\t\t\t[\n\t\t\t\t'operator' => '===',\n\t\t\t\t'slug'     => 'fake',\n\t\t\t\t'value'    => ''\n\t\t\t]\n\t\t] );\n\t\t$this->assertFalse( $result );\n\t}\n\n\tpublic function test_display_with_simple_slug() {\n\t\tadd_filter( 'papi/settings/directories', function () {\n\t\t\treturn [1,  PAPI_FIXTURE_DIR . '/page-types'];\n\t\t} );\n\n\t\t$post_id = $this->factory->post->create();\n\t\t$_GET['post'] = $post_id;\n\n\t\tupdate_post_meta( $post_id, papi_get_page_type_key(), 'simple-page-type' );\n\n\t\t$result = $this->conditional->display( [\n\t\t\t[\n\t\t\t\t'operator' => 'NOT EXISTS',\n\t\t\t\t'slug'     => 'name',\n\t\t\t\t'value'    => ''\n\t\t\t]\n\t\t] );\n\n\t\t$this->assertTrue( $result );\n\n\t\t$property = papi_property( [\n\t\t\t'title' => 'Name',\n\t\t\t'type'  => 'string',\n\t\t\t'value' => 'Fredrik'\n\t\t] );\n\n\t\t$handler = new Papi_Admin_Meta_Handler();\n\n\t\t$_POST = papi_test_create_property_post_data( [\n\t\t\t'slug'  => $property->slug,\n\t\t\t'type'  => $property,\n\t\t\t'value' => $property->value\n\t\t], $_POST );\n\n\t\t$handler->save_properties( $post_id );\n\n\t\t$result = $this->conditional->display( [\n\t\t\t[\n\t\t\t\t'operator' => 'EXISTS',\n\t\t\t\t'slug'     => 'name',\n\t\t\t\t'value'    => ''\n\t\t\t]\n\t\t] );\n\n\t\t$this->assertTrue( $result );\n\n\t\t$result = $this->conditional->display( [\n\t\t\t[\n\t\t\t\t'operator' => '=',\n\t\t\t\t'slug'     => 'name',\n\t\t\t\t'value'    => 'Fredrik'\n\t\t\t]\n\t\t] );\n\n\t\t$this->assertTrue( $result );\n\t}\n\n\tpublic function test_display_with_array_slug() {\n\t\tadd_filter( 'papi/settings/directories', function () {\n\t\t\treturn [1,  PAPI_FIXTURE_DIR . '/page-types'];\n\t\t} );\n\n\t\t$post_id = $this->factory->post->create();\n\t\t$_GET['post'] = $post_id;\n\n\t\tupdate_post_meta( $post_id, papi_get_page_type_key(), 'simple-page-type' );\n\n\t\t$simple_page_type  = papi_get_entry_type_by_id( 'simple-page-type' );\n\t\t$sections_prop     = $simple_page_type->get_property( 'sections' );\n\t\t$title_prop        = $simple_page_type->get_property( 'sections[0][title]' );\n\t\t$title_prop2       = clone $title_prop->get_options();\n\t\t$title_prop3       = clone $title_prop->get_options();\n\t\t$title_prop2       = Papi_Property::factory( $title_prop2 );\n\t\t$title_prop2->slug = $sections_prop->html_name( $title_prop, 0 );\n\t\t$title_prop3       = Papi_Property::factory( $title_prop3 );\n\t\t$title_prop3->slug = $sections_prop->html_name( $title_prop, 0 );\n\t\t$title_prop3->slug = str_replace( 'title', 'name', $title_prop3->slug );\n\n\t\t$result = $this->conditional->display( [\n\t\t\t[\n\t\t\t\t'operator' => 'NOT EXISTS',\n\t\t\t\t'slug'     => 'title',\n\t\t\t\t'value'    => ''\n\t\t\t]\n\t\t], $title_prop );\n\n\t\t$this->assertTrue( $result );\n\n\t\t$result = $this->conditional->display( [\n\t\t\t[\n\t\t\t\t'operator' => 'NOT EXISTS',\n\t\t\t\t'slug'     => 'title',\n\t\t\t\t'value'    => ''\n\t\t\t]\n\t\t], $title_prop2 );\n\n\t\t$this->assertTrue( $result );\n\n\t\t$result = $this->conditional->display( [\n\t\t\t[\n\t\t\t\t'operator' => 'NOT EXISTS',\n\t\t\t\t'slug'     => 'sections.0.title',\n\t\t\t\t'value'    => ''\n\t\t\t]\n\t\t] );\n\n\t\t$this->assertTrue( $result );\n\n\t\t$result = $this->conditional->display( [\n\t\t\t[\n\t\t\t\t'operator' => 'NOT EXISTS',\n\t\t\t\t'slug'     => 'name',\n\t\t\t\t'value'    => ''\n\t\t\t]\n\t\t], $title_prop3 );\n\n\t\t$this->assertTrue( $result );\n\n\t\t$value_slug1         = $title_prop->get_slug( true );\n\t\t$value_type_slug1    = papi_get_property_type_key( $value_slug1 );\n\n\t\t$item = [];\n\t\t$item[$value_slug1] = 'Hello, world!';\n\t\t$item[$value_type_slug1] = $title_prop;\n\n\t\t$handler = new Papi_Admin_Meta_Handler();\n\n\t\t$_POST = papi_test_create_property_post_data( [\n\t\t\t'slug'  => $sections_prop->slug,\n\t\t\t'type'  => $sections_prop,\n\t\t\t'value' => [$item]\n\t\t], $_POST );\n\n\t\t$handler->save_properties( $post_id );\n\n\t\t$result = $this->conditional->display( [\n\t\t\t[\n\t\t\t\t'operator' => 'EXISTS',\n\t\t\t\t'slug'     => 'title',\n\t\t\t\t'value'    => ''\n\t\t\t]\n\t\t], $title_prop2 );\n\n\t\t$this->assertTrue( $result );\n\n\t\t$result = $this->conditional->display( [\n\t\t\t[\n\t\t\t\t'operator' => 'EXISTS',\n\t\t\t\t'slug'     => 'sections.0.title',\n\t\t\t\t'value'    => ''\n\t\t\t]\n\t\t] );\n\n\t\t$this->assertTrue( $result );\n\n\t\t$result = $this->conditional->display( [\n\t\t\t[\n\t\t\t\t'operator' => '=',\n\t\t\t\t'slug'     => 'sections.0.title',\n\t\t\t\t'value'    => 'Hello, world!'\n\t\t\t]\n\t\t] );\n\n\t\t$this->assertTrue( $result );\n\t}\n}\n"
  },
  {
    "path": "tests/cases/core/class-papi-core-container-test.php",
    "content": "<?php\n\nclass Papi_Core_Container_Test extends WP_UnitTestCase {\n\n\tpublic function setUp() {\n\t\tparent::setUp();\n\t\t$this->container = new Papi_Core_Container;\n\t}\n\n\tpublic function tearDown() {\n\t\tparent::tearDown();\n\t\tunset( $this->container );\n\t}\n\n\tpublic function test_make_not_defined() {\n\t\t$this->setExpectedException( 'Exception', 'Identifier `fredrik` is not defined' );\n\t\t$this->container->make( 'fredrik' );\n\t}\n\n\tpublic function test_bind() {\n\t\t$this->container->bind( 'name', 'Fredrik' );\n\t\t$this->assertSame( 'Fredrik', $this->container->make( 'name' ) );\n\t}\n\n\tpublic function test_closure() {\n\t\t$this->container->bind( 'num', 123 );\n\t\t$this->container->bind( 'num2', function ( $c ) {\n\t\t\treturn $c->make( 'num' );\n\t\t} );\n\t\t$this->container->bind( 'num3', function ( $c ) {\n\t\t\treturn $c->make( 'num2' );\n\t\t} );\n\t\t$this->assertSame( 123, $this->container->make( 'num2' ) );\n\t\t$this->assertSame( 123, $this->container->make( 'num3' ) );\n\t}\n\n\tpublic function test_closure_injection() {\n\t\t$this->container->bind( 'num', 123 );\n\t\t$this->container->bind( 'num2', function ( Papi_Core_Container $c ) {\n\t\t\treturn $c->make( 'num' );\n\t\t} );\n\t\t$this->container->bind( 'num3', function ( Papi_Core_Container $c, $num ) {\n\t\t\treturn $c->make( 'num2' ) + $num;\n\t\t} );\n\t\t$this->assertSame( 123, $this->container->make( 'num2' ) );\n\t\t$this->assertSame( 124, $this->container->make( 'num3', [1] ) );\n\n\t\trequire_once PAPI_FIXTURE_DIR . '/container/class-container-test-stub.php';\n\n\t\t$this->container->bind( new \\Papi\\Tests\\Fixtures\\Container\\Container_Test_Stub );\n\t\t$this->container->bind( 'test-class', function ( \\Papi\\Tests\\Fixtures\\Container\\Container_Test_Stub $test ) {\n\t\t\treturn $test->value();\n\t\t} );\n\t\t$this->assertSame( 'Test class', $this->container->make( 'test-class' ) );\n\t}\n\n\tpublic function test_exists() {\n\t\t$this->container->bind( 'name', 'Fredrik' );\n\t\t$this->assertTrue( $this->container->exists( 'name' ) );\n\t}\n\n\tpublic function test_once() {\n\t\t$this->assertNull( $this->container->once( null, null ) );\n\n\t\t$this->container->once( 'Once', function () {\n\t\t\treturn 'App';\n\t\t} );\n\t\t$this->assertSame( 'App', $this->container->make( 'Once' ) );\n\n\t\ttry {\n\t\t\t$this->container->once( 'Once', function () {\n\t\t\t\treturn 'App';\n\t\t\t} );\n\t\t} catch ( \\Exception $e ) {\n\t\t\t$this->assertNotEmpty( $e->getMessage() );\n\t\t}\n\t}\n\n\tpublic function test_remove() {\n\t\t$this->container['plugin'] = 'Papi';\n\t\t$this->container->remove( 'plugin' );\n\t\t$this->assertFalse( isset( $this->container['plugin'] ) );\n\t}\n\n\tpublic function test_reset() {\n\t\t$this->container['plugin'] = 'Papi';\n\t\t$this->container['wp'] = true;\n\t\t$this->container->reset();\n\t\t$this->assertFalse( isset( $this->container['plugin'] ) );\n\t\t$this->assertFalse( isset( $this->container['wp'] ) );\n\t}\n\n\tpublic function test_singleton() {\n\t\t$this->container->singleton( 'Singleton', 'App' );\n\t\t$this->assertSame( 'App', $this->container->make( 'Singleton' ) );\n\n\t\ttry {\n\t\t\t$this->container->bind( 'Singleton', 'App' );\n\t\t} catch ( \\Exception $e ) {\n\t\t\t$this->assertNotEmpty( $e->getMessage() );\n\t\t}\n\n\t\ttry {\n\t\t\t$this->container->singleton( 'Singleton', 'App' );\n\t\t} catch ( \\Exception $e ) {\n\t\t\t$this->assertNotEmpty( $e->getMessage() );\n\t\t}\n\n\t\t$this->assertSame( 'App', $this->container->make( 'Singleton' ) );\n\t\t$this->assertTrue( $this->container->is_singleton( 'Singleton' ) );\n\n\t\ttry {\n\t\t\t$this->container->is_singleton( true );\n\t\t} catch ( \\Exception $e ) {\n\t\t\t$this->assertSame( 'Invalid argument. Must be string.', $e->getMessage() );\n\t\t}\n\t}\n\n\tpublic function test_offset_exists() {\n\t\t$this->container->bind( 'name', 'Fredrik' );\n\t\t$this->assertTrue( isset( $this->container['name'] ) );\n\t}\n\n\tpublic function test_offset_get() {\n\t\t$this->container->bind( 'name', 'Fredrik' );\n\t\t$this->assertSame( 'Fredrik', $this->container['name'] );\n\t}\n\n\tpublic function test_offset_set() {\n\t\t$this->container['plugin'] = 'Papi';\n\t\t$this->assertSame( 'Papi', $this->container['plugin'] );\n\t}\n\n\tpublic function test_offset_unset() {\n\t\t$this->container['plugin'] = 'Papi';\n\t\tunset( $this->container['plugin'] );\n\t\t$this->assertFalse( isset( $this->container['plugin'] ) );\n\t}\n}\n"
  },
  {
    "path": "tests/cases/core/class-papi-core-data-test.php",
    "content": "<?php\n\n/**\n * @group core\n */\nclass Papi_Core_Data_Test extends WP_UnitTestCase {\n\n\tpublic function setUp() {\n\t\tparent::setUp();\n\n\t\t$this->post_id = $this->factory->post->create();\n\t\t$this->term_id = $this->factory->term->create();\n\t}\n\n\tpublic function tearDown() {\n\t\tparent::tearDown();\n\n\t\tunset( $this->post_id, $this->term_id );\n\t}\n\n\tpublic function test_delete() {\n\t\t$data = new Papi_Core_Data();\n\n\t\t$this->assertFalse( $data->delete( $this->post_id, 'random223-page-type' ) );\n\n\t\tupdate_post_meta( $this->post_id, 'random223-page-type', 'post' );\n\t\t$this->assertTrue( $data->delete( $this->post_id, 'random223-page-type' ) );\n\n\t\tupdate_post_meta( $this->post_id, 'random223-page-type', 'post' );\n\t\t$this->assertTrue( $data->delete( $this->post_id, 'papi_random223-page-type' ) );\n\t}\n\n\tpublic function test_delete_option() {\n\t\t$data = new Papi_Core_Data( 'option' );\n\n\t\t$this->assertFalse( $data->delete( $this->post_id, 'random223-page-type' ) );\n\n\t\tupdate_option( 'random223-page-type', 'option' );\n\t\t$this->assertTrue( $data->delete( $this->post_id, 'random223-page-type' ) );\n\n\t\tupdate_option( 'random223-page-type', 'option' );\n\t\t$this->assertTrue( $data->delete( $this->post_id, 'papi_random223-page-type' ) );\n\t}\n\n\tpublic function test_get() {\n\t\t$data = new Papi_Core_Data();\n\n\t\t$this->assertEmpty( $data->get( $this->post_id, 'name' ) );\n\n\t\tupdate_post_meta( $this->post_id, 'name', 'Fredrik' );\n\t\t$this->assertSame( 'Fredrik', $data->get( $this->post_id, 'name' ) );\n\t}\n\n\tpublic function test_get_option() {\n\t\t$data = new Papi_Core_Data( 'option' );\n\n\t\t$this->assertEmpty( $data->get( $this->post_id, 'name' ) );\n\n\t\tupdate_option( 'name', 'Fredrik' );\n\t\t$this->assertSame( 'Fredrik', $data->get( $this->post_id, 'name' ) );\n\t}\n\n\tpublic function test_get_term() {\n\t\t$data = new Papi_Core_Data( 'term' );\n\n\t\t$this->assertEmpty( $data->get( $this->term_id, 'name' ) );\n\n\t\tupdate_term_meta( $this->term_id, 'name', 'Fredrik' );\n\t\t$this->assertSame( 'Fredrik', $data->get( $this->term_id, 'name' ) );\n\t}\n\n\tpublic function test_update() {\n\t\tforeach ( ['post', 'option', 'term'] as $type ) {\n\t\t\t$id = $type === 'term' ? $this->term_id : $this->post_id;\n\n\t\t\t$data = new Papi_Core_Data( $type );\n\n\t\t\t$this->assertTrue( $data->update( $id, 'name', 'Fredrik' ) );\n\t\t\t$this->assertFalse( $data->update( $id, 'name', 'Fredrik' ) );\n\t\t\t$this->assertSame( 'Fredrik', $data->get( $id, 'name' ) );\n\n\t\t\t$this->assertTrue( $data->update( $id, 'name', '' ) );\n\t\t\t$this->assertEmpty( $data->get( $id, 'name' ) );\n\n\t\t\t$this->assertTrue( $data->update( $id, 'what', [\n\t\t\t\t'firstname' => 'Fredrik'\n\t\t\t] ) );\n\t\t\t$this->assertSame( 'Fredrik', $data->get( $id, 'firstname' ) );\n\n\t\t\t$this->assertTrue( $data->update( $id, 'what', [\n\t\t\t\t'Fredrik'\n\t\t\t] ) );\n\t\t\t$this->assertSame( ['Fredrik'], $data->get( $id, 'what' ) );\n\n\t\t\t$this->assertTrue( $data->update( $id, 'what', '{}' ) );\n\t\t\t$this->assertEmpty( $data->get( $id, 'what' ) );\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "tests/cases/core/class-papi-core-property-test.php",
    "content": "<?php\n\n/**\n * @group core\n */\nclass Papi_Core_Property_Test extends WP_UnitTestCase {\n\n\tpublic function setUp() {\n\t\tparent::setUp();\n\n\t\tadd_filter( 'papi/settings/directories', function () {\n\t\t\treturn [1,  PAPI_FIXTURE_DIR . '/page-types'];\n\t\t} );\n\n\t\t$this->post_id = $this->factory->post->create();\n\t\t$_GET['post'] = $this->post_id;\n\t}\n\n\tpublic function tearDown() {\n\t\tparent::tearDown();\n\t\tunset( $_GET, $this->post_id );\n\t}\n\n\tpublic function test_converter() {\n\t\tupdate_post_meta( $this->post_id, papi_get_page_type_key(), 'properties-page-type' );\n\n\t\t$page_type = papi_get_entry_type_by_id( 'properties-page-type' );\n\t\t$flexible  = $page_type->get_property( 'flexible_test_other' );\n\n\t\t$this->assertSame( 'flexible', $flexible->type );\n\t\t$this->assertSame( 'flexible', $flexible->get_option( 'type' ) );\n\t\t$this->assertSame( 'Flexible test', $flexible->title );\n\t\t$this->assertSame( 'Flexible test', $flexible->get_option( 'title' ) );\n\t\t$this->assertSame( 'papi_flexible_test_other', $flexible->slug );\n\t\t$this->assertSame( 'papi_flexible_test_other', $flexible->get_option( 'slug' ) );\n\t\t$this->assertSame( 'papi_flexible_test_other', $flexible->get_slug() );\n\t\t$this->assertSame( 'flexible_test_other', $flexible->get_slug( true ) );\n\n\t\t$flexible_children = $flexible->get_child_properties();\n\n\t\t$this->assertSame( 'Twitter', $flexible_children['twitter']['title'] );\n\t\t$this->assertSame( 'string', $flexible_children['twitter']['items'][0]->type );\n\t\t$this->assertSame( 'string', $flexible_children['twitter']['items'][0]->get_option( 'type' ) );\n\t\t$this->assertSame( 'papi_twitter_name', $flexible_children['twitter']['items'][0]->slug );\n\t\t$this->assertSame( 'papi_twitter_name', $flexible_children['twitter']['items'][0]->get_option( 'slug' ) );\n\t\t$this->assertSame( 'papi_twitter_name', $flexible_children['twitter']['items'][0]->get_slug() );\n\t\t$this->assertSame( 'twitter_name', $flexible_children['twitter']['items'][0]->get_slug( true ) );\n\t\t$this->assertSame( 'Twitter name', $flexible_children['twitter']['items'][0]->title );\n\t\t$this->assertSame( 'Twitter name', $flexible_children['twitter']['items'][0]->get_option( 'title' ) );\n\t\t$this->assertSame( 'Posts', $flexible_children['posts']['title'] );\n\n\t\t$this->assertSame( 'Posts', $flexible_children['posts']['title'] );\n\t\t$this->assertSame( 'post', $flexible_children['posts']['items'][0]->type );\n\t\t$this->assertSame( 'post', $flexible_children['posts']['items'][0]->get_option( 'type' ) );\n\t\t$this->assertSame( 'papi_post_one', $flexible_children['posts']['items'][0]->slug );\n\t\t$this->assertSame( 'papi_post_one', $flexible_children['posts']['items'][0]->get_option( 'slug' ) );\n\t\t$this->assertSame( 'papi_post_one', $flexible_children['posts']['items'][0]->get_slug() );\n\t\t$this->assertSame( 'post_one', $flexible_children['posts']['items'][0]->get_slug( true ) );\n\t\t$this->assertSame( 'Post one', $flexible_children['posts']['items'][0]->title );\n\t\t$this->assertSame( 'Post one', $flexible_children['posts']['items'][0]->get_option( 'title' ) );\n\t\t$this->assertSame( 'post', $flexible_children['posts']['items'][1]->type );\n\t\t$this->assertSame( 'post', $flexible_children['posts']['items'][1]->get_option( 'type' ) );\n\t\t$this->assertSame( 'papi_post_two', $flexible_children['posts']['items'][1]->slug );\n\t\t$this->assertSame( 'papi_post_two', $flexible_children['posts']['items'][1]->get_option( 'slug' ) );\n\t\t$this->assertSame( 'papi_post_two', $flexible_children['posts']['items'][1]->get_slug() );\n\t\t$this->assertSame( 'post_two', $flexible_children['posts']['items'][1]->get_slug( true ) );\n\t\t$this->assertSame( 'Post two', $flexible_children['posts']['items'][1]->title );\n\t\t$this->assertSame( 'Post two', $flexible_children['posts']['items'][1]->get_option( 'title' ) );\n\n\t\t$this->assertSame( 'List', $flexible_children['list']['title'] );\n\t\t$repeater = $flexible_children['list']['items'][0];\n\n\t\t$this->assertSame( 'repeater', $repeater->type );\n\t\t$this->assertSame( 'repeater', $repeater->get_option( 'type' ) );\n\t\t$this->assertSame( 'Repeater test', $repeater->title );\n\t\t$this->assertSame( 'Repeater test', $repeater->get_option( 'title' ) );\n\t\t$this->assertSame( 'papi_repeater_test_other', $repeater->slug );\n\t\t$this->assertSame( 'papi_repeater_test_other', $repeater->get_option( 'slug' ) );\n\t\t$this->assertSame( 'papi_repeater_test_other', $repeater->get_slug() );\n\t\t$this->assertSame( 'repeater_test_other', $repeater->get_slug( true ) );\n\n\t\t$repeater_children = $repeater->get_child_properties();\n\n\t\t$this->assertSame( 'string', $repeater_children[0]->type );\n\t\t$this->assertSame( 'string', $repeater_children[0]->get_option( 'type' ) );\n\t\t$this->assertSame( 'papi_book_name', $repeater_children[0]->slug );\n\t\t$this->assertSame( 'papi_book_name', $repeater_children[0]->get_option( 'slug' ) );\n\t\t$this->assertSame( 'papi_book_name', $repeater_children[0]->get_slug() );\n\t\t$this->assertSame( 'book_name', $repeater_children[0]->get_slug( true ) );\n\t\t$this->assertSame( 'Book name', $repeater_children[0]->title );\n\t\t$this->assertSame( 'Book name', $repeater_children[0]->get_option( 'title' ) );\n\n\t\t$this->assertSame( 'bool', $repeater_children[1]->type );\n\t\t$this->assertSame( 'bool', $repeater_children[1]->get_option( 'type' ) );\n\t\t$this->assertSame( 'papi_is_open', $repeater_children[1]->slug );\n\t\t$this->assertSame( 'papi_is_open', $repeater_children[1]->get_option( 'slug' ) );\n\t\t$this->assertSame( 'papi_is_open', $repeater_children[1]->get_slug() );\n\t\t$this->assertSame( 'is_open', $repeater_children[1]->get_slug( true ) );\n\t\t$this->assertSame( 'Is open?', $repeater_children[1]->title );\n\t\t$this->assertSame( 'Is open?', $repeater_children[1]->get_option( 'title' ) );\n\n\t\t$this->assertSame( 'List 2', $flexible_children['list2']['title'] );\n\t\t$repeater2 = $flexible_children['list2']['items'][0];\n\n\t\t$this->assertSame( 'repeater', $repeater2->type );\n\t\t$this->assertSame( 'repeater', $repeater2->get_option( 'type' ) );\n\t\t$this->assertSame( 'Repeater test 2', $repeater2->title );\n\t\t$this->assertSame( 'Repeater test 2', $repeater2->get_option( 'title' ) );\n\t\t$this->assertSame( 'papi_repeater_test_other_2', $repeater2->slug );\n\t\t$this->assertSame( 'papi_repeater_test_other_2', $repeater2->get_option( 'slug' ) );\n\t\t$this->assertSame( 'papi_repeater_test_other_2', $repeater2->get_slug() );\n\t\t$this->assertSame( 'repeater_test_other_2', $repeater2->get_slug( true ) );\n\n\t\t$repeater_children2 = $repeater->get_child_properties();\n\n\t\t$this->assertSame( 'string', $repeater_children2[0]->type );\n\t\t$this->assertSame( 'string', $repeater_children2[0]->get_option( 'type' ) );\n\t\t$this->assertSame( 'papi_book_name', $repeater_children2[0]->slug );\n\t\t$this->assertSame( 'papi_book_name', $repeater_children2[0]->get_option( 'slug' ) );\n\t\t$this->assertSame( 'papi_book_name', $repeater_children2[0]->get_slug() );\n\t\t$this->assertSame( 'book_name', $repeater_children2[0]->get_slug( true ) );\n\t\t$this->assertSame( 'Book name', $repeater_children2[0]->title );\n\t\t$this->assertSame( 'Book name', $repeater_children2[0]->get_option( 'title' ) );\n\n\t\t$this->assertSame( 'bool', $repeater_children2[1]->type );\n\t\t$this->assertSame( 'bool', $repeater_children2[1]->get_option( 'type' ) );\n\t\t$this->assertSame( 'papi_is_open', $repeater_children2[1]->slug );\n\t\t$this->assertSame( 'papi_is_open', $repeater_children2[1]->get_option( 'slug' ) );\n\t\t$this->assertSame( 'papi_is_open', $repeater_children2[1]->get_slug() );\n\t\t$this->assertSame( 'is_open', $repeater_children2[1]->get_slug( true ) );\n\t\t$this->assertSame( 'Is open?', $repeater_children2[1]->title );\n\t\t$this->assertSame( 'Is open?', $repeater_children2[1]->get_option( 'title' ) );\n\t}\n\n\tpublic function test_current_user_can() {\n\t\t$property = Papi_Core_Property::factory( [\n\t\t\t'type'  => 'string',\n\t\t\t'title' => 'Name'\n\t\t] );\n\n\t\t$this->assertTrue( $property->current_user_can() );\n\n\t\t$property = Papi_Core_Property::factory( [\n\t\t\t'type'         => 'string',\n\t\t\t'title'        => 'Name',\n\t\t\t'capabilities' => ['fake']\n\t\t] );\n\n\t\t$this->assertFalse( $property->current_user_can() );\n\t}\n\n\tpublic function test_default_options() {\n\t\t$_GET['post'] = 0;\n\t\t$_GET['post_type'] = '';\n\t\t$property = Papi_Core_Property::factory();\n\t\t$options = $property->get_options();\n\t\t$this->assertTrue( is_object( $options ) );\n\t\t$this->assertEmpty( $options->after_class );\n\t\t$this->assertEmpty( $options->after_html );\n\t\t$this->assertEmpty( $options->before_class );\n\t\t$this->assertEmpty( $options->before_html );\n\t\t$this->assertSame( [], $options->capabilities );\n\t\t$this->assertEmpty( $options->default );\n\t\t$this->assertEmpty( $options->description );\n\t\t$this->assertFalse( $options->disabled );\n\t\t$this->assertTrue( $options->display );\n\t\t$this->assertFalse( $options->lang );\n\t\t$this->assertFalse( $options->overwrite );\n\t\t$this->assertEmpty( $options->post_type );\n\t\t$this->assertFalse( $options->raw );\n\t\t$this->assertFalse( $options->required );\n\t\t$this->assertSame( [], $options->rules );\n\t\t$this->assertTrue( is_object( $options->settings ) );\n\t\t$this->assertSame( 'papi_', $options->slug );\n\t\t$this->assertSame( 1000, $options->sort_order );\n\t\t$this->assertEmpty( $options->title );\n\t\t$this->assertEmpty( $options->type );\n\t\t$this->assertEmpty( $options->value );\n\t\tunset( $_GET['post'] );\n\t\tunset( $_GET['post_type'] );\n\t}\n\n\tpublic function test_disabled() {\n\t\t$_GET['post_type'] = 'page';\n\n\t\t$property = Papi_Core_Property::factory( [\n\t\t\t'type'  => 'string',\n\t\t\t'title' => 'Name'\n\t\t] );\n\n\t\t$this->assertFalse( $property->disabled() );\n\n\t\t$property = Papi_Core_Property::factory( [\n\t\t\t'type'    => 'string',\n\t\t\t'title'   => 'Name',\n\t\t\t'site_id' => 2\n\t\t] );\n\n\t\t$this->assertTrue( $property->disabled() );\n\n\t\tunset( $_GET['post_type'] );\n\t}\n\n\tpublic function test_display() {\n\t\t$page_type = papi_get_entry_type_by_id( 'properties-page-type' );\n\t\t$property  = $page_type->get_property( 'string_test' );\n\t\t$this->assertTrue( $property->display() );\n\n\t\t$page_type2 = papi_get_entry_type_by_id( 'faq-page-type' );\n\t\t$property2  = $page_type2->get_property( 'type' );\n\t\t$this->assertFalse( $property2->display() );\n\t}\n\n\tpublic function test_factory() {\n\t\t$property = Papi_Core_Property::factory( [\n\t\t\t'type'  => 'string',\n\t\t\t'title' => 'Name'\n\t\t] );\n\n\t\t$this->assertSame( $property->get_option( 'type' ), 'string' );\n\t\t$this->assertSame( $property->get_option( 'title' ), 'Name' );\n\t\t$this->assertSame( $property->get_option( 'slug' ), 'papi_name' );\n\t}\n\n\tpublic function test_factory_fake() {\n\t\trequire_once PAPI_FIXTURE_DIR . '/properties/class-papi-property-fake.php';\n\n\t\t$this->assertNull( Papi_Core_Property::factory( '' ) );\n\t\t$this->assertNull( Papi_Core_Property::factory( 'fake' ) );\n\t}\n\n\tpublic function test_factory_bad_value() {\n\t\tpapi()->bind( 'Papi_Core_Property_String', '' );\n\t\t$this->assertTrue( Papi_Core_Property::factory( 'string' ) instanceof Papi_Core_Property );\n\t}\n\n\tpublic function test_factory_property() {\n\t\t$property = Papi_Core_Property::factory( [\n\t\t\t'type'  => 'string',\n\t\t\t'title' => 'Hello'\n\t\t] );\n\t\t$this->assertTrue( Papi_Core_Property::factory( $property ) instanceof Papi_Core_Property );\n\t}\n\n\tpublic function test_factory_slug() {\n\t\t$property = Papi_Core_Property::factory( [\n\t\t\t'type'  => 'string',\n\t\t\t'title' => 'Name',\n\t\t\t'slug'  => 'name'\n\t\t] );\n\n\t\t$this->assertSame( $property->get_option( 'slug' ), 'papi_name' );\n\t}\n\n\tpublic function test_factory_slug_empty() {\n\t\t$property = Papi_Core_Property::factory( [\n\t\t\t'type'  => 'string',\n\t\t\t'title' => 'Name'\n\t\t] );\n\n\t\t$this->assertSame( $property->get_option( 'slug' ), 'papi_name' );\n\t}\n\n\tpublic function test_factory_slug_false() {\n\t\t$property = Papi_Core_Property::factory( [\n\t\t\t'type'  => 'string',\n\t\t\t'title' => 'Name',\n\t\t\t'slug'  => false\n\t\t] );\n\n\t\t$slug = unpapify( $property->get_option( 'slug' ) );\n\t\t$slug = str_replace( '_', '', $slug );\n\n\t\t$this->assertRegExp( '/^[a-f0-9]{32}$/', $slug );\n\t}\n\n\tpublic function test_format_value() {\n\t\t$property = Papi_Core_Property::factory( [\n\t\t\t'type'  => 'string',\n\t\t\t'title' => 'Hello'\n\t\t] );\n\n\t\t$actual = $property->format_value( 'Fredrik', '', 0 );\n\n\t\t$this->assertSame( 'Fredrik', $actual );\n\t}\n\n\tpublic function test_get_child_property_fail() {\n\t\t$property = Papi_Core_Property::factory( [\n\t\t\t'type'  => 'string',\n\t\t\t'title' => 'Hello'\n\t\t] );\n\n\t\t$this->assertNull( $property->get_child_property( 'hello' ) );\n\t}\n\n\tpublic function test_get_child_property_success() {\n\t\t$property = Papi_Core_Property::factory( [\n\t\t\t'type'     => 'string',\n\t\t\t'title'    => 'Hello',\n\t\t\t'settings' => [\n\t\t\t\t'items' => [\n\t\t\t\t\tPapi_Core_Property::factory( [\n\t\t\t\t\t\t'type'  => 'string',\n\t\t\t\t\t\t'title' => 'Name'\n\t\t\t\t\t] )\n\t\t\t\t]\n\t\t\t]\n\t\t] );\n\n\t\t$child_property = $property->get_child_property( 'name' );\n\n\t\t$this->assertInstanceOf( 'Papi_Core_Property', $child_property );\n\t\t$this->assertSame( 'string', $child_property->type );\n\t\t$this->assertSame( 'Name', $child_property->title );\n\t}\n\n\tpublic function test_get_convert_type() {\n\t\t$property = Papi_Core_Property::factory();\n\n\t\t$this->assertSame( 'string', $property->get_convert_type() );\n\t}\n\n\tpublic function test_get_default_settings() {\n\t\t$property = Papi_Core_Property::factory();\n\n\t\t$this->assertSame( [], $property->get_default_settings() );\n\t}\n\n\tpublic function test_get_default_value_fail() {\n\t\t$property = Papi_Core_Property::factory( [\n\t\t\t'type'  => 'string',\n\t\t\t'title' => 'Hello'\n\t\t] );\n\n\t\t$this->assertEmpty( $property->get_value() );\n\n\t\t$property = Papi_Core_Property::factory( [\n\t\t\t'default' => 'Hello',\n\t\t\t'title'   => 'Hello',\n\t\t] );\n\n\t\t$this->assertSame( 'Hello', $property->get_value() );\n\t}\n\n\tpublic function test_get_default_value_success() {\n\t\t$_GET['post'] = 0;\n\n\t\t$property = Papi_Core_Property::factory( [\n\t\t\t'default' => 'Hello',\n\t\t\t'type'    => 'string',\n\t\t\t'title'   => 'Hello'\n\t\t] );\n\n\t\t$this->assertSame( 'Hello', $property->get_value() );\n\n\t\tunset( $_GET['post'] );\n\t}\n\n\tpublic function test_get_meta_type() {\n\t\t$property = new Papi_Core_Property();\n\t\t$this->assertSame( 'post', $property->get_meta_type() );\n\t}\n\n\tpublic function test_get_option() {\n\t\t$property = new Papi_Core_Property();\n\n\t\t$property->set_option( 'title', 'Name' );\n\n\t\t$this->assertSame( 'Name', $property->title );\n\t\t$this->assertSame( 'Name', $property->get_option( 'title' ) );\n\n\t\t$property = Papi_Core_Property::factory( [\n\t\t\t'type'  => 'string',\n\t\t\t'title' => 'Name'\n\t\t] );\n\n\t\t$this->assertNull( $property->fake );\n\t\t$this->assertNull( $property->get_option( 'fake' ) );\n\t\t$this->assertSame( 'Name', $property->title );\n\t\t$this->assertSame( 'Name', $property->get_option( 'title' ) );\n\t\t$this->assertSame( 1000, $property->sort_order );\n\t\t$this->assertSame( 1000, $property->get_option( 'sort_order' ) );\n\n\t\t$property->title = 'Link';\n\n\t\t$this->assertSame( 'Link', $property->title );\n\n\t\t$settings = $property->get_option( 'settings' );\n\t\t$this->assertTrue( is_object( $settings ) );\n\t}\n\n\tpublic function test_get_options() {\n\t\t$property = Papi_Core_Property::factory( [\n\t\t\t'type'  => 'string',\n\t\t\t'title' => 'Name'\n\t\t] );\n\n\t\t$options = $property->get_options();\n\n\t\t$this->assertSame( 'Name', $options->title );\n\t}\n\n\tpublic function test_get_store() {\n\t\t$property = new Papi_Core_Property();\n\t\t$this->assertTrue( $property->get_store() instanceof Papi_Post_Store );\n\t}\n\n\tpublic function test_get_post_id() {\n\t\t$property = Papi_Core_Property::factory();\n\n\t\t$this->assertSame( $this->post_id, $property->get_post_id() );\n\t}\n\n\tpublic function test_get_setting() {\n\t\t$property = new Papi_Core_Property();\n\t\t$this->assertNull( $property->get_setting( null ) );\n\t\t$this->assertNull( $property->get_setting( 'length' ) );\n\n\t\t$property = Papi_Core_Property::factory( [\n\t\t\t'type'     => 'string',\n\t\t\t'settings' => [\n\t\t\t\t'length' => 50\n\t\t\t]\n\t\t] );\n\n\t\t$this->assertSame( 50, $property->get_setting( 'length' ) );\n\t}\n\n\tpublic function test_get_settings() {\n\t\t$property = Papi_Core_Property::factory( [\n\t\t\t'type'     => 'string',\n\t\t\t'settings' => [\n\t\t\t\t'items' => [\n\t\t\t\t\t[\n\t\t\t\t\t\t'type' => 'faker'\n\t\t\t\t\t],\n\t\t\t\t\t[\n\t\t\t\t\t\t'type' => 'fake'\n\t\t\t\t\t]\n\t\t\t\t]\n\t\t\t]\n\t\t] );\n\n\t\t$settings = $property->get_settings();\n\n\t\t$this->assertTrue( is_object( $settings ) );\n\t\t$this->assertNotEmpty( $settings->items );\n\t}\n\n\tpublic function test_get_slug() {\n\t\t$property = new Papi_Core_Property();\n\n\t\t$this->assertEmpty( $property->get_slug() );\n\n\t\t$property = Papi_Core_Property::factory( [\n\t\t\t'type'  => 'string',\n\t\t\t'slug'  => 'name',\n\t\t\t'value' => 'Fredrik'\n\t\t] );\n\n\t\t$this->assertSame( 'papi_name', $property->get_slug() );\n\n\t\t$this->assertSame( 'name', $property->get_slug( true ) );\n\t}\n\n\tpublic function test_get_value() {\n\t\t$this->assertNull( ( new Papi_Core_Property )->get_value() );\n\t}\n\n\tpublic function test_html_id() {\n\t\t$property = Papi_Core_Property::factory( [\n\t\t\t'type'  => 'string',\n\t\t\t'slug'  => 'name'\n\t\t] );\n\n\t\t$this->assertSame( '_papi_name', $property->html_id() );\n\t\t$this->assertSame( '_papi_name_suffix', $property->html_id( 'suffix' ) );\n\t\t$this->assertSame( '_papi_name_black', $property->html_id( 'Black' ) );\n\t\t$this->assertSame( '_papi_name_lank', $property->html_id( 'Länk' ) );\n\t}\n\n\tpublic function test_html_id_array() {\n\t\t$property = Papi_Core_Property::factory( [\n\t\t\t'type'  => 'string',\n\t\t\t'slug'  => 'name'\n\t\t] );\n\n\t\t$sub_property = Papi_Core_Property::factory( [\n\t\t\t'type' => 'number',\n\t\t\t'slug' => 'age'\n\t\t] );\n\n\t\t$this->assertSame( '_papi_name[age]', $property->html_id( $sub_property ) );\n\t\t$this->assertSame( '_papi_name[0][age]', $property->html_id( $sub_property, 0 ) );\n\n\t\t$sub_property = (object) [\n\t\t\t'type' => 'number',\n\t\t\t'slug' => 'age'\n\t\t];\n\n\t\t$this->assertSame( '_papi_name[age]', $property->html_id( $sub_property ) );\n\t\t$this->assertSame( '_papi_name[0][age]', $property->html_id( $sub_property, 0 ) );\n\n\t\t$sub_property = 'non array or object';\n\n\t\t$this->assertSame( '_papi_name_non_array_or_object', $property->html_id( $sub_property ) );\n\t\t$this->assertSame( '_papi_name_non_array_or_object', $property->html_id( $sub_property, 0 ) );\n\n\t\t$property = Papi_Core_Property::factory( [\n\t\t\t'type' => 'number',\n\t\t\t'slug' => 'sections[0][age]'\n\t\t] );\n\n\t\t$this->assertSame( '_papi_sections[0][age_sort_order]', $property->html_id( 'sort_order' ) );\n\t}\n\n\tpublic function test_html_name() {\n\t\t$property = Papi_Core_Property::factory( [\n\t\t\t'type'  => 'string',\n\t\t\t'slug'  => 'name'\n\t\t] );\n\n\t\t$this->assertSame( 'papi_name', $property->html_name() );\n\t}\n\n\tpublic function test_html_name_array() {\n\t\t$property = Papi_Core_Property::factory( [\n\t\t\t'type'  => 'string',\n\t\t\t'slug'  => 'name'\n\t\t] );\n\n\t\t$sub_property = Papi_Core_Property::factory( [\n\t\t\t'type' => 'number',\n\t\t\t'slug' => 'age'\n\t\t] );\n\n\t\t$this->assertSame( 'papi_name[age]', $property->html_name( $sub_property ) );\n\t\t$this->assertSame( 'papi_name[0][age]', $property->html_name( $sub_property, 0 ) );\n\n\t\t$sub_property = (object) [\n\t\t\t'type' => 'number',\n\t\t\t'slug' => 'age'\n\t\t];\n\n\t\t$this->assertSame( 'papi_name[age]', $property->html_name( $sub_property ) );\n\t\t$this->assertSame( 'papi_name[0][age]', $property->html_name( $sub_property, 0 ) );\n\n\t\t$sub_property = 'non array or object';\n\n\t\t$this->assertSame( 'papi_name', $property->html_name( $sub_property ) );\n\t\t$this->assertSame( 'papi_name[0]', $property->html_name( $sub_property, 0 ) );\n\t}\n\n\tpublic function test_load_value() {\n\t\t$property = Papi_Core_Property::factory( [\n\t\t\t'type'  => 'string',\n\t\t\t'title' => 'Name'\n\t\t] );\n\n\t\t$actual = $property->load_value( 'Fredrik', '', 0 );\n\n\t\t$this->assertSame( 'Fredrik', $actual );\n\t}\n\n\tpublic function test_layout() {\n\t\t$property = Papi_Core_Property::factory( [\n\t\t\t'type' => 'string',\n\t\t\t'slug' => 'name'\n\t\t] );\n\n\t\t$this->assertSame( 'horizontal', $property->get_option( 'layout' ) );\n\t\t$this->assertTrue( $property->get_option( 'sidebar' ) );\n\n\t\t$property = Papi_Core_Property::factory( [\n\t\t\t'layout' => 'vertical',\n\t\t\t'type'   => 'string',\n\t\t\t'slug'   => 'name'\n\t\t] );\n\n\t\t$this->assertSame( 'vertical', $property->get_option( 'layout' ) );\n\t\t$this->assertTrue( $property->get_option( 'sidebar' ) );\n\t}\n\n\tpublic function test_match_slug() {\n\t\t$property = Papi_Core_Property::factory( [\n\t\t\t'description' => 'Test',\n\t\t\t'type'        => 'string',\n\t\t\t'slug'        => 'name',\n\t\t\t'value'       => 'Fredrik'\n\t\t] );\n\n\t\t$this->assertTrue( $property->match_slug( 'name' ) );\n\t\t$this->assertTrue( $property->match_slug( 'papi_name' ) );\n\n\t\t$this->assertFalse( $property->match_slug( 'kvack' ) );\n\t\t$this->assertFalse( $property->match_slug( 'papi_kvack' ) );\n\t\t$this->assertFalse( $property->match_slug( null ) );\n\t\t$this->assertFalse( $property->match_slug( true ) );\n\t\t$this->assertFalse( $property->match_slug( false ) );\n\t\t$this->assertFalse( $property->match_slug( 1 ) );\n\t\t$this->assertFalse( $property->match_slug( 0 ) );\n\t\t$this->assertFalse( $property->match_slug( [] ) );\n\t\t$this->assertFalse( $property->match_slug( (object) [] ) );\n\t\t$this->assertFalse( $property->match_slug( '' ) );\n\t}\n\n\tpublic function test_post_type_option() {\n\t\t$_GET['post_type'] = 'faq';\n\t\t$page_type2 = papi_get_entry_type_by_id( 'faq-page-type' );\n\t\t$property2  = $page_type2->get_property( 'question' );\n\t\t$this->assertFalse( $property2->disabled() );\n\n\t\t$_GET['post_type'] = 'page';\n\t\t$page_type2 = papi_get_entry_type_by_id( 'faq-page-type' );\n\t\t$property2  = $page_type2->get_property( 'question' );\n\t\t$this->assertTrue( $property2->disabled() );\n\t\tunset( $_GET['post_type'] );\n\t}\n\n\tpublic function test_set_option() {\n\t\t$property = Papi_Core_Property::factory( [\n\t\t\t'type'  => 'string',\n\t\t\t'title' => 'Hello'\n\t\t] );\n\n\t\t$property->set_option( 'title', 'Name' );\n\n\t\t$value = $property->get_option( 'title' );\n\n\t\t$this->assertSame( 'Name', $value );\n\t}\n\n\tpublic function test_set_options() {\n\t\t$property = Papi_Core_Property::factory( [\n\t\t\t'type'  => 'string',\n\t\t\t'title' => 'Hello'\n\t\t] );\n\n\t\t$property->set_options( [\n\t\t\t'title' => 'Name'\n\t\t] );\n\n\t\t$this->assertSame( 'Name', $property->get_option( 'title' ) );\n\n\t\t$property = Papi_Core_Property::factory( [\n\t\t\t'type' => 'string'\n\t\t] );\n\n\t\t$this->assertSame( 'papi_string', $property->get_option( 'slug' ) );\n\n\t\t$property = Papi_Core_Property::factory( [\n\t\t\t'type'        => 'string',\n\t\t\t'description' => 'test'\n\t\t] );\n\n\t\t$this->assertSame( 'test', $property->get_option( 'description' ) );\n\n\t\t$property = Papi_Core_Property::factory( [\n\t\t\t'type' => 'string',\n\t\t\t'desc' => 'test'\n\t\t] );\n\n\t\t$this->assertSame( 'test', $property->get_option( 'description' ) );\n\t}\n\n\tpublic function test_set_post_id() {\n\t\t$property = Papi_Core_Property::factory( [\n\t\t\t'type'  => 'string',\n\t\t\t'title' => 'Hello'\n\t\t] );\n\n\t\t$property->set_post_id( null );\n\n\t\t$this->assertNotEmpty( $property->get_post_id() );\n\n\t\t$post_id = $this->factory->post->create();\n\t\t$property->set_post_id( $post_id );\n\n\t\t$this->assertSame( $post_id, $property->get_post_id() );\n\t}\n\n\tpublic function test_set_settings() {\n\t\t$property = Papi_Core_Property::factory( [\n\t\t\t'type'     => 'string',\n\t\t\t'title'    => 'Hello',\n\t\t\t'settings' => []\n\t\t] );\n\n\t\t$this->assertFalse( $property->get_setting( 'allow_html' ) );\n\n\t\t$property->set_setting( 'allow_html', true );\n\n\t\t$this->assertTrue( $property->get_setting( 'allow_html' ) );\n\t}\n\n\tpublic function test_update_value() {\n\t\t$property = Papi_Core_Property::factory( [\n\t\t\t'type'  => 'string',\n\t\t\t'title' => 'Name'\n\t\t] );\n\n\t\t$actual = $property->update_value( 'Fredrik', '', 0 );\n\n\t\t$this->assertSame( 'Fredrik', $actual );\n\t}\n\n\tpublic function test_update_value_empty() {\n\t\t$property = Papi_Core_Property::factory( [\n\t\t\t'type'  => 'string',\n\t\t\t'title' => 'Name'\n\t\t] );\n\n\t\t$value = $property->update_value( [null], '', 0 );\n\t\t$this->assertNull( $value );\n\n\t\t$value1 = $property->load_value( $value, '', 0 );\n\t\t$this->assertNull( $value1 );\n\t}\n\n\tpublic function test_update_value_with_empty_values() {\n\t\t$property = Papi_Core_Property::factory( [\n\t\t\t'type'  => 'string',\n\t\t\t'title' => 'Name'\n\t\t] );\n\n\t\t$value = $property->update_value( ['', 75, 87], '', 0 );\n\t\t$this->assertTrue( is_string( $value ) );\n\n\t\t$value1 = $property->load_value( $value, '', 0 );\n\t\t$this->assertSame( [75, 87], $value1 );\n\t}\n\n\tpublic function test_update_value_with_key_value() {\n\t\t$property = Papi_Core_Property::factory( [\n\t\t\t'type'  => 'string',\n\t\t\t'title' => 'Name'\n\t\t] );\n\n\t\t$value = $property->update_value( ['name' => 'Fredrik'], '', 0 );\n\t\t$this->assertTrue( is_string( $value ) );\n\n\t\t$value1 = $property->load_value( $value, '', 0 );\n\t\t$this->assertSame( 'Fredrik', $value1->name );\n\t}\n\n\tpublic function test_value_serialize() {\n\t\t$property = Papi_Core_Property::factory( [\n\t\t\t'title' => 'Name'\n\t\t] );\n\n\t\t$value = $property->update_value( [1, 2, 3], '', 0 );\n\t\t$this->assertTrue( is_string( $value ) );\n\n\t\t$value1 = $property->load_value( $value, '', 0 );\n\t\t$this->assertSame( [1, 2, 3], $value1 );\n\n\t\t$value2 = $property->format_value( $value, '', 0 );\n\t\t$this->assertSame( [1, 2, 3], $value2 );\n\t}\n}\n"
  },
  {
    "path": "tests/cases/core/class-papi-core-tab-test.php",
    "content": "<?php\n\n/**\n * @group core\n */\nclass Papi_Core_Tab_Test extends WP_UnitTestCase {\n\n\tpublic function test_options() {\n\t\t$tab = new Papi_Core_Tab( [\n\t\t\t'title' => 'Tab'\n\t\t] );\n\t\t$this->assertSame( 'Tab', $tab->title );\n\t\t$this->assertSame( '_papi_tab', $tab->id );\n\t}\n}\n"
  },
  {
    "path": "tests/cases/core/class-papi-core-type-test.php",
    "content": "<?php\n\n/**\n * @group core\n */\nclass Papi_Core_Type_Test extends WP_UnitTestCase {\n\n\tpublic function setUp() {\n\t\tparent::setUp();\n\n\t\t$this->info_core_path  = PAPI_FIXTURE_DIR . '/core-types/info-core-type.php';\n\t\t$this->info2_core_path = PAPI_FIXTURE_DIR . '/core-types/info2-core-type.php';\n\t\t$this->info_core_type  = new Papi_Core_Type( $this->info_core_path );\n\n\t\tif ( ! class_exists( 'Info_Core_Type' ) ) {\n\t\t\trequire_once $this->info_core_path;\n\t\t}\n\n\t\tif ( ! class_exists( 'Info2_Core_Type' ) ) {\n\t\t\trequire $this->info2_core_path;\n\t\t}\n\t}\n\n\tpublic function tearDown() {\n\t\tparent::tearDown();\n\t\tunset(\n\t\t\t$this->info_core_path,\n\t\t\t$this->info2_core_type,\n\t\t\t$this->info_core_type\n\t\t);\n\t}\n\n\tpublic function test_core_type_allowed() {\n\t\t$this->assertTrue( $this->info_core_type->allowed() );\n\t}\n\n\tpublic function test_core_type_get_id() {\n\t\t$this->assertRegExp( '/\\/core-types\\/info-core-type$/', $this->info_core_type->get_id() );\n\t}\n\n\tpublic function test_core_type_get_meta() {\n\t\t$class = new Info_Core_Type( $this->info_core_path );\n\t\t$this->assertSame( 'Info core type', $class->name );\n\t\t$this->assertSame( 500, $class->sort_order );\n\n\t\t$class2 = new Info2_Core_Type( $this->info2_core_path );\n\t\t$this->assertSame( 'Info2 core type', $class2->name );\n\t\t$this->assertSame( 500, $class2->sort_order );\n\t}\n\n\tpublic function test_core_type_get_meta_abstract() {\n\t\t$path = PAPI_FIXTURE_DIR . '/core-types/abstract-core-type.php';\n\n\t\tif ( ! class_exists( 'Abstract_Core_Type' ) ) {\n\t\t\trequire_once $path;\n\t\t}\n\n\t\t$class = new Abstract_Core_Type( $path );\n\t\t$this->assertSame( 'Abstract core', $class->name );\n\t}\n\n\tpublic function test_core_type_get_type() {\n\t\t$this->assertSame( 'core', $this->info_core_type->get_type() );\n\t}\n\n\tpublic function test_core_type_has_name() {\n\t\t$this->assertFalse( $this->info_core_type->has_name() );\n\t\t$class = new Info_Core_Type( $this->info_core_path );\n\t\t$this->assertTrue( $class->has_name() );\n\t}\n\n\tpublic function test_core_type_match_id() {\n\t\t$this->assertTrue( $this->info_core_type->match_id( $this->info_core_type->get_id() ) );\n\t\t$this->assertFalse( $this->info_core_type->match_id( '' ) );\n\t}\n\n\tpublic function test_core_type_new_class() {\n\t\t$class = $this->info_core_type->new_class();\n\t\t$this->assertInstanceOf( 'Papi_Core_Type', $class );\n\t}\n\n\tpublic function test_set_id() {\n\t\t$class = new Papi_Core_Type;\n\t\t$this->assertEmpty( $class->get_id() );\n\n\t\t$class->set_id( 'hello' );\n\t\t$this->assertSame( 'hello', $class->get_id() );\n\t}\n}\n"
  },
  {
    "path": "tests/cases/lib/core/cache-test.php",
    "content": "<?php\n\nclass Papi_Lib_Core_Cache_Test extends WP_UnitTestCase {\n\n\tpublic function setUp() {\n\t\tparent::setUp();\n\n\t\tadd_filter( 'papi/settings/directories', function () {\n\t\t\treturn [1, PAPI_FIXTURE_DIR . '/page-types'];\n\t\t} );\n\n\t\t$this->post_id = $this->factory->post->create();\n\n\t\tupdate_post_meta( $this->post_id, papi_get_page_type_key(), 'simple-page-type' );\n\t}\n\n\tpublic function tearDown() {\n\t\tparent::tearDown();\n\t\tunset( $this->post_id );\n\t}\n\n\tpublic function test_papi_cache_delete_1() {\n\t\tpapi_cache_set( 'test', $this->post_id, 'fredrik' );\n\t\t$this->assertSame( 'fredrik', papi_cache_get( 'test', $this->post_id ) );\n\n\t\tpapi_cache_set( 'test', $this->post_id, 'elli' );\n\t\t$this->assertSame( 'elli', papi_cache_get( 'test', $this->post_id ) );\n\n\t\tpapi_cache_delete( 'test', $this->post_id );\n\t\t$this->assertEmpty( papi_cache_get( 'test', $this->post_id ) );\n\t}\n\n\tpublic function test_papi_cache_delete_2() {\n\t\t$this->assertTrue( papi_update_field( $this->post_id, 'namn', 'fredrik' ) );\n\t\t$this->assertSame( 'fredrik', papi_get_field( $this->post_id, 'namn' ) );\n\n\t\t$this->assertTrue( papi_update_field( $this->post_id, 'namn', 'elli' ) );\n\t\t$this->assertSame( 'elli', papi_get_field( $this->post_id, 'namn' ) );\n\t}\n\n\tpublic function test_papi_cache_delete_admin() {\n\t\tpapi_cache_delete( 'test', $this->post_id );\n\t\t$this->assertEmpty( papi_cache_get( 'test', $this->post_id ) );\n\n\t\tglobal $current_screen;\n\t\t$current_screen = WP_Screen::get( 'admin_init' );\n\t\tpapi_cache_set( 'test', $this->post_id, 'elli' );\n\t\t$this->assertSame( 'elli', papi_cache_get( 'test', $this->post_id ) );\n\n\t\tpapi_cache_delete( 'test', $this->post_id );\n\t\t$this->assertEmpty( papi_cache_get( 'test', $this->post_id ) );\n\t\t$current_screen = null;\n\t}\n\n\tpublic function test_papi_cache_get() {\n\t\tpapi_cache_set( 'get', $this->post_id, 'elli' );\n\t\t$this->assertSame( 'elli', papi_cache_get( 'get', $this->post_id ) );\n\t}\n\n\tpublic function test_papi_cache_get_admin() {\n\t\tglobal $current_screen;\n\t\t$current_screen = WP_Screen::get( 'admin_init' );\n\n\t\tpapi_cache_set( 'test', $this->post_id, 'elli' );\n\t\t$this->assertSame( 'elli', papi_cache_get( 'test', $this->post_id ) );\n\n\t\t$current_screen = null;\n\n\t\tpapi_cache_delete( 'test', $this->post_id );\n\t\t$this->assertEmpty( papi_cache_get( 'test', $this->post_id ) );\n\n\t\tglobal $current_screen;\n\t\t$current_screen = WP_Screen::get( 'admin_init' );\n\n\t\tpapi_cache_delete( 'test', $this->post_id );\n\t\t$this->assertEmpty( papi_cache_get( 'test', $this->post_id ) );\n\n\t\t$current_screen = null;\n\t}\n\n\tpublic function test_papi_cache_key() {\n\t\t$this->assertEmpty( papi_cache_key( 0, 1 ) );\n\t\t$this->assertEmpty( papi_cache_key( [], 'hello' ) );\n\t\t$this->assertEmpty( papi_cache_key( (object) [], 230 ) );\n\t\t$this->assertEmpty( papi_cache_key( true, 'false' ) );\n\t\t$this->assertEmpty( papi_cache_key( false, 'true' ) );\n\t\t$this->assertEmpty( papi_cache_key( null, 2 ) );\n\n\t\tglobal $post;\n\t\t$post_id = $this->factory->post->create();\n\t\t$post    = get_post( $post_id );\n\t\t$this->assertSame( 'papi_post_page_' . $post_id, papi_cache_key( 'page', $post_id ) );\n\t\t$this->assertSame( 'papi_post_page_' . $post_id, papi_cache_key( 'papi_page', $post_id ) );\n\t\t$this->assertSame( 'papi_post_page_920', papi_cache_key( 'page', 920 ) );\n\t\tunset( $post );\n\t}\n\n\tpublic function test_papi_cache_set() {\n\t\tpapi_cache_set( 'set', $this->post_id, 'elli' );\n\t\t$this->assertSame( 'elli', papi_cache_get( 'set', $this->post_id ) );\n\t}\n\n\tpublic function test_papi_cache_set_admin() {\n\t\tglobal $current_screen;\n\t\t$current_screen = WP_Screen::get( 'admin_init' );\n\n\t\tpapi_cache_set( 'test', $this->post_id, 'elli' );\n\t\t$this->assertSame( 'elli', papi_cache_get( 'test', $this->post_id ) );\n\n\t\t$current_screen = null;\n\n\t\tpapi_cache_delete( 'test', $this->post_id );\n\t\t$this->assertEmpty( papi_cache_get( 'test', $this->post_id ) );\n\n\t\tglobal $current_screen;\n\t\t$current_screen = WP_Screen::get( 'admin_init' );\n\n\t\tpapi_cache_delete( 'test', $this->post_id );\n\t\t$this->assertEmpty( papi_cache_get( 'test', $this->post_id ) );\n\n\t\t$current_screen = null;\n\t}\n}\n"
  },
  {
    "path": "tests/cases/lib/core/conditional-test.php",
    "content": "<?php\n\nclass Papi_Lib_Core_Conditional_Test extends WP_UnitTestCase {\n\n\tpublic function test_papi_is_rule() {\n\t\t$this->assertFalse( papi_is_rule( null ) );\n\t\t$this->assertFalse( papi_is_rule( true ) );\n\t\t$this->assertFalse( papi_is_rule( false ) );\n\t\t$this->assertFalse( papi_is_rule( 1 ) );\n\t\t$this->assertFalse( papi_is_rule( '' ) );\n\t\t$this->assertFalse( papi_is_rule( [] ) );\n\t\t$this->assertFalse( papi_is_rule( (object) [] ) );\n\t\t$this->assertTrue( papi_is_rule( papi_rule( [\n\t\t\t'operator' => '>',\n\t\t\t'slug'     => 'name',\n\t\t\t'value'    => 'Fredrik'\n\t\t] ) ) );\n\t}\n\n\tpublic function test_papi_rule() {\n\t\t$this->assertNull( papi_rule( null ) );\n\t\t$this->assertNull( papi_rule( true ) );\n\t\t$this->assertNull( papi_rule( false ) );\n\t\t$this->assertNull( papi_rule( 1 ) );\n\t\t$this->assertNull( papi_rule( '' ) );\n\t\t$this->assertNull( papi_rule( [] ) );\n\t\t$this->assertNull( papi_rule( (object) [] ) );\n\t\t$rule = papi_rule( [\n\t\t\t'operator' => '>',\n\t\t\t'slug'     => 'name',\n\t\t\t'value'    => 'Fredrik'\n\t\t] );\n\t\t$this->assertTrue( papi_is_rule( $rule ) );\n\t\t$this->assertTrue( papi_is_rule( papi_rule( $rule ) ) );\n\t}\n}\n"
  },
  {
    "path": "tests/cases/lib/core/data-test.php",
    "content": "<?php\n\nclass Papi_Lib_Core_Data_Test extends WP_UnitTestCase {\n\n\tpublic function setUp() {\n\t\tparent::setUp();\n\t\t$this->post_id = $this->factory->post->create();\n\t}\n\n\tpublic function tearDown() {\n\t\tparent::tearDown();\n\t\tunset( $this->post_id );\n\t}\n\n\tpublic function test_papi_data_delete() {\n\t\t$this->assertFalse( papi_data_delete( $this->post_id, 'name' ) );\n\n\t\tpapi_data_update( $this->post_id, 'name', 'Fredrik' );\n\t\t$this->assertTrue( papi_data_delete( $this->post_id, 'name' ) );\n\t}\n\n\tpublic function test_papi_data_get() {\n\t\t$this->assertEmpty( papi_data_get( $this->post_id, 'name' ) );\n\n\t\tpapi_data_update( $this->post_id, 'name', 'Fredrik' );\n\t\t$this->assertSame( 'Fredrik', papi_data_get( $this->post_id, 'name' ) );\n\t}\n\n\tpublic function test_papi_data_update() {\n\t\t$this->assertEmpty( papi_data_get( $this->post_id, 'name' ) );\n\n\t\tpapi_data_update( $this->post_id, 'name', 'Fredrik' );\n\t\t$this->assertSame( 'Fredrik', papi_data_get( $this->post_id, 'name' ) );\n\t}\n}\n"
  },
  {
    "path": "tests/cases/lib/core/deprecated-test.php",
    "content": "<?php\n\nclass Papi_Lib_Core_Deprecated_Test extends WP_UnitTestCase {\n\n\tpublic function setUp() {\n\t\tparent::setUp();\n\t\t$_GET = [];\n\n\t\tadd_action( 'deprecated_function_run', [$this, 'deprecated_function_run'] );\n\t\tadd_filter( 'papi/settings/directories', function () {\n\t\t\treturn [1, PAPI_FIXTURE_DIR . '/page-types'];\n\t\t} );\n\n\t\t$this->post_id = $this->factory->post->create();\n\t\tupdate_post_meta( $this->post_id, papi_get_page_type_key(), 'simple-page-type' );\n\t}\n\n\tpublic function deprecated_function_run( $function ) {\n\t\tadd_filter( 'deprecated_function_trigger_error', '__return_false' );\n\t}\n\n\tpublic function tearDown() {\n\t\tparent::tearDown();\n\t\tremove_filter( 'deprecated_function_trigger_error', '__return_false' );\n\t\tunset( $_GET, $this->post_id );\n\t}\n\n\t/**\n\t * `papi_get_page` is deprecated since 3.2.0\n\t */\n\tpublic function test_papi_get_page() {\n\t\t$page = papi_get_page( $this->post_id );\n\t\t$this->assertTrue( is_object( $page ) );\n\t\t$page = papi_get_page( $this->post_id, 'fake' );\n\t\t$this->assertNull( $page );\n\t}\n}\n"
  },
  {
    "path": "tests/cases/lib/core/io-test.php",
    "content": "<?php\n\nclass Papi_Lib_Core_IO_Test extends WP_UnitTestCase {\n\n\tpublic function test_register_papi_directory() {\n\t\t$defaults = [get_template_directory() . '/page-types'];\n\t\t$this->assertEmpty( register_papi_directory( null ) );\n\t\t$this->assertEmpty( register_papi_directory( null ) );\n\n\t\tregister_papi_directory( 'fake-path' );\n\t\t$this->assertEquals( $defaults, papi_filter_settings_directories() );\n\n\t\tregister_papi_directory( ['fake-path'] );\n\t\t$this->assertEquals( $defaults, papi_filter_settings_directories() );\n\n\t\tregister_papi_directory( PAPI_FIXTURE_DIR . '/entry-types' );\n\t\t$this->assertSame( [PAPI_FIXTURE_DIR . '/entry-types'], papi_filter_settings_directories() );\n\t}\n\n\tpublic function test_papi_get_all_files_in_directory() {\n\t\t$this->assertEmpty( papi_get_all_files_in_directory( 1 ) );\n\t\t$this->assertEmpty( papi_get_all_files_in_directory( true ) );\n\t\t$this->assertEmpty( papi_get_all_files_in_directory( false ) );\n\t\t$this->assertEmpty( papi_get_all_files_in_directory( [] ) );\n\t\t$this->assertEmpty( papi_get_all_files_in_directory( (object) [] ) );\n\t\t$this->assertEmpty( papi_get_all_files_in_directory( '' ) );\n\t\t$this->assertEmpty( papi_get_all_files_in_directory() );\n\t\t$this->assertTrue( is_array( papi_get_all_files_in_directory() ) );\n\n\t\t$actual = papi_get_all_files_in_directory( PAPI_FIXTURE_DIR . '/page-types' );\n\t\t$expected = PAPI_FIXTURE_DIR . '/page-types/simple-page-type.php';\n\n\t\t$this->assertTrue( is_array( $actual ) );\n\t\t$this->assertTrue( ! empty( $actual ) );\n\t\t$this->assertTrue( in_array( $expected, $actual ) );\n\t}\n\n\tpublic function test_papi_get_core_type_file_path() {\n\t\t$this->assertEmpty( papi_get_core_type_file_path( '' ) );\n\n\t\tadd_filter( 'papi/settings/directories', function () {\n\t\t\treturn [PAPI_FIXTURE_DIR . '/entry-types', PAPI_FIXTURE_DIR . '/entry-types2'];\n\t\t} );\n\n\t\t$output    = papi_get_all_core_type_files();\n\t\t$file_path = '';\n\n\t\tforeach ( $output as $file ) {\n\t\t\tif ( strpos( 'entry-types2/term-entry-type.php', $file ) !== -1 ) {\n\t\t\t\t$this->assertTrue( true );\n\t\t\t\t$file_path = $file;\n\t\t\t}\n\t\t}\n\n\t\tif ( empty( $file_path ) ) {\n\t\t\t$this->assertTrue( false );\n\t\t}\n\n\t\t$file_path = str_replace( 'types2', 'types', $file_path );\n\t\t$file_path = papi_get_core_type_file_path( $file_path );\n\n\t\t$this->assertTrue( strpos( 'entry-types2/term-entry-type.php', $file ) !== -1 );\n\t}\n\n\tpublic function test_papi_get_all_core_type_files() {\n\t\tpapi()->reset();\n\t\t$this->assertEmpty( papi_get_all_core_type_files() );\n\n\t\tadd_filter( 'papi/settings/directories', function () {\n\t\t\treturn [1,  PAPI_FIXTURE_DIR . '/page-types'];\n\t\t} );\n\n\t\tpapi()->reset();\n\t\t$actual = papi_get_all_core_type_files();\n\t\t$this->assertFalse( empty( $actual ) );\n\t\t$this->assertTrue( is_array( $actual ) );\n\t}\n\n\tpublic function test_papi_get_file_path() {\n\t\t$this->assertNull( papi_get_file_path( 1 ) );\n\t\t$this->assertNull( papi_get_file_path( null ) );\n\t\t$this->assertNull( papi_get_file_path( true ) );\n\t\t$this->assertNull( papi_get_file_path( false ) );\n\t\t$this->assertNull( papi_get_file_path( [] ) );\n\t\t$this->assertNull( papi_get_file_path( (object) [] ) );\n\t\t$this->assertNull( papi_get_file_path( '' ) );\n\t\t$this->assertNull( papi_get_file_path( 'simple-page-type' ) );\n\n\t\tadd_filter( 'papi/settings/directories', function () {\n\t\t\treturn [1,  PAPI_FIXTURE_DIR . '/page-types'];\n\t\t} );\n\n\t\t$path1 = papi_get_file_path( 'simple-page-type' );\n\t\t$path2 = papi_get_file_path( 'simple-page-type.php' );\n\n\t\t$this->assertTrue( strpos( $path1, 'simple-page-type.php' ) !== false );\n\t\t$this->assertTrue( strpos( $path2, 'simple-page-type.php' ) !== false );\n\t}\n\n\tpublic function test_papi_get_core_type_base_path() {\n\t\t$this->assertEmpty( papi_get_core_type_base_path( 1 ) );\n\t\t$this->assertEmpty( papi_get_core_type_base_path( true ) );\n\t\t$this->assertEmpty( papi_get_core_type_base_path( false ) );\n\t\t$this->assertEmpty( papi_get_core_type_base_path( [] ) );\n\t\t$this->assertEmpty( papi_get_core_type_base_path( (object) [] ) );\n\t\t$this->assertEmpty( papi_get_core_type_base_path( '' ) );\n\t\t$this->assertSame( 'simple-page-type', papi_get_core_type_base_path( 'simple-page-type' ) );\n\n\t\tadd_filter( 'papi/settings/directories', function () {\n\t\t\treturn [1,  PAPI_FIXTURE_DIR . '/page-types'];\n\t\t} );\n\n\t\t$path = papi_get_file_path( 'simple-page-type' );\n\t\t$this->assertSame( 'simple-page-type', papi_get_core_type_base_path( $path ) );\n\n\t\t$this->assertEmpty( papi_get_core_type_base_path( '' ) );\n\t\t$this->assertEmpty( papi_get_core_type_base_path( null ) );\n\t}\n}\n"
  },
  {
    "path": "tests/cases/lib/core/meta-test.php",
    "content": "<?php\n\nclass Papi_Lib_Core_Meta_Test extends WP_UnitTestCase {\n\n\tpublic function setUp() {\n\t\tparent::setUp();\n\t\t$_GET = [];\n\t\t$this->post_id = $this->factory->post->create();\n\t\t$this->term_id = $this->factory->term->create();\n\t}\n\n\tpublic function tearDown() {\n\t\tparent::tearDown();\n\t\tunset( $_GET, $this->post_id, $this->term_id );\n\t}\n\n\tpublic function test_papi_get_meta_id() {\n\t\t$this->assertSame( 0, papi_get_meta_id( 'option' ) );\n\n\t\t$_GET['post'] = 1;\n\t\t$this->assertSame( 1, papi_get_meta_id() );\n\t\t$this->assertSame( 1, papi_get_meta_id( 'post' ) );\n\t\tunset( $_GET['post'] );\n\n\t\t$_GET['term_id'] = 2;\n\t\t$this->assertSame( 2, papi_get_meta_id( 'term' ) );\n\t\tunset( $_GET['term_id'] );\n\t}\n\n\tpublic function test_papi_get_meta_id_column() {\n\t\t$this->assertSame( 'post_id', papi_get_meta_id_column() );\n\t\t$this->assertSame( 'post_id', papi_get_meta_id_column( 'post' ) );\n\t\t$this->assertSame( 'post_id', papi_get_meta_id_column( 'page' ) );\n\t\t$this->assertSame( 'term_id', papi_get_meta_id_column( 'term' ) );\n\t\t$this->assertSame( 'term_id', papi_get_meta_id_column( 'taxonomy' ) );\n\t\t$this->assertNull( papi_get_meta_id_column( 'hello' ) );\n\t}\n\n\tpublic function test_papi_get_meta_store() {\n\t\t$this->assertInstanceOf( 'Papi_Post_Store', papi_get_meta_store( $this->post_id ) );\n\t\t$this->assertInstanceOf( 'Papi_Option_Store', papi_get_meta_store( 0, 'option' ) );\n\t\t$store = papi_get_meta_store( $this->post_id, 'fake' );\n\t\t$this->assertNull( $store );\n\n\t\tif ( function_exists( 'get_term_meta' ) ) {\n\t\t\t$this->assertInstanceOf( 'Papi_Term_Store', papi_get_meta_store( $this->term_id, 'term' ) );\n\t\t}\n\t}\n\n\tpublic function test_papi_get_meta_type() {\n\t\t$this->assertSame( 'post', papi_get_meta_type() );\n\t\t$this->assertSame( 'post', papi_get_meta_type( 'post' ) );\n\t\t$this->assertSame( 'post', papi_get_meta_type( 'page' ) );\n\t\t$this->assertSame( 'term', papi_get_meta_type( 'term' ) );\n\t\t$this->assertSame( 'term', papi_get_meta_type( 'taxonomy' ) );\n\t\t$this->assertSame( 'option', papi_get_meta_type( 'option' ) );\n\t\t$this->assertNull( papi_get_meta_type( 'hello' ) );\n\n\t\t$_GET['meta_type'] = 'test';\n\t\t$this->assertSame( 'test', papi_get_meta_type() );\n\t\tunset( $_GET['meta_type'] );\n\n\t\t$this->assertSame( 'post', papi_get_meta_type() );\n\t}\n\n\tpublic function test_papi_get_meta_type_quried_object() {\n\t\tglobal $wp_query;\n\n\t\t// Test term object.\n\t\t$wp_query->is_category = true;\n\t\t$category_id = $this->factory->category->create();\n\t\t$wp_query->query_vars['cat'] = $category_id;\n\t\t$this->assertSame( 'term', papi_get_meta_type() );\n\t\t$this->assertTrue( isset( get_queried_object()->term_id ) );\n\n\t\t// Turn off category query.\n\t\t$wp_query->queried_object = null;\n\t\t$wp_query->is_category = false;\n\n\t\t// Test post object.\n\t\t$post_id = $this->factory->post->create();\n\t\t$wp_query->is_singular = true;\n\t\t$wp_query->post = get_post( $post_id );\n\t\t$this->assertSame( 'post', papi_get_meta_type() );\n\t\t$this->assertTrue( isset( get_queried_object()->post_type ) );\n\n\t\t// Turn off post query.\n\t\t$wp_query->queried_object = null;\n\t\t$wp_query->is_singular = false;\n\t\t$wp_query->post = null;\n\t}\n\n\tpublic function test_papi_get_meta_type_wp_query() {\n\t\tglobal $wp_query;\n\n\t\t$wp_query->is_tag = true;\n\t\t$this->assertSame( 'term', papi_get_meta_type() );\n\t\t$wp_query->is_tag = false;\n\n\t\t$wp_query->is_category = true;\n\t\t$this->assertSame( 'term', papi_get_meta_type() );\n\t\t$wp_query->is_category = false;\n\t}\n\n\tpublic function test_papi_get_meta_type_admin() {\n\t\tglobal $current_screen;\n\n\t\t$current_screen = WP_Screen::get( 'admin_init' );\n\n\t\t$_SERVER['REQUEST_URI'] = 'http://site.com/?taxonomy=test';\n\t\t$this->assertSame( 'term', papi_get_meta_type() );\n\t\t$_SERVER['REQUEST_URI'] = '';\n\n\t\t$_SERVER['REQUEST_URI'] = 'http://site.com/?page=papi/option/test';\n\t\t$this->assertSame( 'option', papi_get_meta_type() );\n\t\t$_SERVER['REQUEST_URI'] = '';\n\n\t\t$current_screen = null;\n\t}\n\n\tpublic function test_papi_get_meta_type_admin_ajax() {\n\t\tglobal $current_screen;\n\n\t\t$current_screen = WP_Screen::get( 'admin_init' );\n\n\t\tif ( ! defined( 'DOING_AJAX' ) ) {\n\t\t\tdefine( 'DOING_AJAX', true );\n\t\t}\n\n\t\t$_POST['post_type'] = 'post';\n\t\t$_POST['taxonomy'] = 'test';\n\t\t$this->assertSame( 'term', papi_get_meta_type() );\n\t\tunset( $_POST['taxonomy'] );\n\n\t\t$this->assertSame( 'post', papi_get_meta_type() );\n\t\tunset( $_POST['post_type'] );\n\n\t\t$current_screen = null;\n\t}\n}\n"
  },
  {
    "path": "tests/cases/lib/core/post-test.php",
    "content": "<?php\n\nclass Papi_Lib_Core_Post_Test extends WP_UnitTestCase {\n\n\tpublic function test_papi_get_post_id() {\n\t\tglobal $post;\n\n\t\t$post_id = $this->factory->post->create();\n\n\t\t$post = get_post( $post_id );\n\t\t$this->assertSame( 1, papi_get_post_id( 1 ) );\n\t\t$this->assertSame( $post_id, papi_get_post_id() );\n\t\t$this->assertSame( $post_id, papi_get_post_id( null ) );\n\n\t\t$this->assertSame( $post_id, papi_get_post_id( $post ) );\n\t\t$this->assertSame( 1, papi_get_post_id( '1' ) );\n\n\t\t$post = null;\n\n\t\t$_GET = ['post' => $post_id];\n\t\t$this->assertSame( $post_id, papi_get_post_id() );\n\t\tunset( $_GET );\n\n\t\t$_GET = ['post' => [1, 2, 3]];\n\t\t$this->assertSame( 0, papi_get_post_id() );\n\t\tunset( $_GET );\n\n\t\t$_GET = ['page_id' => $post_id];\n\t\t$this->assertSame( $post_id, papi_get_post_id() );\n\t\tunset( $_GET );\n\n\t\t$_POST = [\n\t\t\t'action' => 'query-attachments',\n\t\t\t'query'  => [\n\t\t\t\t'item' => $post_id\n\t\t\t]\n\t\t];\n\t\t$this->assertSame( $post_id, papi_get_post_id() );\n\t\tunset( $_POST );\n\n\t\t$_GET  = [\n\t\t\t'post' => $post_id\n\t\t];\n\t\t$_POST = [\n\t\t\t'action' => 'query-attachments'\n\t\t];\n\t\t$this->assertSame( $post_id, papi_get_post_id() );\n\t\tunset( $_POST );\n\t}\n\n\tpublic function test_papi_get_parent_post_id() {\n\t\t$this->assertEmpty( papi_get_parent_post_id() );\n\t\t$_GET['post_parent'] = 7;\n\t\t$this->assertSame( 7, papi_get_parent_post_id() );\n\t\tunset( $_GET );\n\t}\n\n\tpublic function test_papi_get_post_type() {\n\t\tglobal $post;\n\n\t\t$this->assertEmpty( papi_get_post_type() );\n\n\t\t$_GET = ['post_type' => 'post'];\n\t\t$this->assertSame( 'post', papi_get_post_type() );\n\n\t\t$_GET = ['page' => 'papi-add-new-page,books'];\n\t\t$this->assertSame( 'books', papi_get_post_type() );\n\n\t\t$_GET = ['page' => 'papi-add-new-page,dash-post'];\n\t\t$this->assertSame( 'dash-post', papi_get_post_type() );\n\n\t\t$_GET = ['page' => 'papi-add-new-page,und_post'];\n\t\t$this->assertSame( 'und_post', papi_get_post_type() );\n\n\t\t$_GET = ['page' => 'papi-add-new-page,3414'];\n\t\t$this->assertSame( '3414', papi_get_post_type() );\n\n\t\t$_GET = ['page' => 'papi-add-new-page,dash13'];\n\t\t$this->assertSame( 'dash13', papi_get_post_type() );\n\n\t\t$_GET = ['page' => ''];\n\t\t$this->assertEmpty( papi_get_post_type() );\n\n\t\t$_GET = ['page' => 'papi-add-new-page,'];\n\t\t$this->assertEmpty( papi_get_post_type() );\n\t\tunset( $_GET );\n\n\t\t$_POST = ['post_type' => 'page'];\n\t\t$this->assertSame( 'page', papi_get_post_type() );\n\t\tunset( $_POST );\n\n\t\t$_SERVER['REQUEST_URI'] = 'wordpress/wp-admin/post-new.php';\n\t\t$this->assertSame( 'post', papi_get_post_type() );\n\t\t$_SERVER['REQUEST_URI'] = '';\n\n\t\t$post_id = $this->factory->post->create();\n\t\t$post = get_post( $post_id );\n\t\t$this->assertSame( 'post', papi_get_post_type() );\n\t}\n\n\tpublic function test_papi_get_post_type_label() {\n\t\t$this->assertEmpty( papi_get_post_type_label( 'fake', 'name', '' ) );\n\t\t$this->assertSame( 'Posts', papi_get_post_type_label( 'post', 'name' ) );\n\t}\n}\n"
  },
  {
    "path": "tests/cases/lib/core/property-test.php",
    "content": "<?php\n\nclass Papi_Lib_Core_Property_Test extends WP_UnitTestCase {\n\n\tpublic function setUp() {\n\t\tparent::setUp();\n\n\t\tglobal $post;\n\n\t\t$this->post_id = $this->factory->post->create();\n\n\t\t$post = get_post( $this->post_id );\n\t}\n\n\tpublic function tearDown() {\n\t\tparent::tearDown();\n\t\tunset( $this->post_id );\n\t}\n\n\tpublic function test_papi_property_from_array_slugs() {\n\t\t$actual = papi_from_property_array_slugs( [\n\t\t\t'repeater' => 1,\n\t\t\t'repeater_0_image' => 1,\n\t\t\t'_repeater_0_image_property' => 'image'\n\t\t], 'repeater' );\n\n\t\t$expected = [\n\t\t\t[\n\t\t\t\t'image' => 1,\n\t\t\t\t'_image_property' => 'image'\n\t\t\t]\n\t\t];\n\n\t\t$this->assertSame( $expected, $actual );\n\n\t\t$this->assertEmpty( papi_from_property_array_slugs( [], 'custom_slug' ) );\n\t}\n\n\tpublic function test_papi_is_property() {\n\t\t$this->assertTrue( papi_is_property( new Papi_Property() ) );\n\t\t$this->assertTrue( papi_is_property( new Papi_Property_String() ) );\n\t\t$this->assertFalse( papi_is_property( (object) [] ) );\n\t\t$this->assertFalse( papi_is_property( [] ) );\n\t\t$this->assertFalse( papi_is_property( null ) );\n\t\t$this->assertFalse( papi_is_property( 1 ) );\n\t\t$this->assertFalse( papi_is_property( true ) );\n\t\t$this->assertFalse( papi_is_property( false ) );\n\t\t$this->assertFalse( papi_is_property( '' ) );\n\t}\n\n\tpublic function test_papi_get_options_and_properties() {\n\t\t$simple_box = PAPI_FIXTURE_DIR . '/page-types/boxes/simple.php';\n\n\t\t$options = papi_get_options_and_properties( $simple_box, [\n\t\t\t'test' => 'test'\n\t\t] );\n\n\t\t$this->assertSame( 'Simple', $options[0]['title'] );\n\t\t$this->assertSame( 'test', $options[0]['test'] );\n\t\t$this->assertSame( 'Name', $options[1][0]->title );\n\t\t$this->assertSame( 'string', $options[1][0]->type );\n\n\t\t$options = papi_get_options_and_properties( 'hello' );\n\t\t$this->assertSame( 'hello', $options[0]['title'] );\n\n\t\t$options = papi_get_options_and_properties( [\n\t\t\t'title' => 'Simple',\n\t\t\tpapi_property( [\n\t\t\t\t'type'  => 'string',\n\t\t\t\t'title' => 'Name'\n\t\t\t] )\n\t\t], [], false );\n\n\t\t$this->assertSame( 'Simple', $options[0]['title'] );\n\t\t$this->assertSame( 'Name', $options[1][0]->title );\n\t\t$this->assertSame( 'string', $options[1][0]->type );\n\n\t\t$options = papi_get_options_and_properties( [\n\t\t\t'title' => 'Simple'\n\t\t] );\n\n\t\t$this->assertSame( 'Simple', $options[0]['title'] );\n\n\t\t$options = papi_get_options_and_properties( [] );\n\n\t\t$this->assertEmpty( $options[0]['title'] );\n\n\t\t$options = papi_get_options_and_properties( [ papi_property( [\n\t\t\t'type'  => 'string',\n\t\t\t'title' => 'Name'\n\t\t] ) ] );\n\n\t\t$this->assertSame( 'Name', $options[0]['title'] );\n\t\t$this->assertSame( 'Name', $options[1][0]->title );\n\t\t$this->assertSame( 'string', $options[1][0]->type );\n\n\t\t$options = papi_get_options_and_properties( [ papi_property( [\n\t\t\t'type'     => 'string',\n\t\t\t'title'    => 'Name',\n\t\t\t'sidebar'  => false,\n\t\t\t'required' => true\n\t\t] ) ] );\n\n\t\t$this->assertSame( 'Name', $options[0]['title'] );\n\t\t$this->assertSame( 'Name', $options[1][0]->title );\n\t\t$this->assertSame( 'string', $options[1][0]->type );\n\n\t\t$options = papi_get_options_and_properties( [ (object) [\n\t\t\t'options' => [\n\t\t\t\t'title' => 'Name'\n\t\t\t]\n\t\t] ] );\n\n\t\t$this->assertSame( 'Name', $options[0]['title'] );\n\n\t\t$options = papi_get_options_and_properties( ['title' => 'Test', 'context' => 'side'] );\n\t\t$this->assertSame( ['title' => 'Test', 'context' => 'side'], $options[0] );\n\n\t\tlist( $options, $properties ) = papi_get_options_and_properties(\n\t\t\t[\n\t\t\t\t'title'      => 'Content',\n\t\t\t\t'properties' => [\n\t\t\t\t\tpapi_property( [\n\t\t\t\t\t\t'type'  => 'string',\n\t\t\t\t\t\t'title' => 'Name',\n\t\t\t\t\t] ),\n\t\t\t\t],\n\t\t\t]\n\t\t);\n\n\t\t$this->assertSame( [\n\t\t\t'title' => 'Content',\n\t\t], $options );\n\n\t\t$this->assertSame( 'Name', $properties[0]->title );\n\n\t\tlist( $options, $properties ) = papi_get_options_and_properties( PAPI_FIXTURE_DIR . '/page-types/boxes/big.php' );\n\n\t\t$this->assertSame( [\n\t\t\t'title' => 'Content',\n\t\t], $options );\n\n\t\t$this->assertSame( 'Name', $properties[0]->title );\n\n\t\tlist( $options, $properties ) = papi_get_options_and_properties(\n\t\t\t[\n\t\t\t\t'title' => 'Content',\n\t\t\t\t'props' => [\n\t\t\t\t\tpapi_property( [\n\t\t\t\t\t\t'type'  => 'string',\n\t\t\t\t\t\t'title' => 'Name',\n\t\t\t\t\t] ),\n\t\t\t\t],\n\t\t\t]\n\t\t);\n\n\t\t$this->assertSame( [\n\t\t\t'title' => 'Content',\n\t\t], $options );\n\n\t\t$this->assertSame( 'Name', $properties[0]->title );\n\t}\n\n\tpublic function test_papi_get_property_class_name() {\n\t\t$this->assertSame( 'Papi_Property_String', papi_get_property_class_name( 'PropertyString' ) );\n\t\t$this->assertSame( 'Papi_Property_String', papi_get_property_class_name( 'string' ) );\n\t\t$this->assertSame( 'Papi_Property_Fake', papi_get_property_class_name( 'fake' ) );\n\t\t$this->assertSame( 'Papi_Property_Test_Form_1', papi_get_property_class_name( 'test-form-1' ) );\n\t\t$this->assertNull( papi_get_property_class_name( null ) );\n\t\t$this->assertNull( papi_get_property_class_name( false ) );\n\t\t$this->assertNull( papi_get_property_class_name( true ) );\n\t\t$this->assertNull( papi_get_property_class_name( [] ) );\n\t\t$this->assertNull( papi_get_property_class_name( new stdClass() ) );\n\t\t$this->assertNull( papi_get_property_class_name( 1 ) );\n\t}\n\n\tpublic function test_papi_get_property_type() {\n\t\t$this->assertTrue( papi_get_property_type( 'string' ) instanceof Papi_Property_String );\n\t\t$this->assertFalse( papi_get_property_type( null ) instanceof Papi_Property_String );\n\t\t$this->assertFalse( papi_get_property_type( 'fake' ) instanceof Papi_Property_String );\n\t\t$this->assertFalse( papi_get_property_type( 1 ) instanceof Papi_Property_String );\n\t\t$this->assertFalse( papi_get_property_type( true ) instanceof Papi_Property_String );\n\t\t$this->assertFalse( papi_get_property_type( false ) instanceof Papi_Property_String );\n\t\t$this->assertFalse( papi_get_property_type( [] ) instanceof Papi_Property_String );\n\t\t$this->assertFalse( papi_get_property_type( new stdClass() ) instanceof Papi_Property_String );\n\t\t$options = Papi_Core_Property::factory( [\n\t\t\t'type' => 'string'\n\t\t] )->get_options();\n\t\t$this->assertTrue( papi_get_property_type( $options ) instanceof Papi_Property_String );\n\t}\n\n\tpublic function test_papi_get_property_type_custom() {\n\t\trequire_once PAPI_FIXTURE_DIR . '/properties/class-papi-property-kvack.php';\n\t\t$this->assertTrue( papi_get_property_type( 'kvack' ) instanceof Papi_Property_Kvack );\n\t}\n\n\tpublic function test_papi_get_property_type_key() {\n\t\t$this->assertSame( 'image_property', papi_get_property_type_key( 'image' ) );\n\t\t$this->assertSame( '_property', papi_get_property_type_key( null ) );\n\t\t$this->assertSame( '_property', papi_get_property_type_key( false ) );\n\t\t$this->assertSame( '_property', papi_get_property_type_key( true ) );\n\t\t$this->assertSame( '_property', papi_get_property_type_key( [] ) );\n\t\t$this->assertSame( '_property', papi_get_property_type_key( 1 ) );\n\t\t$this->assertSame( '_property', papi_get_property_type_key( new stdClass() ) );\n\t\t$this->assertSame( 'my_slug[0][key_property]', papi_get_property_type_key( 'my_slug[0][key]' ) );\n\t}\n\n\tpublic function test_papi_get_property_type_f() {\n\t\t$this->assertSame( '_image_property', papi_get_property_type_key_f( 'image' ) );\n\t\t$this->assertSame( '_property', papi_get_property_type_key_f( null ) );\n\t\t$this->assertSame( '_property', papi_get_property_type_key_f( true ) );\n\t\t$this->assertSame( '_property', papi_get_property_type_key_f( false ) );\n\t\t$this->assertSame( '_property', papi_get_property_type_key_f( 1 ) );\n\t\t$this->assertSame( '_property', papi_get_property_type_key_f( [] ) );\n\t\t$this->assertSame( '_property', papi_get_property_type_key_f( new stdClass() ) );\n\t}\n\n\tpublic function test_papi_is_property_type_key() {\n\t\t$this->assertTrue( papi_is_property_type_key( 'image_property' ) );\n\t\t$this->assertTrue( papi_is_property_type_key( '_image_property' ) );\n\t\t$this->assertFalse( papi_is_property_type_key( 'kvack' ) );\n\t\t$this->assertFalse( papi_is_property_type_key( 1 ) );\n\t\t$this->assertFalse( papi_is_property_type_key( false ) );\n\t\t$this->assertFalse( papi_is_property_type_key( true ) );\n\t}\n\n\tpublic function test_papi_render_property() {\n\t\t$property = papi_property( [\n\t\t\t'title' => 'Name'\n\t\t] );\n\t\t$this->assertEmpty( papi_render_property( $property ) );\n\n\t\t$property = papi_property( [\n\t\t\t'type'  => 'fake',\n\t\t\t'title' => 'Name'\n\t\t] );\n\n\t\t$this->assertEmpty( papi_render_property( $property ) );\n\n\t\t$user_id = $this->factory->user->create( ['role' => 'administrator'] );\n\t\twp_set_current_user( $user_id );\n\n\t\t$_GET = ['lang' => 'se'];\n\t\t$property = papi_property( [\n\t\t\t'type'  => 'string',\n\t\t\t'title' => 'Name',\n\t\t\t'lang'  => 'dk',\n\t\t\t'capabilities' => 'administrator'\n\t\t] );\n\t\t$this->assertEmpty( papi_render_property( $property ) );\n\t\t$this->expectOutputRegex( '//' );\n\t\tunset( $_GET );\n\n\t\t$user_id = $this->factory->user->create( [ 'role' => 'read' ] );\n\t\twp_set_current_user( $user_id );\n\t\t$property = papi_property( [\n\t\t\t'type'  => 'string',\n\t\t\t'title' => 'Name',\n\t\t\t'capabilities' => 'administrator'\n\t\t] );\n\t\t$this->assertEmpty( papi_render_property( $property ) );\n\t\t$this->expectOutputRegex( '//' );\n\t\twp_set_current_user( 0 );\n\t}\n\n\tpublic function test_papi_render_required_property() {\n\t\t$property = papi_property( [\n\t\t\t'slug'     => 'required_name',\n\t\t\t'type'     => 'string',\n\t\t\t'title'    => 'Name',\n\t\t\t'required' => true\n\t\t] );\n\t\tpapi_render_property( $property );\n\t\t$this->expectOutputRegex( '/class\\=\\\"papi\\-rq\\\"/' );\n\t}\n\n\tpublic function test_papi_render_properties() {\n\t\t$tab = papi_tab( 'Content', [] );\n\t\tpapi_render_properties( [ $tab ] );\n\t\t$this->expectOutputRegex( '/class\\=\\\"papi\\-tabs\\-wrapper\\\"/' );\n\n\t\t$property = papi_property( [\n\t\t\t'type'  => 'string',\n\t\t\t'title' => 'Name'\n\t\t] );\n\t\tpapi_render_properties( [ $property ] );\n\t\t$this->expectOutputRegex( '/table\\s*class\\=\\\"papi\\-table\\\"/' );\n\t}\n\n\tpublic function test_papi_require_text() {\n\t\t$this->assertEmpty( papi_property_require_text( null ) );\n\t\t$property = papi_property( [\n\t\t\t'type'     => 'string',\n\t\t\t'title'    => 'Name',\n\t\t\t'required' => true\n\t\t] );\n\t\t$this->assertSame( '(required field)', papi_property_require_text( $property ) );\n\t}\n\n\tpublic function test_papi_required_html() {\n\t\t$this->assertEmpty( papi_property_required_html( null ) );\n\t\t$property = papi_property( [\n\t\t\t'type'     => 'string',\n\t\t\t'title'    => 'Name',\n\t\t\t'required' => true\n\t\t] );\n\t\t$this->assertRegExp( '/class\\=\\\"papi\\-rq\\\"/', papi_property_required_html( $property ) );\n\t}\n\n\tpublic function test_papi_populate_properties() {\n\t\t$this->assertEmpty( papi_populate_properties( [] ) );\n\t\t$this->assertEmpty( papi_populate_properties( null ) );\n\t\t$this->assertEmpty( papi_populate_properties( true ) );\n\t\t$this->assertEmpty( papi_populate_properties( false ) );\n\t\t$this->assertEmpty( papi_populate_properties( 1 ) );\n\n\t\t$actual = papi_populate_properties( new stdClass() );\n\t\t$this->assertTrue( is_object( $actual[0] ) );\n\n\t\t$actual = papi_populate_properties( [\n\t\t\t'type'  => 'string',\n\t\t\t'title' => 'Name'\n\t\t] );\n\n\t\t$this->assertSame( 'Name', $actual[0]->title );\n\t\t$this->assertSame( 'string', $actual[0]->type );\n\n\t\t$this->assertSame( [ $actual[0] ], papi_populate_properties( $actual[0] ) );\n\n\t\t$actual = papi_populate_properties( [\n\t\t\t[\n\t\t\t\t'type'  => 'string',\n\t\t\t\t'title' => 'Name 1'\n\t\t\t],\n\t\t\t[\n\t\t\t\t'type'  => 'string',\n\t\t\t\t'title' => 'Name 2'\n\t\t\t]\n\t\t] );\n\n\t\t$this->assertSame( 'Name 1', $actual[0]->title );\n\t\t$this->assertSame( 'string', $actual[0]->type );\n\n\t\t$this->assertSame( 'Name 2', $actual[1]->title );\n\t\t$this->assertSame( 'string', $actual[1]->type );\n\n\t\t$tab = papi_tab( 'Content', [] );\n\t\t$actual = papi_populate_properties( [$tab] );\n\n\t\t$this->assertSame( 'Content', $actual[0]->title );\n\t\t$this->assertEmpty( $actual[0]->properties );\n\n\t\t$actual = papi_populate_properties( [\n\t\t\t[\n\t\t\t\t'type'  \t => 'string',\n\t\t\t\t'title' \t => 'Name 1',\n\t\t\t\t'sort_order' => 1\n\t\t\t],\n\t\t\t[\n\t\t\t\t'type'  \t => 'string',\n\t\t\t\t'title' \t => 'Name 3'\n\t\t\t],\n\t\t\t[\n\t\t\t\t'type'  \t => 'string',\n\t\t\t\t'title' \t => 'Name 2',\n\t\t\t\t'sort_order' => 0\n\t\t\t]\n\t\t] );\n\n\t\t$this->assertSame( 'Name 2', $actual[0]->title );\n\t\t$this->assertSame( 'string', $actual[0]->type );\n\n\t\t$this->assertSame( 'Name 1', $actual[1]->title );\n\t\t$this->assertSame( 'string', $actual[1]->type );\n\n\t\t$this->assertSame( 'Name 3', $actual[2]->title );\n\t\t$this->assertSame( 'string', $actual[2]->type );\n\t}\n\n\tpublic function test_papi_property() {\n\t\t$actual = papi_property( [\n\t\t\t'type'  => 'string',\n\t\t\t'title' => 'Name'\n\t\t] );\n\n\t\t$this->assertSame( 'Name', $actual->title );\n\t\t$this->assertSame( 'string', $actual->type );\n\n\t\t$this->assertNull( papi_property( [] ) );\n\t\t$this->assertNull( papi_property( null ) );\n\t\t$this->assertNull( papi_property( true ) );\n\t\t$this->assertNull( papi_property( false ) );\n\t\t$this->assertNull( papi_property( 1 ) );\n\t}\n\n\tpublic function test_papi_property_template() {\n\t\t$actual = papi_property( PAPI_FIXTURE_DIR . '/properties/simple.php' );\n\n\t\t$this->assertSame( 'Name', $actual->title );\n\t\t$this->assertSame( 'string', $actual->type );\n\t}\n\n\tpublic function test_papi_property_template_in_page_type() {\n\t\tadd_filter( 'papi/settings/directories', function () {\n\t\t\treturn [1,  PAPI_FIXTURE_DIR . '/page-types'];\n\t\t} );\n\n\t\t$page_type  = papi_get_entry_type_by_id( 'name-page-type' );\n\t\t$boxes      = $page_type->get_boxes();\n\t\t$properties = $boxes[0]->properties;\n\t\t$this->assertSame( 'papi_my_name_is', $properties[0]->get_slug() );\n\t}\n\n\tpublic function test_papi_property_to_array_slugs() {\n\t\t$actual = papi_property_to_array_slugs( [\n\t\t\t[\n\t\t\t\t'image' => 1,\n\t\t\t\t'image_property' => 'image',\n\t\t\t\t0 => false\n\t\t\t]\n\t\t], 'repeater' );\n\n\t\t$expected = [\n\t\t\t'repeater' => 1,\n\t\t\t'repeater_0_image' => 1,\n\t\t\t'_repeater_0_image_property' => 'image'\n\t\t];\n\n\t\t$this->assertEquals( $expected, $actual );\n\n\t\t$actual = papi_property_to_array_slugs( [1, '', true, false], 'repeater' );\n\n\t\t$expected = [\n\t\t\t'repeater' => 0\n\t\t];\n\n\t\t$this->assertEquals( $expected, $actual );\n\t}\n}\n"
  },
  {
    "path": "tests/cases/lib/core/slug-test.php",
    "content": "<?php\n\nclass Papi_Lib_Core_Slug_Test extends WP_UnitTestCase {\n\n\tpublic function setUp() {\n\t\tparent::setUp();\n\t\t$_GET  = [];\n\t\t$_POST = [];\n\t}\n\n\tpublic function tearDown() {\n\t\tparent::tearDown();\n\t\t$_GET = [];\n\t\t$_POST = [];\n\t}\n\n\tpublic function test_papify() {\n\t\t$this->assertSame( 'papi_hello_world', papify( 'hello_world' ) );\n\t\t$this->assertSame( 'papi_hello_world', papify( 'papi_hello_world' ) );\n\t\t$this->assertSame( 'papi_hello_world', papify( '_hello_world' ) );\n\t\t$this->assertEmpty( papify( null ) );\n\t\t$this->assertEmpty( papify( true ) );\n\t\t$this->assertEmpty( papify( false ) );\n\t\t$this->assertEmpty( papify( 1 ) );\n\t\t$this->assertEmpty( papify( [] ) );\n\t\t$this->assertEmpty( papify( new stdClass ) );\n\t}\n\n\tpublic function test_unpapify() {\n\t\t$this->assertSame( 'hello-world', unpapify( 'papi-hello-world' ) );\n\t\t$this->assertEmpty( unpapify( null ) );\n\t\t$this->assertEmpty( unpapify( true ) );\n\t\t$this->assertEmpty( unpapify( false ) );\n\t\t$this->assertEmpty( unpapify( 1 ) );\n\t\t$this->assertEmpty( unpapify( [] ) );\n\t\t$this->assertEmpty( unpapify( new stdClass() ) );\n\t}\n}\n"
  },
  {
    "path": "tests/cases/lib/core/tabs-test.php",
    "content": "<?php\n\nclass Papi_Lib_Core_Tabs_Test extends WP_UnitTestCase {\n\n\t/**\n\t * Test papi_tabs_setup.\n\t */\n\tpublic function test_papi_tabs_setup() {\n\t\t$tab  = papi_tab( 'Content' );\n\t\t$tabs = papi_tabs_setup( [$tab] );\n\n\t\t$this->assertSame( 'Content', $tabs[0]->title );\n\t\t$this->assertSame( 1000, $tabs[0]->sort_order );\n\n\t\t$tabs = papi_tabs_setup( [1] );\n\t\t$this->assertEmpty( $tabs );\n\n\t\t$tabs = papi_tabs_setup( [] );\n\t\t$this->assertEmpty( $tabs );\n\t}\n\n\t/**\n\t * Test papi_tab.\n\t */\n\tpublic function test_papi_tab() {\n\t\t$actual = papi_tab( 'Content', [\n\t\t\tpapi_property( [\n\t\t\t\t'type'  => 'string',\n\t\t\t\t'title' => 'Name'\n\t\t\t] )\n\t\t] );\n\n\t\t$this->assertTrue( $actual->tab );\n\t\t$this->assertSame( 'Content', $actual->title );\n\t\t$this->assertSame( 'Name', $actual->properties[0]->title );\n\t\t$this->assertSame( 'string', $actual->properties[0]->type );\n\t}\n}\n"
  },
  {
    "path": "tests/cases/lib/core/taxonomy-test.php",
    "content": "<?php\n\nclass Papi_Lib_Core_Taxonomy_Test extends WP_UnitTestCase {\n\n\tpublic function test_papi_get_term_id() {\n\t\t$this->assertSame( 1, papi_get_term_id( 1 ) );\n\t\t$this->assertSame( 1, papi_get_term_id( '1' ) );\n\n\t\t$term_id = $this->factory->term->create();\n\n\t\t$term = get_term( $term_id );\n\t\t$this->assertSame( $term_id, papi_get_term_id( $term ) );\n\n\t\t$_GET['term_id'] = $term_id;\n\t\t$this->assertSame( $term_id, papi_get_term_id() );\n\t\t$this->assertSame( $term_id, papi_get_term_id( null ) );\n\t\tunset( $_GET['term_id'] );\n\n\t\t$_GET['tag_ID'] = $term_id;\n\t\t$this->assertSame( $term_id, papi_get_term_id() );\n\t\t$this->assertSame( $term_id, papi_get_term_id( null ) );\n\t\tunset( $_GET['tag_ID'] );\n\t}\n\n\tpublic function test_papi_get_term_id_wp_query() {\n\t\t$term_id = $this->factory->term->create( ['slug' => 'category_test'] );\n\t\t$term = get_term_by( 'slug', 'category_test', 'post_tag' );\n\t\t$this->go_to( get_term_link( $term ) );\n\t\t$this->assertSame( $term_id, papi_get_term_id() );\n\t}\n\n\tpublic function test_papi_get_taxonomy() {\n\t\t$this->assertSame( '', papi_get_taxonomy() );\n\n\t\t$_GET['taxonomy'] = 'post_tag';\n\t\t$this->assertSame( 'post_tag', papi_get_taxonomy() );\n\t\t$this->assertSame( 'post_tag', papi_get_taxonomy( null ) );\n\t\tunset( $_GET['taxonomy'] );\n\n\t\t$_GET['term_id'] = $this->factory->term->create();\n\t\t$this->assertSame( get_term( $_GET['term_id'] )->taxonomy, papi_get_taxonomy() );\n\t\tunset( $_GET['term_id'] );\n\t}\n\n\tpublic function test_papi_get_taxonomy_label() {\n\t\t$this->assertSame( 'Categories', papi_get_taxonomy_label( 'category', 'name' ) );\n\t\t$this->assertSame( 'Fake', papi_get_taxonomy_label( 'Fake', 'name', 'Fake' ) );\n\t}\n}\n"
  },
  {
    "path": "tests/cases/lib/core/template-test.php",
    "content": "<?php\n\nclass Papi_Lib_Core_Template_Test extends WP_UnitTestCase {\n\n\tpublic function setUp() {\n\t\tparent::setUp();\n\t\t$this->post_id = $this->factory->post->create( [\n\t\t\t'post_type' => 'page'\n\t\t] );\n\t}\n\n\tpublic function tearDown() {\n\t\tparent::tearDown();\n\t\tunset( $this->post_id );\n\t}\n\n\tpublic function test_papi_body_class() {\n\t\tglobal $post;\n\n\t\t$this->assertEmpty( papi_body_class( [] ) );\n\n\t\t$post = get_post( $this->post_id );\n\t\t$this->go_to( get_permalink( $this->post_id ) );\n\t\t$this->assertEmpty( papi_body_class( [] ) );\n\n\t\tupdate_post_meta( $this->post_id, papi_get_page_type_key(), '/' );\n\t\t$this->assertEmpty( papi_body_class( [] ) );\n\n\t\tadd_filter( 'papi/settings/directories', function () {\n\t\t\treturn [1,  PAPI_FIXTURE_DIR . '/page-types'];\n\t\t} );\n\n\t\tupdate_post_meta( $this->post_id, papi_get_page_type_key(), 'dot-page-type' );\n\t\t$this->assertSame( ['custom-css-class', 'dot-page-type'], papi_body_class( [] ) );\n\t}\n\n\tpublic function test_papi_get_template_file_name() {\n\t\t$this->assertNull( papi_get_template_file_name( '' ) );\n\t\t$this->assertNull( papi_get_template_file_name( true ) );\n\t\t$this->assertNull( papi_get_template_file_name( false ) );\n\t\t$this->assertNull( papi_get_template_file_name( null ) );\n\t\t$this->assertNull( papi_get_template_file_name( 1 ) );\n\t\t$this->assertNull( papi_get_template_file_name( 0 ) );\n\t\t$this->assertNull( papi_get_template_file_name( '' ) );\n\t\t$this->assertNull( papi_get_template_file_name( [] ) );\n\t\t$this->assertNull( papi_get_template_file_name( (object) [] ) );\n\n\t\t$this->assertSame( 'hello.world.php', papi_get_template_file_name( 'hello.world' ) );\n\t\t$this->assertSame( 'hello/world.php', papi_get_template_file_name( 'hello/world' ) );\n\t\t$this->assertSame( 'hello/world.php', papi_get_template_file_name( 'hello/world.php' ) );\n\t}\n\n\tpublic function test_papi_include_template() {\n\t\t$this->assertEmpty( papi_include_template( '' ) );\n\t\t$this->assertEmpty( papi_include_template( 1 ) );\n\t\t$this->assertEmpty( papi_include_template( null ) );\n\t\t$this->assertEmpty( papi_include_template( [] ) );\n\t\t$this->assertEmpty( papi_include_template( new stdClass ) );\n\t\t$this->assertEmpty( papi_include_template( true ) );\n\t\t$this->assertEmpty( papi_include_template( false ) );\n\t\t$this->assertEmpty( papi_include_template( 'path/to/fake/file.php' ) );\n\n\t\tpapi_include_template( 'admin/views/add-new-page.php' );\n\t\t$this->expectOutputRegex( '/Add New Page/' );\n\t}\n\n\tpublic function test_papi_template() {\n\t\t$template = papi_template( PAPI_FIXTURE_DIR . '/properties/simple.php', [\n\t\t\t'lang' => 'se'\n\t\t] );\n\n\t\t$this->assertSame( 'Name', $template->title );\n\t\t$this->assertSame( 'Name', $template->get_option( 'title' ) );\n\t\t$this->assertSame( 'string', $template->type );\n\t\t$this->assertSame( 'string', $template->get_option( 'type' ) );\n\t\t$this->assertSame( 'se', $template->lang );\n\t\t$this->assertSame( 'se', $template->get_option( 'lang' ) );\n\n\t\t$this->assertEmpty( papi_template( '' ) );\n\t\t$this->assertEmpty( papi_template( null ) );\n\t\t$this->assertEmpty( papi_template( true ) );\n\t\t$this->assertEmpty( papi_template( false ) );\n\t\t$this->assertEmpty( papi_template( 1 ) );\n\t\t$this->assertEmpty( papi_template( [] ) );\n\t\t$this->assertEmpty( papi_template( new stdClass() ) );\n\t\t$this->assertEmpty( papi_template( PAPI_FIXTURE_DIR ) );\n\n\t\t$template = papi_template( PAPI_FIXTURE_DIR . '/properties/array.php', [], true );\n\n\t\t$this->assertSame( 'Name', $template->title );\n\t\t$this->assertSame( 'string', $template->type );\n\n\t\t$this->assertEmpty( papi_template( 'hello' ) );\n\t}\n\n\tpublic function test_papi_template_include() {\n\t\tglobal $post;\n\n\t\t$this->assertEmpty( apply_filters( 'template_include', '' ) );\n\n\t\t$post = get_post( $this->post_id );\n\t\t$this->go_to( get_permalink( $this->post_id ) );\n\t\t$this->assertEmpty( apply_filters( 'template_include', '' ) );\n\n\t\tadd_filter( 'papi/settings/directories', function () {\n\t\t\treturn [1,  PAPI_FIXTURE_DIR . '/page-types'];\n\t\t} );\n\n\t\tupdate_post_meta( $this->post_id, papi_get_page_type_key(), 'simple-page-type' );\n\t\t$this->assertEmpty( apply_filters( 'template_include', '' ) );\n\n\t\tupdate_post_meta( $this->post_id, papi_get_page_type_key(), 'twenty-page-type' );\n\t\t$this->flush_cache();\n\n\t\t$path = get_template_directory();\n\t\t$path = trailingslashit( $path );\n\t\t$path = apply_filters( 'template_include', '' );\n\t\t$this->assertNotFalse( strpos( $path, 'functions.php' ) );\n\t}\n\n\tpublic function test_filter_papi_template_include() {\n\t\tglobal $post;\n\n\t\t$this->assertEmpty( apply_filters( 'papi/template_include', '' ) );\n\n\t\t$post = get_post( $this->post_id );\n\t\t$this->go_to( get_permalink( $this->post_id ) );\n\n\t\tadd_filter( 'papi/settings/directories', function () {\n\t\t\treturn [1,  PAPI_FIXTURE_DIR . '/page-types'];\n\t\t} );\n\n\t\tadd_filter( 'papi/template_include', function () {\n\t\t\treturn 'purus-risus-adipiscing-pharetramollis.php';\n\t\t} );\n\n\t\tupdate_post_meta( $this->post_id, papi_get_page_type_key(), 'simple-page-type' );\n\t\t$this->assertEmpty( apply_filters( 'template_include', '' ) );\n\n\t\tupdate_post_meta( $this->post_id, papi_get_page_type_key(), 'twenty-page-type' );\n\t\t$this->flush_cache();\n\n\t\t$this->assertSame( 'purus-risus-adipiscing-pharetramollis.php', apply_filters( 'template_include', '' ) );\n\t}\n\n\tpublic function test_filter_papi_pre_template_include() {\n\t\tglobal $post;\n\n\t\t$this->assertEmpty( apply_filters( 'papi/pre_template_include', '' ) );\n\n\t\t$post = get_post( $this->post_id );\n\t\t$this->go_to( get_permalink( $this->post_id ) );\n\n\t\tadd_filter( 'papi/settings/directories', function () {\n\t\t\treturn [1,  PAPI_FIXTURE_DIR . '/page-types'];\n\t\t} );\n\n\t\tadd_filter( 'papi/pre_template_include', function () {\n\t\t\treturn 'pre-template.php';\n\t\t} );\n\n\t\tupdate_post_meta( $this->post_id, papi_get_page_type_key(), 'simple-page-type' );\n\t\t$this->assertSame( 'pre-template.php', apply_filters( 'template_include', '' ) );\n\t}\n}\n"
  },
  {
    "path": "tests/cases/lib/core/url-test.php",
    "content": "<?php\n\nclass Papi_Lib_Core_Url_Test extends WP_UnitTestCase {\n\n\tpublic function setUp() {\n\t\tparent::setUp();\n\t\t$this->post_id = $this->factory->post->create();\n\t}\n\n\tpublic function tearDown() {\n\t\tparent::tearDown();\n\t\tunset( $this->post_id );\n\t}\n\n\tpublic function test_papi_get_page_new_url() {\n\t\t$_SERVER['REQUEST_URI'] = '';\n\t\t$url = papi_get_page_new_url( 'page', true, 'page' );\n\t\t$this->assertNotFalse( strpos( $url, 'page_type=page&post_type=page' ) );\n\n\t\t$url = papi_get_page_new_url( 'page', true, 'page', ['post_type'] );\n\t\t$this->assertNotFalse( strpos( $url, 'page_type=page&post_type=page' ) );\n\t}\n\n\tpublic function test_papi_append_post_type_query() {\n\t\tglobal $post;\n\n\t\t$url = papi_append_post_type_query( 'http://wordpress/?post_parent=1', 'post' );\n\t\t$this->assertSame( 'http://wordpress/?post_parent=1&post_type=post', $url );\n\n\t\t$post = get_post( $this->post_id );\n\t\t$url = papi_append_post_type_query( 'http://wordpress/?post_parent=1' );\n\t\t$this->assertSame( 'http://wordpress/?post_parent=1&post_type=post', $url );\n\n\t\t$url = papi_append_post_type_query( 'http://wordpress/?post_parent=1&post_type=post' );\n\t\t$this->assertSame( 'http://wordpress/?post_parent=1&post_type=post', $url );\n\n\t\t$_GET['post_type'] = 'faq';\n\t\t$url = papi_append_post_type_query( 'http://wordpress/?post_parent=1', 'post' );\n\t\t$this->assertSame( 'http://wordpress/?post_parent=1&post_type=post', $url );\n\t\tunset( $_GET['post_type'] );\n\t}\n\n\tpublic function test_papi_append_post_type_query_fail() {\n\t\t$url = papi_append_post_type_query( 'http://wordpress/?post_parent=1' );\n\t\t$this->assertSame( 'http://wordpress/?post_parent=1&post_type=post', $url );\n\t}\n\n\tpublic function test_papi_include_query_strings() {\n\t\t$this->assertEmpty( papi_include_query_strings() );\n\n\t\t$old_get = $_GET;\n\n\t\t$_GET = [\n\t\t\t'foo' => 'bar',\n\t\t\t'baz' => 'boom',\n\t\t\t'cow' => 'milk',\n\t\t\t'php' => 'hypertext processor'\n\t\t];\n\n\t\t$this->assertEmpty( papi_include_query_strings( '?' ) );\n\n\t\t$this->assertEmpty( papi_include_query_strings( '?', ['missing'] ) );\n\n\t\t$this->assertSame( '?foo=bar', papi_include_query_strings( '?', ['foo'] ) );\n\n\t\t$this->assertSame( '&baz=boom', papi_include_query_strings( '&', ['baz'] ) );\n\n\t\t$this->assertSame( '?foo=bar&baz=boom', papi_include_query_strings( '?', ['foo', 'baz'] ) );\n\n\t\t$this->assertSame( '?php=hypertext+processor', papi_include_query_strings( '?', ['php'] ) );\n\n\t\t$_GET = $old_get;\n\t}\n}\n"
  },
  {
    "path": "tests/cases/lib/core/utilities-test.php",
    "content": "<?php\n\nclass Papi_Lib_Core_Utilities_Test extends WP_UnitTestCase {\n\n\tpublic function setUp() {\n\t\tparent::setUp();\n\t\t$_GET  = [];\n\t\t$_POST = [];\n\t}\n\n\tpublic function tearDown() {\n\t\tparent::tearDown();\n\t\t$_GET = [];\n\t\t$_POST = [];\n\t}\n\n\tpublic function test_papi_camel_case() {\n\t\t$this->assertSame( 'fooBar', papi_camel_case( 'foo-bar' ) );\n\t\t$this->assertSame( 'fooBar', papi_camel_case( 'foo bar' ) );\n\t\t$this->assertSame( 'fooBar', papi_camel_case( 'foo_bar' ) );\n\t\t$this->assertNull( papi_camel_case( null ) );\n\t}\n\n\tpublic function test_papi_convert_to_string() {\n\t\t$this->assertSame( 'false', papi_convert_to_string( false ) );\n\t\t$this->assertSame( 'true', papi_convert_to_string( true ) );\n\t\t$this->assertSame( '0.1', papi_convert_to_string( 0.1 ) );\n\t\t$this->assertSame( '1.1', papi_convert_to_string( 1.1 ) );\n\t\t$this->assertSame( '0', papi_convert_to_string( 0 ) );\n\t\t$this->assertEmpty( papi_convert_to_string( [] ) );\n\t\t$this->assertEmpty( papi_convert_to_string( new stdClass() ) );\n\t\t$this->assertNotEmpty( papi_convert_to_string( new ReflectionClass( 'ReflectionClass' ) ) );\n\t\t$this->assertEmpty( papi_convert_to_string( Papi_Loader::instance() ) );\n\t}\n\n\tpublic function test_papi_current_user_is_allowed() {\n\t\t$this->assertTrue( papi_current_user_is_allowed( null ) );\n\t\t$this->assertTrue( papi_current_user_is_allowed( true ) );\n\t\t$this->assertTrue( papi_current_user_is_allowed( false ) );\n\t\t$this->assertTrue( papi_current_user_is_allowed( 1 ) );\n\t\t$this->assertTrue( papi_current_user_is_allowed( new stdClass() ) );\n\t\t$this->assertTrue( papi_current_user_is_allowed( [] ) );\n\t\t$this->assertTrue( papi_current_user_is_allowed( '' ) );\n\t\t$this->assertTrue( papi_current_user_is_allowed() );\n\n\t\t$user_id = $this->factory->user->create( [ 'role' => 'administrator' ] );\n\t\twp_set_current_user( $user_id );\n\t\t$this->assertTrue( papi_current_user_is_allowed( 'administrator' ) );\n\t\t$this->assertFalse( papi_current_user_is_allowed( 'administrator2' ) );\n\t}\n\n\tpublic function test_papi_doing_ajax() {\n\t\t$out = papi_doing_ajax();\n\n\t\tif ( $out ) {\n\t\t\t$this->assertTrue( $out );\n\t\t} else {\n\t\t\t$this->assertFalse( $out );\n\t\t}\n\t}\n\n\tpublic function test_papi_esc_html() {\n\t\t$this->assertSame( '&lt;script&gt;alert(1);&lt;/script&gt;', papi_esc_html( '<script>alert(1);</script>' ) );\n\t\t$this->assertSame( [ '&lt;script&gt;alert(1);&lt;/script&gt;' => 'hello world&lt;script&gt;alert(1);&lt;/script&gt;' ], papi_esc_html( [ '<script>alert(1);</script>' => 'hello world<script>alert(1);</script>' ] ) );\n\n\t\t$obj = new stdClass;\n\t\t$obj->title = '<script>alert(1);</script>';\n\t\t$obj2 = new stdClass;\n\t\t$obj2->title = '&lt;script&gt;alert(1);&lt;/script&gt;';\n\t\t$this->assertEquals( $obj2, papi_esc_html( $obj ) );\n\n\t\t$obj = new stdClass;\n\t\t$obj->html = '<br />';\n\t\t$obj->name = '<p>name</p>';\n\t\t$obj2 = new stdClass;\n\t\t$obj2->html = '<br />';\n\t\t$obj2->name = '&lt;p&gt;name&lt;/p&gt;';\n\t\t$this->assertEquals( $obj2, papi_esc_html( $obj, ['html'] ) );\n\n\t\t$this->assertSame( [1], papi_esc_html( [1] ) );\n\t}\n\n\tpublic function test_papi_f() {\n\t\t$this->assertSame( '_page', papi_f( 'page' ) );\n\t\t$this->assertSame( '_page', papi_f( '_page' ) );\n\t\t$this->assertEmpty( papi_f( null ) );\n\t\t$this->assertEmpty( papi_f( true ) );\n\t\t$this->assertEmpty( papi_f( false ) );\n\t\t$this->assertEmpty( papi_f( 1 ) );\n\t\t$this->assertEmpty( papi_f( [] ) );\n\t\t$this->assertEmpty( papi_f( new stdClass() ) );\n\t\t$this->assertSame( '__page', papi_f( 'page', 2 ) );\n\t\t$this->assertSame( '__page', papi_f( '_page', 2 ) );\n\t\t$this->assertSame( '__page', papi_f( '__page', 2 ) );\n\t}\n\n\tpublic function test_papi_maybe_get_callable_value() {\n\t\t$this->assertSame( 'Hello', papi_maybe_get_callable_value( 'say_hello_stub' ) );\n\t\t$this->assertSame( 'file', papi_maybe_get_callable_value( 'file' ) );\n\t\t$this->assertSame( false, papi_maybe_get_callable_value( false ) );\n\t\t$this->assertSame( 'Hello Fredrik', papi_maybe_get_callable_value( 'say_hello_name_stub', ['Fredrik'] ) );\n\t\t$this->assertSame( 'Hello Fredrik', papi_maybe_get_callable_value( 'say_hello_name_stub', 'Fredrik' ) );\n\t}\n\n\tpublic function test_papi_get_class_name() {\n\t\t$actual = papi_get_class_name( PAPI_FIXTURE_DIR . '/page-types/simple-page-type.php' );\n\t\t$this->assertSame( 'Simple_Page_Type', $actual );\n\t\t$this->assertEmpty( papi_get_class_name( null ) );\n\t\t$this->assertEmpty( papi_get_class_name( true ) );\n\t\t$this->assertEmpty( papi_get_class_name( false ) );\n\t\t$this->assertEmpty( papi_get_class_name( 1 ) );\n\t\t$this->assertEmpty( papi_get_class_name( [] ) );\n\t\t$this->assertEmpty( papi_get_class_name( new stdClass() ) );\n\t\t$actual = papi_get_class_name( PAPI_FIXTURE_DIR . '/page-types/namespace-page-type.php' );\n\t\t$this->assertSame( '\\Foo\\Bar\\Namespace_Page_Type', $actual );\n\t}\n\n\tpublic function test_papi_get_lang() {\n\t\t$lang = papi_get_lang();\n\t\t$this->assertEmpty( $lang );\n\n\t\t$_GET['lang'] = 'sv';\n\t\t$lang = papi_get_lang();\n\t\t$this->assertSame( 'sv', $lang );\n\n\t\tadd_filter( 'papi/lang', function () {\n\t\t\treturn 'en';\n\t\t} );\n\n\t\t$lang = papi_get_lang();\n\t\t$this->assertSame( 'en', $lang );\n\n\t\tunset( $_GET['lang'] );\n\t}\n\n\tpublic function test_papi_get_only_objects() {\n\t\t$actual = true;\n\n\t\t$items = papi_get_only_objects( [ 1, 3, new stdClass, 'hej', [] ] );\n\n\t\tforeach ( $items as $item ) {\n\t\t\t$actual = is_object( $item );\n\n\t\t\tif ( ! $actual ) {\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\n\t\t$this->assertTrue( $actual );\n\t}\n\n\tpublic function test_papi_get_or_post() {\n\t\t$_GET['world'] = 'hello';\n\n\t\t$this->assertSame( 'hello', papi_get_or_post( 'world' ) );\n\t\tunset( $_GET['world'] );\n\n\t\t$_POST['world'] = 'hello';\n\n\t\t$this->assertSame( 'hello', papi_get_or_post( 'world' ) );\n\t\tunset( $_POST['world'] );\n\n\t\t$this->assertEmpty( papi_get_or_post( null ) );\n\t\t$this->assertEmpty( papi_get_or_post( true ) );\n\t\t$this->assertEmpty( papi_get_or_post( false ) );\n\t\t$this->assertEmpty( papi_get_or_post( 1 ) );\n\t\t$this->assertEmpty( papi_get_or_post( [] ) );\n\t\t$this->assertEmpty( papi_get_or_post( new stdClass() ) );\n\t\t$this->assertEMpty( papi_get_or_post( 'trams' ) );\n\t}\n\n\tpublic function test_papi_get_qs() {\n\t\t$_GET['page'] = 1;\n\n\t\t$this->assertSame( 1, papi_get_qs( 'page' ) );\n\t\tunset( $_GET['page'] );\n\n        $_GET['page'] = 'papi/option/options/header-option-type';\n\t\t$this->assertSame( 'options/header-option-type', papi_get_qs( 'page' ) );\n\t\tunset( $_GET['page'] );\n\n\t\t$this->assertEmpty( papi_get_qs( null ) );\n\t\t$this->assertEmpty( papi_get_qs( true ) );\n\t\t$this->assertEmpty( papi_get_qs( false ) );\n\t\t$this->assertEmpty( papi_get_qs( 1 ) );\n\t\t$this->assertEmpty( papi_get_qs( [] ) );\n\t\t$this->assertEmpty( papi_get_qs( new stdClass() ) );\n\t\t$this->assertEmpty( papi_get_qs( '' ) );\n\n\t\t$_GET['good'] = 'true';\n\t\t$_GET['bad'] = 'false';\n\t\t$qs = papi_get_qs( [ 'good', 'bad' ], true );\n\t\t$qs2 = [ 'good' => true, 'bad' => false ];\n\t\t$this->assertSame( $qs, $qs2 );\n\t}\n\n\tpublic function test_papi_get_sanitized_post() {\n\t\t$this->assertEmpty( papi_get_sanitized_post( 'hello' ) );\n\t\t$_POST['hello'] = '<';\n\t\t$this->assertSame( '&lt;', papi_get_sanitized_post( 'hello' ) );\n\t\tunset( $_POST['hello'] );\n\t\t$_POST['tag'] = '<tag>';\n\t\t$this->assertEmpty( papi_get_sanitized_post( 'tag' ) );\n\t\tunset( $_POST['tag'] );\n\t}\n\n\tpublic function test_papi_html_name() {\n\t\t$this->assertSame( 'papi_hello_world_aao', papi_html_name( 'hello world åäö' ) );\n\t\t$this->assertSame( 'papi_hello', papi_html_name( 'papi_hello' ) );\n\t\t$this->assertSame( '_papi_hello', papi_html_name( '_papi_hello' ) );\n\t\t$this->assertSame( 'papi_hello[0][image]', papi_html_name( 'papi_hello[0][image]' ) );\n\t\t$this->assertSame( 'papi_hello[image]', papi_html_name( 'papi_hello[image]' ) );\n\t\t$this->assertEmpty( papi_html_name( null ) );\n\t\t$this->assertEmpty( papi_html_name( true ) );\n\t\t$this->assertEmpty( papi_html_name( false ) );\n\t\t$this->assertEmpty( papi_html_name( 1 ) );\n\t\t$this->assertEmpty( papi_html_name( [] ) );\n\t\t$this->assertEmpty( papi_html_name( new stdClass() ) );\n\t}\n\n\tpublic function test_papi_html_tag() {\n\t\t$this->assertSame( '<label for=\"test\">hello</label>', papi_html_tag( 'label', [\n\t\t\t'for' => 'test',\n\t\t\t'hello'\n\t\t] ) );\n\n\t\t$this->assertSame( '<label for=\"{}\">hello</label>', papi_html_tag( 'label', [\n\t\t\t'for' => (object) [],\n\t\t\t'hello'\n\t\t] ) );\n\n\t\t$this->assertSame( '<label for=\"true\">hello</label>', papi_html_tag( 'label', [\n\t\t\t'for' => 'true',\n\t\t\t'hello'\n\t\t] ) );\n\n\t\t$this->assertSame( '<label for=\"false\">hello</label>', papi_html_tag( 'label', [\n\t\t\t'for' => false,\n\t\t\t'hello'\n\t\t] ) );\n\n\t\t$this->assertSame( '<label for=\"{}\">hello world</label>', papi_html_tag( 'label', [\n\t\t\t'for' => (object) [],\n\t\t\t['hello', 'world']\n\t\t] ) );\n\n\t\t$this->assertSame( '<label for=\"{}\">', papi_html_tag( 'label', [\n\t\t\t'for' => (object) []\n\t\t] ) );\n\n\t\t$this->assertSame( '<label>Hello</label>', papi_html_tag( 'label', 'Hello' ) );\n\n\t\t$this->assertSame( '<label>Hello</label>', papi_html_tag( 'label', papi_maybe_get_callable_value( 'say_hello_stub' ) ) );\n\n\t\t$this->assertSame( '<option selected=\"selected\">hello</option>', papi_html_tag( 'option', [\n\t\t\t'selected' => true,\n\t\t\t'hello'\n\t\t] ) );\n\n\t\t$this->assertSame( '<input type=\"checkbox\" checked=\"checked\">', papi_html_tag( 'input', [\n\t\t\t'type'    => 'checkbox',\n\t\t\t'checked' => true\n\t\t] ) );\n\n\t\t$this->assertSame( '<textarea></textarea>', papi_html_tag( 'textarea', '' ) );\n\t}\n\n\tpublic function test_papi_render_html_tag() {\n\t\tpapi_render_html_tag( 'label', [\n\t\t\t'for' => 'test',\n\t\t\t'hello'\n\t\t] );\n\n\t\t$this->expectOutputRegex( '/\\<label for\\=\\\"test\\\"\\>hello\\<\\/label\\>/' );\n\n\t\tpapi_render_html_tag( 'label', [\n\t\t\t'for' => (object) [],\n\t\t\t'hello'\n\t\t] );\n\n\t\t$this->expectOutputRegex( '/\\<label for\\=\\\"\\{\\}\\\"\\>hello\\<\\/label\\>/' );\n\n\t\tpapi_html_tag( 'label', [\n\t\t\t'for' => 'true',\n\t\t\t'hello'\n\t\t] );\n\n\t\t$this->expectOutputRegex( '/\\<label for\\=\\\"true\\\"\\>hello\\<\\/label\\>/' );\n\n\t\tpapi_render_html_tag( 'label', [\n\t\t\t'for' => false,\n\t\t\t'hello'\n\t\t] );\n\n\t\t$this->expectOutputRegex( '/\\<label for\\=\\\"false\\\"\\>hello\\<\\/label\\>/' );\n\n\t\tpapi_render_html_tag( 'label', [\n\t\t\t'for' => (object) [],\n\t\t\t['hello', 'world']\n\t\t] );\n\n\t\t$this->expectOutputRegex( '/\\<label for\\=\\\"\\{\\}\\\"\\>hello world\\<\\/label\\>/' );\n\n\t\tpapi_render_html_tag( 'label', [\n\t\t\t'for' => (object) [],\n\t\t\tpapi_maybe_get_callable_value( 'say_hello_stub' )\n\t\t] );\n\n\t\t$this->expectOutputRegex( '/\\<label for\\=\\\"\\{\\}\\\"\\>Hello\\<\\/label\\>/' );\n\t}\n\n\tpublic function test_papi_is_empty() {\n\t\t$this->assertTrue( papi_is_empty( [null, null] ) );\n\t\t$this->assertFalse( papi_is_empty( [4, null] ) );\n\t\t$this->assertTrue( papi_is_empty( null ) );\n\t\t$this->assertTrue( papi_is_empty( '' ) );\n\t\t$this->assertFalse( papi_is_empty( 'false' ) );\n\t\t$this->assertFalse( papi_is_empty( true ) );\n\t\t$this->assertFalse( papi_is_empty( false ) );\n\t\t$this->assertFalse( papi_is_empty( 0 ) );\n\t\t$this->assertFalse( papi_is_empty( 0.0 ) );\n\t\t$this->assertFalse( papi_is_empty( '0' ) );\n\t}\n\n\tpublic function test_papi_is_json() {\n\t\t$this->assertTrue( papi_is_json( '{\"yes\":true}' ) );\n\t\t$this->assertTrue( papi_is_json( '{}' ) );\n\t\t$this->assertTrue( papi_is_json( '[1, 2, 3]' ) );\n\t\t$this->assertTrue( papi_is_json( '[]' ) );\n\t\t$this->assertFalse( papi_is_json( '12345' ) );\n\t\t$this->assertFalse( papi_is_json( 12345 ) );\n\t\t$this->assertFalse( papi_is_json( 123.45 ) );\n\t\t$this->assertFalse( papi_is_json( true ) );\n\t\t$this->assertFalse( papi_is_json( false ) );\n\t\t$this->assertFalse( papi_is_json( null ) );\n\t\t$this->assertFalse( papi_is_json( [] ) );\n\t\t$this->assertFalse( papi_is_json( (object) [] ) );\n\t}\n\n\tpublic function test_papi_maybe_convert_to_array() {\n\t\t$this->assertEquals( [], papi_maybe_convert_to_array( (object) [] ) );\n\t\t$this->assertSame( true, papi_maybe_convert_to_array( true ) );\n\t\t$this->assertSame( false, papi_maybe_convert_to_array( false ) );\n\t\t$this->assertSame( null, papi_maybe_convert_to_array( null ) );\n\t\t$this->assertSame( [], papi_maybe_convert_to_array( [] ) );\n\t\t$this->assertSame( 1, papi_maybe_convert_to_array( 1 ) );\n\t\t$this->assertSame( 'hello', papi_maybe_convert_to_array( 'hello' ) );\n\t}\n\n\tpublic function test_papi_maybe_convert_to_object() {\n\t\t$this->assertEquals( (object) [], papi_maybe_convert_to_object( [] ) );\n\t\t$this->assertSame( true, papi_maybe_convert_to_object( true ) );\n\t\t$this->assertSame( false, papi_maybe_convert_to_object( false ) );\n\t\t$this->assertSame( null, papi_maybe_convert_to_object( null ) );\n\t\t$this->assertEquals( (object) [], papi_maybe_convert_to_object( (object) [] ) );\n\t\t$this->assertSame( 1, papi_maybe_convert_to_object( 1 ) );\n\t\t$this->assertSame( 'hello', papi_maybe_convert_to_object( 'hello' ) );\n\t}\n\n\tpublic function test_papi_maybe_json_decode() {\n\t\t$this->assertEquals( (object) ['yes' => true], papi_maybe_json_decode( '{\"yes\":true}' ) );\n\t\t$this->assertEquals( (object) [], papi_maybe_json_decode( '{}' ) );\n\t\t$this->assertSame( [1, 2, 3], papi_maybe_json_decode( '[1, 2, 3]', true ) );\n\t\t$this->assertSame( [], papi_maybe_json_decode( '[]', true ) );\n\t\t$this->assertSame( '12345', papi_maybe_json_decode( '12345' ) );\n\t\t$this->assertSame( 12345, papi_maybe_json_decode( 12345 ) );\n\t\t$this->assertSame( 123.45, papi_maybe_json_decode( 123.45 ) );\n\t\t$this->assertSame( true, papi_maybe_json_decode( true ) );\n\t\t$this->assertSame( false, papi_maybe_json_decode( false ) );\n\t\t$this->assertSame( null, papi_maybe_json_decode( null ) );\n\t\t$this->assertSame( [], papi_maybe_json_decode( [] ) );\n\t\t$this->assertEquals( (object) [], papi_maybe_json_decode( (object) [] ) );\n\t}\n\n\tpublic function test_papi_maybe_json_encode() {\n\t\t$this->assertSame( '{\"name\":\"Malmö\"}', papi_maybe_json_encode( ['name' => 'Malmö'] ) );\n\t\t$this->assertTrue( is_string( papi_maybe_json_encode( (object) ['yes' => true] ) ) );\n\t\t$this->assertTrue( is_string( papi_maybe_json_encode( (object) [] ) ) );\n\t\t$this->assertTrue( is_string( papi_maybe_json_encode( [1, 2 , 3] ) ) );\n\t\t$this->assertTrue( is_string( papi_maybe_json_encode( [] ) ) );\n\t\t$this->assertFalse( is_string( papi_maybe_json_encode( 1 ) ) );\n\t\t$this->assertFalse( is_string( papi_maybe_json_encode( 123.4 ) ) );\n\t\t$this->assertFalse( is_string( papi_maybe_json_encode( true ) ) );\n\t\t$this->assertFalse( is_string( papi_maybe_json_encode( false ) ) );\n\t\t$this->assertFalse( is_string( papi_maybe_json_encode( null ) ) );\n\t}\n\n\tpublic function test_papi_nl2br() {\n\t\t$this->assertSame( papi_nl2br( 'Hello\\nWorld' ), 'Hello<br />World' );\n\t\t$this->assertSame( papi_nl2br( \"Hello\\nWorld\" ), \"Hello<br />\\nWorld\" );\n\t}\n\n\tpublic function test_papi_santize_data() {\n\t\t$value = papi_santize_data( '\\\"hello\\\"' );\n\t\t$this->assertSame( '\"hello\"', $value );\n\n\t\t$value = papi_santize_data( [\n\t\t\t'\\\"hello\\\"',\n\t\t\t'\\\"world\\\"'\n\t\t] );\n\n\t\t$this->assertSame( [\n\t\t\t'\"hello\"',\n\t\t\t'\"world\"'\n\t\t], $value );\n\t}\n\n\tpublic function test_papi_sort_order() {\n\t\t$this->assertEmpty( papi_sort_order( null ) );\n\t\t$this->assertEmpty( papi_sort_order( true ) );\n\t\t$this->assertEmpty( papi_sort_order( false ) );\n\t\t$this->assertEmpty( papi_sort_order( 0 ) );\n\t\t$this->assertEmpty( papi_sort_order( [] ) );\n\n\t\t$order = papi_sort_order( papi_property( [\n\t\t\t'type' => 'string',\n\t\t\t'title' => 'Name'\n\t\t] ) );\n\t\t$this->assertSame( $order[0]->sort_order, 1000 );\n\n\t\t$order = papi_sort_order( [ papi_property( [\n\t\t\t'type' => 'string',\n\t\t\t'title' => 'Name'\n\t\t] ) ] );\n\n\t\t$this->assertSame( $order[0]->sort_order, 1000 );\n\t}\n\n\tpublic function test_papi_slugify() {\n\t\t$this->assertSame( 'hello-world-aao', papi_slugify( 'hello world åäö' ) );\n\t\t$this->assertEmpty( papi_slugify( null ) );\n\t\t$this->assertEmpty( papi_slugify( true ) );\n\t\t$this->assertEmpty( papi_slugify( false ) );\n\t\t$this->assertEmpty( papi_slugify( 1 ) );\n\t\t$this->assertEmpty( papi_slugify( [] ) );\n\t\t$this->assertEmpty( papi_slugify( new stdClass ) );\n\t\t$this->assertSame( 'hello-aao', papi_slugify( 'hello world åäö', [ 'world' ] ) );\n\t}\n\n\tpublic function test_papi_slugify_locale() {\n\t\t$locale = setlocale( LC_ALL, '0' );\n\t\t$this->assertSame( 'hello-world-aao', papi_slugify( 'hello world åäö' ) );\n\t\t$this->assertSame( $locale, setlocale( LC_ALL, '0' ) );\n\t}\n\n\tpublic function test_papi_to_array() {\n\t\t$this->assertSame( [ 1 ], papi_to_array( 1 ) );\n\t\t$this->assertSame( [ null ], papi_to_array( null ) );\n\t\t$this->assertSame( [ false ], papi_to_array( false ) );\n\t\t$this->assertSame( [ true ], papi_to_array( true ) );\n\t\t$this->assertSame( [], papi_to_array( [] ) );\n\t\t$this->assertEquals( [ new stdClass ], papi_to_array( new stdClass ) );\n\t}\n\n\tpublic function test_papi_underscorify() {\n\t\t$this->assertSame( 'hello_world_it', papi_underscorify( 'hello world-it' ) );\n\t\t$this->assertEmpty( papi_underscorify( null ) );\n\t\t$this->assertEmpty( papi_underscorify( true ) );\n\t\t$this->assertEmpty( papi_underscorify( false ) );\n\t\t$this->assertEmpty( papi_underscorify( 1 ) );\n\t\t$this->assertEmpty( papi_underscorify( [] ) );\n\t\t$this->assertEmpty( papi_underscorify( new stdClass ) );\n\t}\n}\n"
  },
  {
    "path": "tests/cases/lib/fields/option-test.php",
    "content": "<?php\n\nclass Papi_Lib_Fields_Option_Test extends WP_UnitTestCase {\n\n\tpublic function setUp() {\n\t\tparent::setUp();\n\n\t\tglobal $current_screen;\n\n\t\t$current_screen = WP_Screen::get( 'admin_init' );\n\n\t\t$_SERVER['REQUEST_URI'] = 'http://site.com/?page=papi/options/header-option-type';\n\n\t\tadd_filter( 'papi/settings/directories', function () {\n\t\t\treturn [1,  PAPI_FIXTURE_DIR . '/page-types'];\n\t\t} );\n\t}\n\n\tpublic function tearDown() {\n\t\tparent::tearDown();\n\t\t$_SERVER['REQUEST_URI'] = '';\n\n\t\tglobal $current_screen;\n\t\t$current_screen = null;\n\t}\n\n\tpublic function test_papi_delete_option() {\n\t\t$this->assertFalse( papi_delete_option( 1 ) );\n\t\t$this->assertFalse( papi_delete_option( null ) );\n\t\t$this->assertFalse( papi_delete_option( true ) );\n\t\t$this->assertFalse( papi_delete_option( false ) );\n\t\t$this->assertFalse( papi_delete_option( [] ) );\n\t\t$this->assertFalse( papi_delete_option( (object) [] ) );\n\t\t$this->assertFalse( papi_delete_option( '' ) );\n\t\t$this->assertFalse( papi_delete_option( 'fake_slug' ) );\n\t\tupdate_option( 'name', 'Kalle' );\n\t\t$this->assertSame( 'Kalle', papi_get_option( 'name' ) );\n\t\t$this->assertTrue( papi_delete_option( 'name' ) );\n\t\t$this->assertNull( papi_get_option( 'name' ) );\n\t}\n\n\tpublic function test_papi_get_option() {\n\t\t$this->assertNull( papi_get_option( 'site' ) );\n\t\t$this->assertNull( papi_get_option( 1 ) );\n\t\t$this->assertNull( papi_get_option( null ) );\n\t\t$this->assertNull( papi_get_option( true ) );\n\t\t$this->assertNull( papi_get_option( false ) );\n\t\t$this->assertNull( papi_get_option( [] ) );\n\t\t$this->assertNull( papi_get_option( (object) [] ) );\n\t\t$this->assertNull( papi_get_option( '' ) );\n\t\tupdate_option( 'name', 'fredrik' );\n\t\t$this->assertSame( 'fredrik', papi_get_option( 'name' ) );\n\t}\n\n\tpublic function test_papi_option_shortcode() {\n\t\tupdate_option( 'name', 'fredrik' );\n\n\t\t$this->assertEmpty( papi_option_shortcode( [] ) );\n\t\t$this->assertSame( 'fredrik', papi_option_shortcode( [\n\t\t\t'slug' => 'name'\n\t\t] ) );\n\n\t\t$this->assertSame( '1, 2, 3', papi_option_shortcode( [\n\t\t\t'slug'    => 'numbers',\n\t\t\t'default' => [1, 2, 3]\n\t\t] ) );\n\t}\n\n\tpublic function test_papi_update_option() {\n\t\t$this->assertFalse( papi_update_option( 1 ) );\n\t\t$this->assertFalse( papi_update_option( null ) );\n\t\t$this->assertFalse( papi_update_option( true ) );\n\t\t$this->assertFalse( papi_update_option( false ) );\n\t\t$this->assertFalse( papi_update_option( [] ) );\n\t\t$this->assertFalse( papi_update_option( (object) [] ) );\n\t\t$this->assertFalse( papi_update_option( '' ) );\n\t\t$this->assertFalse( papi_update_option( 0, 'fake_slug' ) );\n\t\t$this->assertFalse( papi_update_option( 'fake_slug' ) );\n\t\t$this->assertFalse( papi_update_option( 93099, 'fake_slug' ) );\n\t\t$this->assertFalse( papi_update_option( 'fake_slug' ) );\n\t\t$this->assertTrue( papi_update_option( 'name', 'Kalle' ) );\n\t\t$this->assertSame( 'Kalle', papi_get_option( 'name' ) );\n\t}\n\n\tpublic function test_the_papi_option() {\n\t\tupdate_option( 'name', 'fredrik' );\n\n\t\tthe_papi_option( 'name' );\n\t\t$this->expectOutputRegex( '/fredrik/' );\n\n\t\tthe_papi_option( 'numbers', [1, 2, 3] );\n\t\t$this->expectOutputRegex( '/1\\, 2\\, 3/' );\n\t}\n}\n"
  },
  {
    "path": "tests/cases/lib/fields/page-test.php",
    "content": "<?php\n\nclass Papi_Lib_Fields_Page_Test extends WP_UnitTestCase {\n\n\tpublic function setUp() {\n\t\tparent::setUp();\n\n\t\t$_GET = [];\n\n\t\tadd_filter( 'papi/settings/directories', function () {\n\t\t\treturn [1,  PAPI_FIXTURE_DIR . '/page-types'];\n\t\t} );\n\n\t\t$this->post_id = $this->factory->post->create();\n\n\t\tupdate_post_meta( $this->post_id, papi_get_page_type_key(), 'simple-page-type' );\n\t}\n\n\tpublic function tearDown() {\n\t\tparent::tearDown();\n\t\tunset( $_GET, $this->post_id );\n\t}\n\n\tpublic function test_papi_delete_field() {\n\t\t$this->assertFalse( papi_delete_field( 1 ) );\n\t\t$this->assertFalse( papi_delete_field( null ) );\n\t\t$this->assertFalse( papi_delete_field( true ) );\n\t\t$this->assertFalse( papi_delete_field( false ) );\n\t\t$this->assertFalse( papi_delete_field( [] ) );\n\t\t$this->assertFalse( papi_delete_field( (object) [] ) );\n\t\t$this->assertFalse( papi_delete_field( '' ) );\n\t\t$this->assertFalse( papi_delete_field( 0, 'fake_slug' ) );\n\t\t$this->assertFalse( papi_delete_field( 'fake_slug' ) );\n\t\t$this->assertFalse( papi_delete_field( 93099, 'fake_slug' ) );\n\n\t\tupdate_post_meta( $this->post_id, 'name', 'brunnsgatan' );\n\t\t$this->assertTrue( papi_delete_field( $this->post_id, 'name' ) );\n\n\t\t$_GET['page_id'] = $this->post_id;\n\t\tupdate_post_meta( $this->post_id, 'name', 'brunnsgatan' );\n\t\t$this->assertTrue( papi_delete_field( 'name' ) );\n\t\tunset( $_GET['page_id'] );\n\n\t\t$this->assertFalse( papi_delete_field( $this->post_id, 'fake' ) );\n\t\t$this->assertFalse( papi_delete_field( 0, 'fake' ) );\n\t}\n\n\tpublic function test_papi_get_field() {\n\t\tupdate_post_meta( $this->post_id, 'name', 'fredrik' );\n\n\t\t$this->assertNull( papi_get_field( 1 ) );\n\t\t$this->assertNull( papi_get_field( null ) );\n\t\t$this->assertNull( papi_get_field( true ) );\n\t\t$this->assertNull( papi_get_field( false ) );\n\t\t$this->assertNull( papi_get_field( [] ) );\n\t\t$this->assertNull( papi_get_field( (object) [] ) );\n\t\t$this->assertNull( papi_get_field( '' ) );\n\t\t$this->assertNull( papi_get_field( $this->post_id, '' ) );\n\t\t$this->assertNull( papi_get_field( 99999, 'fake' ) );\n\n\t\t$this->assertSame( 'fredrik', papi_get_field( $this->post_id, 'name' ) );\n\t\t$this->assertSame( 'fredrik', papi_get_field( $this->post_id, 'name', '', 'post' ) );\n\n\t\t$this->assertSame( 'world', papi_get_field( $this->post_id, 'hello', 'world' ) );\n\n\t\t$_GET['post_id'] = $this->post_id;\n\t\t$this->assertNull( papi_get_field( 'name' ) );\n\t\t$this->assertSame( 'fredrik', papi_get_field( '', 'fredrik' ) );\n\n\t\tupdate_post_meta( $this->post_id, 'uppercase', 'fredrik' );\n\t\t$this->assertSame( 'fredrik', papi_get_field( $this->post_id, 'UPPERCASE' ) );\n\t}\n\n\tpublic function test_papi_get_fields() {\n\t\tupdate_post_meta( $this->post_id, papi_get_page_type_key(), 'fields-page-type' );\n\t\tupdate_post_meta( $this->post_id, 'name', 'fredrik' );\n\n\t\t$output = get_post_meta( $this->post_id, 'name', true );\n\t\t$this->assertSame( 'fredrik', $output );\n\n\t\t$output = papi_get_fields( $this->post_id );\n\t\t$this->assertSame( 'fields', $output['name'] );\n\n\t\tadd_filter( 'papi/get_fields', function( $fields ) {\n\t\t\treturn ['name' => 'filter'];\n\t\t} );\n\n\t\t$output = papi_get_fields( $this->post_id );\n\t\t$this->assertSame( 'filter', $output['name'] );\n\t}\n\n\tpublic function test_papi_get_field_cache() {\n\t\tpapi_data_update( $this->post_id, 'name', 'fredrik' );\n\n\t\t$this->assertSame( 'fredrik', papi_get_field( $this->post_id, 'name' ) );\n\t\t$this->assertSame( 'fredrik', papi_cache_get( 'name', $this->post_id ) );\n\n\t\t// Turn off property cache.\n\t\tadd_filter( 'papi/get_property', function ( $property ) {\n\t\t\t$property->set_option( 'cache', false );\n\t\t\treturn $property;\n\t\t} );\n\n\t\t$this->assertSame( 'fredrik', papi_get_field( $this->post_id, 'name' ) );\n\t\t$this->assertEmpty( papi_cache_get( 'name', $this->post_id ) );\n\t}\n\n\tpublic function test_papi_get_slugs() {\n\t\t$this->assertEmpty( papi_get_slugs() );\n\n\t\tglobal $post;\n\n\t\t$post = get_post( $this->post_id );\n\n\t\tadd_filter( 'papi/settings/directories', function () {\n\t\t\treturn [1,  PAPI_FIXTURE_DIR . '/page-types'];\n\t\t} );\n\n\t\tupdate_post_meta( $this->post_id, papi_get_page_type_key(), 'simple-page-type' );\n\t\t$actual = papi_get_slugs( $this->post_id );\n\n\t\t$this->assertTrue( ! empty( $actual ) );\n\t\t$this->assertTrue( is_array( $actual ) );\n\n\t\t$slugs = papi_get_slugs( $this->post_id, true );\n\t\t$this->assertTrue( array_filter( $slugs, 'is_string' ) === $slugs );\n\n\t\t$_GET['post_id'] = $this->post_id;\n\t\t$slugs = papi_get_slugs( true );\n\t\t$this->assertTrue( array_filter( $slugs, 'is_string' ) === $slugs );\n\t\tunset( $_GET['post_id'] );\n\n\t\tupdate_post_meta( $this->post_id, papi_get_page_type_key(), 'empty-page-type' );\n\t\t$this->flush_cache();\n\t\t$this->assertEmpty( papi_get_slugs() );\n\t}\n\n\tpublic function test_papi_field_value() {\n\t\t$this->assertSame( 'fredrik', papi_field_value(\n\t\t\t[ 'what', 'name' ],\n\t\t\t[ 'what' => [ 'name' => 'fredrik' ] ]\n\t\t) );\n\t\t$this->assertSame( 'fredrik', papi_field_value(\n\t\t\t[ 'what', 'name' ],\n\t\t\t(object) [ 'what' => [ 'name' => 'fredrik' ] ]\n\t\t) );\n\t}\n\n\tpublic function test_papi_field_shortcode() {\n\t\tupdate_post_meta( $this->post_id, 'name', 'fredrik' );\n\n\t\t$this->assertEmpty( papi_field_shortcode( [] ) );\n\t\t$this->assertSame( 'fredrik', papi_field_shortcode( [\n\t\t\t'id'   => $this->post_id,\n\t\t\t'slug' => 'name'\n\t\t] ) );\n\n\t\t$this->assertSame( '1, 2, 3', papi_field_shortcode( [\n\t\t\t'slug'    => 'numbers',\n\t\t\t'default' => [1, 2, 3]\n\t\t] ) );\n\n\t\t$this->assertSame( '1, 2, 3', papi_field_shortcode( [\n\t\t\t'id'      => $this->post_id,\n\t\t\t'slug'    => 'numbers',\n\t\t\t'default' => [1, 2, 3]\n\t\t] ) );\n\n\t\tglobal $post;\n\n\t\t$post = get_post( $this->post_id );\n\n\t\t$this->assertSame( 'fredrik', papi_field_shortcode( [\n\t\t\t'slug' => 'name'\n\t\t] ) );\n\t}\n\n\tpublic function test_papi_update_field() {\n\t\t$this->assertFalse( papi_update_field( 1 ) );\n\t\t$this->assertFalse( papi_update_field( null ) );\n\t\t$this->assertFalse( papi_update_field( true ) );\n\t\t$this->assertFalse( papi_update_field( false ) );\n\t\t$this->assertFalse( papi_update_field( [] ) );\n\t\t$this->assertFalse( papi_update_field( (object) [] ) );\n\t\t$this->assertFalse( papi_update_field( '' ) );\n\t\t$this->assertFalse( papi_update_field( 0, 'fake_slug' ) );\n\t\t$this->assertFalse( papi_update_field( 0, 'fake_slug', 'value' ) );\n\t\t$this->assertFalse( papi_update_field( 'fake_slug', 'value' ) );\n\t\t$this->assertFalse( papi_update_field( 93099, 'fake_slug', 'value' ) );\n\t\t$this->assertFalse( papi_update_field( $this->post_id, 'fake_slug', 'value' ) );\n\t\t$this->assertTrue( papi_update_field( $this->post_id, 'name', 'Kalle' ) );\n\t\t$this->assertSame( 'Kalle', papi_get_field( $this->post_id, 'name' ) );\n\t}\n\n\tpublic function test_the_papi_field() {\n\t\tupdate_post_meta( $this->post_id, 'name', 'fredrik' );\n\n\t\tthe_papi_field( $this->post_id, 'name' );\n\t\t$this->expectOutputRegex( '/fredrik/' );\n\n\t\tthe_papi_field( $this->post_id, 'numbers', [ 1, 2, 3 ] );\n\t\t$this->expectOutputRegex( '/1\\, 2\\, 3/' );\n\t}\n}\n"
  },
  {
    "path": "tests/cases/lib/fields/taxonomy-test.php",
    "content": "<?php\n\nclass Papi_Lib_Fields_Taxonomy_Test extends WP_UnitTestCase {\n\n\tpublic function setUp() {\n\t\tparent::setUp();\n\n\t\t$_GET = [];\n\n\t\tadd_filter( 'papi/settings/directories', function () {\n\t\t\treturn [1,  PAPI_FIXTURE_DIR . '/taxonomy-types'];\n\t\t} );\n\n\t\t$this->term_id = $this->factory->term->create();\n\n\t\tupdate_term_meta( $this->term_id, papi_get_page_type_key(), 'properties-taxonomy-type' );\n\t}\n\n\tpublic function tearDown() {\n\t\tparent::tearDown();\n\t\tunset( $_GET, $this->term_id );\n\t}\n\n\tpublic function test_papi_delete_term_field() {\n\t\t$this->assertFalse( papi_delete_term_field( 1 ) );\n\t\t$this->assertFalse( papi_delete_term_field( null ) );\n\t\t$this->assertFalse( papi_delete_term_field( true ) );\n\t\t$this->assertFalse( papi_delete_term_field( false ) );\n\t\t$this->assertFalse( papi_delete_term_field( [] ) );\n\t\t$this->assertFalse( papi_delete_term_field( (object) [] ) );\n\t\t$this->assertFalse( papi_delete_term_field( '' ) );\n\t\t$this->assertFalse( papi_delete_term_field( 0, 'fake_slug' ) );\n\t\t$this->assertFalse( papi_delete_term_field( 'fake_slug' ) );\n\t\t$this->assertFalse( papi_delete_term_field( 93099, 'fake_slug' ) );\n\n\t\tupdate_term_meta( $this->term_id, 'string_test', 'brunnsgatan' );\n\t\t$this->assertTrue( papi_delete_term_field( $this->term_id, 'string_test' ) );\n\n\t\t$_GET['term_id'] = $this->term_id;\n\t\tupdate_term_meta( $this->term_id, 'string_test', 'brunnsgatan' );\n\t\t$this->assertTrue( papi_delete_term_field( 'string_test' ) );\n\t\tunset( $_GET['term_id'] );\n\n\t\t$this->assertFalse( papi_delete_term_field( $this->term_id, 'fake' ) );\n\t\t$this->assertFalse( papi_delete_term_field( 0, 'fake' ) );\n\t}\n\n\tpublic function test_papi_get_term_field() {\n\t\tupdate_term_meta( $this->term_id, 'string_test', 'fredrik' );\n\n\t\t$this->assertNull( papi_get_term_field( 1 ) );\n\t\t$this->assertNull( papi_get_term_field( null ) );\n\t\t$this->assertNull( papi_get_term_field( true ) );\n\t\t$this->assertNull( papi_get_term_field( false ) );\n\t\t$this->assertNull( papi_get_term_field( [] ) );\n\t\t$this->assertNull( papi_get_term_field( (object) [] ) );\n\t\t$this->assertNull( papi_get_term_field( '' ) );\n\t\t$this->assertNull( papi_get_term_field( $this->term_id, '' ) );\n\n\t\t$this->assertSame( 'fredrik', papi_get_term_field( $this->term_id, 'string_test' ) );\n\t\t$this->assertSame( 'fredrik', papi_get_term_field( $this->term_id, 'string_test', '', 'post' ) );\n\n\t\t$this->assertSame( 'world', papi_get_term_field( $this->term_id, 'hello', 'world' ) );\n\n\t\t$_GET['term_id'] = 0;\n\t\t$this->assertNull( papi_get_term_field( 'string_test' ) );\n\t\t$this->assertSame( 'fredrik', papi_get_term_field( '', 'fredrik' ) );\n\t}\n\n\tpublic function test_papi_get_term_fields() {\n\t\tupdate_term_meta( $this->term_id, 'string_test', 'fredrik' );\n\n\t\t$output = papi_get_term_fields( $this->term_id );\n\t\t$this->assertSame( 'fredrik', $output['string_test'] );\n\t}\n\n\tpublic function test_papi_get_term_slugs() {\n\t\t$this->assertEmpty( papi_get_term_slugs() );\n\n\t\tupdate_term_meta( $this->term_id, papi_get_page_type_key(), 'simple-taxonomy-type' );\n\t\t$actual = papi_get_term_slugs( $this->term_id );\n\n\t\t$this->assertTrue( ! empty( $actual ) );\n\t\t$this->assertTrue( is_array( $actual ) );\n\n\t\t$slugs = papi_get_term_slugs( $this->term_id, true );\n\t\t$this->assertTrue( array_filter( $slugs, 'is_string' ) === $slugs );\n\n\t\t$_GET['term_id'] = $this->term_id;\n\t\t$slugs = papi_get_term_slugs( true );\n\t\t$this->assertTrue( array_filter( $slugs, 'is_string' ) === $slugs );\n\t\tunset( $_GET['term_id'] );\n\n\t\tupdate_term_meta( $this->term_id, papi_get_page_type_key(), '' );\n\t\t$this->flush_cache();\n\t\t$this->assertEmpty( papi_get_term_slugs() );\n\n\t\tupdate_term_meta( $this->term_id, papi_get_page_type_key(), 'empty-taxonomy-type' );\n\t\t$this->flush_cache();\n\t\t$this->assertEmpty( papi_get_term_slugs() );\n\t}\n\n\tpublic function test_papi_taxonomy_shortcode() {\n\t\tupdate_term_meta( $this->term_id, 'string_test', 'fredrik' );\n\n\t\t$this->assertEmpty( papi_taxonomy_shortcode( [] ) );\n\t\t$this->assertSame( 'fredrik', papi_taxonomy_shortcode( [\n\t\t\t'id'   => $this->term_id,\n\t\t\t'slug' => 'string_test'\n\t\t] ) );\n\n\t\t$this->assertSame( '1, 2, 3', papi_taxonomy_shortcode( [\n\t\t\t'slug'    => 'numbers',\n\t\t\t'default' => [1, 2, 3]\n\t\t] ) );\n\n\t\t$this->assertSame( '1, 2, 3', papi_taxonomy_shortcode( [\n\t\t\t'id'      => $this->term_id,\n\t\t\t'slug'    => 'numbers',\n\t\t\t'default' => [1, 2, 3]\n\t\t] ) );\n\n\t\t$_GET['term_id'] = $this->term_id;\n\n\t\t$this->assertSame( 'fredrik', papi_taxonomy_shortcode( [\n\t\t\t'slug' => 'string_test'\n\t\t] ) );\n\t}\n\n\tpublic function test_papi_update_term_field() {\n\t\t$this->assertFalse( papi_update_term_field( 1 ) );\n\t\t$this->assertFalse( papi_update_term_field( null ) );\n\t\t$this->assertFalse( papi_update_term_field( true ) );\n\t\t$this->assertFalse( papi_update_term_field( false ) );\n\t\t$this->assertFalse( papi_update_term_field( [] ) );\n\t\t$this->assertFalse( papi_update_term_field( (object) [] ) );\n\t\t$this->assertFalse( papi_update_term_field( '' ) );\n\t\t$this->assertFalse( papi_update_term_field( 0, 'fake_slug' ) );\n\t\t$this->assertFalse( papi_update_term_field( 0, 'fake_slug', 'value' ) );\n\t\t$this->assertFalse( papi_update_term_field( 'fake_slug', 'value' ) );\n\t\t$this->assertFalse( papi_update_term_field( 93099, 'fake_slug', 'value' ) );\n\t\t$this->assertFalse( papi_update_term_field( $this->term_id, 'fake_slug', 'value' ) );\n\t\t$this->assertTrue( papi_update_term_field( $this->term_id, 'string_test', 'Kalle' ) );\n\t\t$this->assertSame( 'Kalle', papi_get_term_field( $this->term_id, 'string_test' ) );\n\t}\n\n\tpublic function test_the_papi_term_field() {\n\t\tupdate_term_meta( $this->term_id, 'string_test', 'fredrik' );\n\n\t\tthe_papi_term_field( $this->term_id, 'string_test' );\n\t\t$this->expectOutputRegex( '/fredrik/' );\n\n\t\tthe_papi_term_field( $this->term_id, 'numbers', [ 1, 2, 3 ] );\n\t\t$this->expectOutputRegex( '/1\\, 2\\, 3/' );\n\t}\n}\n"
  },
  {
    "path": "tests/cases/lib/hooks/actions-test.php",
    "content": "<?php\n\n/**\n * @group hooks\n */\nclass Papi_Lib_Actions_Test extends WP_UnitTestCase {\n\n\tpublic function test() {\n\t\t$this->assertTrue( true );\n\t}\n}\n"
  },
  {
    "path": "tests/cases/lib/hooks/filters-page-type-test.php",
    "content": "<?php\n\n/**\n * @group hooks\n */\nclass Papi_Lib_Filters_Page_Type_Test extends WP_UnitTestCase {\n\n\tpublic function test_papi_filter_settings_only_page_type() {\n\t\t$this->assertSame( '', papi_filter_settings_only_page_type( 'post' ) );\n\n\t\tadd_filter( 'papi/settings/only_page_type_post', function () {\n\t\t\treturn 'simple-page-type';\n\t\t} );\n\n\t\t$this->assertSame( 'simple-page-type', papi_filter_settings_only_page_type( 'post' ) );\n\n\t\tadd_filter( 'papi/settings/only_page_type_post', function () {\n\t\t\treturn false;\n\t\t} );\n\n\t\t$this->assertEmpty( papi_filter_settings_only_page_type( 'post' ) );\n\t}\n\n\tpublic function test_papi_filter_settings_show_page_type() {\n\t\t$this->assertTrue( papi_filter_settings_show_page_type( 'post', 'test-page-type' ) );\n\n\t\tadd_filter( 'papi/settings/show_page_type_post', function ( $page_type ) {\n\t\t\tif ( $page_type == 'test-page-type' ) {\n\t\t\t\treturn false;\n\t\t\t}\n\n\t\t\treturn true;\n\t\t} );\n\n\t\t$this->assertFalse( papi_filter_settings_show_page_type( 'post', 'test-page-type' ) );\n\n\t\tadd_filter( 'papi/settings/directories', function () {\n\t\t\treturn [1,  PAPI_FIXTURE_DIR . '/page-types'];\n\t\t} );\n\n\t\t$page_type = papi_get_entry_type_by_id( 'simple-page-type' );\n\t\t$this->assertTrue( papi_filter_settings_show_page_type( 'post', $page_type ) );\n\n\t\tadd_filter( 'papi/settings/show_page_type_post', function ( $page_type ) {\n\t\t\treturn 'no';\n\t\t} );\n\n\t\t$page_type = papi_get_entry_type_by_id( 'simple-page-type' );\n\t\t$this->assertFalse( papi_filter_settings_show_page_type( 'post', $page_type ) );\n\t}\n\n\tpublic function test_papi_filter_settings_standard_page_type_description() {\n\t\t$this->assertSame( 'Post with WordPress standard fields', papi_filter_settings_standard_page_type_description( 'post' ) );\n\t\t$this->assertSame( 'Page with WordPress standard fields', papi_filter_settings_standard_page_type_description( 'fake' ) );\n\t\t$this->assertSame( 'Page with WordPress standard fields', papi_filter_settings_standard_page_type_description( 'Page' ) );\n\n\t\tadd_filter( 'papi/settings/standard_page_type_description_post', function () {\n\t\t\treturn 'Hello, world!';\n\t\t} );\n\n\t\t$this->assertSame( 'Hello, world!', papi_filter_settings_standard_page_type_description( 'post' ) );\n\n\t\t// Old filters, should work until Papi 4.0.0.\n\t\tadd_filter( 'papi/settings/standard_page_description_post', function () {\n\t\t\treturn 'Hello, world!';\n\t\t} );\n\n\t\t$this->assertSame( 'Hello, world!', papi_filter_settings_standard_page_type_description( 'post' ) );\n\t}\n\n\tpublic function test_papi_filter_settings_standard_page_type_name() {\n\t\t$this->assertSame( 'Standard Post', papi_filter_settings_standard_page_type_name( 'post' ) );\n\t\t$this->assertSame( 'Standard Page', papi_filter_settings_standard_page_type_name( 'fake' ) );\n\t\t$this->assertSame( 'Standard Page', papi_filter_settings_standard_page_type_name( 'page' ) );\n\n\t\tadd_filter( 'papi/settings/standard_page_type_name_post', function () {\n\t\t\treturn 'Hello, world!';\n\t\t} );\n\n\t\t$this->assertSame( 'Hello, world!', papi_filter_settings_standard_page_type_name( 'post' ) );\n\n\t\t// Old filters, should work until Papi 4.0.0.\n\t\tadd_filter( 'papi/settings/standard_page_type_post', function () {\n\t\t\treturn 'Hello, world!';\n\t\t} );\n\n\t\t$this->assertSame( 'Hello, world!', papi_filter_settings_standard_page_type_name( 'post' ) );\n\t}\n\n\tpublic function test_papi_filter_settings_show_standard_page_type() {\n\t\t$this->assertSame( false, papi_filter_settings_show_standard_page_type( 'post' ) );\n\n\t\tadd_filter( 'papi/settings/show_standard_page_type_post', '__return_true' );\n\n\t\t$this->assertSame( true, papi_filter_settings_show_standard_page_type( 'post' ) );\n\t}\n\n\tpublic function test_papi_filter_settings_show_standard_page_type_in_filter() {\n\t\t$this->assertSame( false, papi_filter_settings_show_standard_page_type_in_filter( 'post' ) );\n\n\t\tadd_filter( 'papi/settings/show_standard_page_type_in_filter_post', '__return_true' );\n\n\t\t$this->assertSame( true, papi_filter_settings_show_standard_page_type_in_filter( 'post' ) );\n\t}\n\n\tpublic function test_papi_filter_settings_standard_page_type_thumbnail() {\n\t\t$this->assertSame( '', papi_filter_settings_standard_page_type_thumbnail( 'post' ) );\n\n\t\tadd_filter( 'papi/settings/standard_page_type_thumbnail_post', function () {\n\t\t\treturn 'Hello, world!';\n\t\t} );\n\n\t\t$this->assertSame( 'Hello, world!', papi_filter_settings_standard_page_type_thumbnail( 'post' ) );\n\n\t\t// Old filters, should work until Papi 4.0.0.\n\t\tadd_filter( 'papi/settings/standard_page_thumbnail_post', function () {\n\t\t\treturn 'Hello, world!';\n\t\t} );\n\n\t\t$this->assertSame( 'Hello, world!', papi_filter_settings_standard_page_type_thumbnail( 'post' ) );\n\t}\n}\n"
  },
  {
    "path": "tests/cases/lib/hooks/filters-taxonomy-type-test.php",
    "content": "<?php\n\n/**\n * @group hooks\n */\nclass Papi_Lib_Filters_Taxonomy_Type_Test extends WP_UnitTestCase {\n\n\tpublic function test_papi_filter_settings_only_taxonomy_type() {\n\t\t$this->assertSame( '', papi_filter_settings_only_taxonomy_type( 'category' ) );\n\n\t\tadd_filter( 'papi/settings/only_taxonomy_type_category', function () {\n\t\t\treturn 'simple-taxonomy-type';\n\t\t} );\n\n\t\t$this->assertSame( 'simple-taxonomy-type', papi_filter_settings_only_taxonomy_type( 'category' ) );\n\n\t\tadd_filter( 'papi/settings/only_taxonomy_type_category', function () {\n\t\t\treturn false;\n\t\t} );\n\n\t\t$this->assertEmpty( papi_filter_settings_only_taxonomy_type( 'category' ) );\n\t}\n\n\tpublic function test_papi_filter_settings_standard_taxonomy_type_name() {\n\t\t$this->assertSame( 'Standard Category', papi_filter_settings_standard_taxonomy_type_name( 'category' ) );\n\t\t$this->assertSame( 'Standard Taxonomy', papi_filter_settings_standard_taxonomy_type_name( 'fake' ) );\n\n\t\tadd_filter( 'papi/settings/standard_taxonomy_name_post', function () {\n\t\t\treturn 'Hello, world!';\n\t\t} );\n\n\t\t$this->assertSame( 'Hello, world!', papi_filter_settings_standard_taxonomy_type_name( 'post' ) );\n\t}\n\n\tpublic function test_papi_filter_settings_show_standard_taxonomy_type() {\n\t\t$this->assertSame( false, papi_filter_settings_show_standard_taxonomy_type( 'post' ) );\n\n\t\tadd_filter( 'papi/settings/show_standard_taxonomy_type_post', '__return_true' );\n\n\t\t$this->assertSame( true, papi_filter_settings_show_standard_taxonomy_type( 'post' ) );\n\t}\n}\n"
  },
  {
    "path": "tests/cases/lib/hooks/filters-test.php",
    "content": "<?php\n\n/**\n * @group hooks\n */\nclass Papi_Lib_Filters_Test extends WP_UnitTestCase {\n\n\tpublic function setUp() {\n\t\tparent::setUp();\n\t\t$this->post_id = $this->factory->post->create();\n\t}\n\n\tpublic function tearDown() {\n\t\tparent::tearDown();\n\t\tunset( $this->post_id );\n\t}\n\n\tpublic function test_papi_filter_format_value() {\n\t\t$this->assertSame( 'hello', papi_filter_format_value( 'string', 'hello', 'slug', 1 ) );\n\n\t\tadd_filter( 'papi/format_value/string', function () {\n\t\t\treturn 'change-format';\n\t\t} );\n\n\t\t$this->assertSame( 'change-format', papi_filter_format_value( 'string', 'hello', 'slug', 1 ) );\n\t}\n\n\tpublic function test_papi_filter_conditional_rule_allowed() {\n\t\t$this->assertFalse( papi_filter_conditional_rule_allowed( true ) );\n\t\t$this->assertFalse( papi_filter_conditional_rule_allowed( false ) );\n\t\t$this->assertFalse( papi_filter_conditional_rule_allowed( null ) );\n\t\t$this->assertFalse( papi_filter_conditional_rule_allowed( 1 ) );\n\t\t$this->assertFalse( papi_filter_conditional_rule_allowed( '' ) );\n\t\t$this->assertFalse( papi_filter_conditional_rule_allowed( (object) [] ) );\n\t\t$this->assertTrue( papi_filter_conditional_rule_allowed( new Papi_Core_Conditional_Rule( [\n\t\t\t'operator' => '=',\n\t\t\t'slug'     => 'fake',\n\t\t\t'source'   => 'hello',\n\t\t\t'value'    => 'hello'\n\t\t] ) ) );\n\t\t$this->assertTrue( papi_filter_conditional_rule_allowed( [\n\t\t\t'operator' => '=',\n\t\t\t'slug'     => 'fake',\n\t\t\t'source'   => 'hello',\n\t\t\t'value'    => 'hello'\n\t\t] ) );\n\t}\n\n\tpublic function test_papi_filter_load_value() {\n\t\t$this->assertSame( 'hello', papi_filter_load_value( 'string', 'hello', 'slug', 1 ) );\n\n\t\tadd_filter( 'papi/load_value/string', function () {\n\t\t\treturn 'change-load';\n\t\t} );\n\n\t\t$this->assertSame( 'change-load', papi_filter_load_value( 'string', 'hello', 'slug', 1 ) );\n\t}\n\n\tpublic function test_papi_filter_settings_default_sort_order() {\n\t\t$this->assertSame( 1000, papi_filter_settings_sort_order() );\n\n\t\tadd_filter( 'papi/settings/sort_order', function () {\n\t\t\treturn 1;\n\t\t} );\n\n\t\t$this->assertSame( 1, papi_filter_settings_sort_order() );\n\t}\n\n\tpublic function test_papi_filter_page_type_directories() {\n\t\t$defaults = [get_template_directory() . '/page-types'];\n\t\t$this->assertEquals( $defaults, papi_filter_settings_directories() );\n\n\t\tadd_filter( 'papi/settings/directories', function () {\n\t\t\treturn 'path';\n\t\t} );\n\n\t\t$directories = papi_filter_settings_directories();\n\t\t$this->assertSame( 'path', $directories[0] );\n\t\t$this->assertSame( 1, count( $directories ) );\n\n\t\tadd_filter( 'papi/settings/directories', function () {\n\t\t\treturn null;\n\t\t} );\n\n\t\t$this->assertEquals( $defaults, papi_filter_settings_directories() );\n\t}\n\n\tpublic function test_papi_filter_update_value() {\n\t\t$this->assertSame( 'hello', papi_filter_update_value( 'string', 'hello', 'slug', 1 ) );\n\n\t\tadd_filter( 'papi/update_value/string', function () {\n\t\t\treturn 'change-update';\n\t\t} );\n\n\t\t$this->assertSame( 'change-update', papi_filter_update_value( 'string', 'hello', 'slug', 1 ) );\n\t}\n}\n"
  },
  {
    "path": "tests/cases/lib/types/entry-test.php",
    "content": "<?php\n\nclass Papi_Lib_Types_Entry_Test extends WP_UnitTestCase {\n\n\tpublic function setUp() {\n\t\tparent::setUp();\n\t\t$this->post_id = $this->factory->post->create();\n\t}\n\n\tpublic function tearDown() {\n\t\tparent::tearDown();\n\t\tunset( $this->post_id );\n\n\t\tremove_all_filters( 'papi/settings/directories' );\n\t\tpapi()->reset();\n\t}\n\n\tpublic function test_papi_get_entry_type_body_classes() {\n\t\tglobal $post;\n\n\t\t$post = get_post( $this->post_id );\n\t\t$this->assertEmpty( papi_get_entry_type_body_classes() );\n\n\t\tadd_filter( 'papi/settings/directories', function () {\n\t\t\treturn [1,  PAPI_FIXTURE_DIR . '/page-types'];\n\t\t} );\n\n\t\tupdate_post_meta( $this->post_id, papi_get_page_type_key(), 'dot-page-type' );\n\t\t$this->assertSame( ['custom-css-class'], papi_get_entry_type_body_classes() );\n\t}\n\n\tpublic function test_papi_get_entry_type_css_class_page() {\n\t\tglobal $post;\n\n\t\t$this->assertEmpty( papi_get_entry_type_css_class() );\n\n\t\t$post = get_post( $this->post_id );\n\t\t$this->go_to( get_permalink( $this->post_id ) );\n\t\t$this->assertEmpty( papi_get_entry_type_css_class() );\n\n\t\tupdate_post_meta( $this->post_id, papi_get_page_type_key(), '/' );\n\t\t$this->assertEmpty( papi_get_entry_type_css_class() );\n\n\t\tupdate_post_meta( $this->post_id, papi_get_page_type_key(), 'simple-page-type' );\n\t\t$this->assertSame( 'simple-page-type', papi_get_entry_type_css_class() );\n\t}\n\n\tpublic function test_test_papi_get_entry_type_css_class_taxonomy() {\n\t\t$cat_id = $this->factory->category->create();\n\t\t$this->go_to( get_term_link( $cat_id, 'category' ) );\n\t\t$this->assertEmpty( papi_get_entry_type_css_class() );\n\n\t\tupdate_term_meta( $cat_id, papi_get_page_type_key(), '/' );\n\t\t$this->assertEmpty( papi_get_entry_type_css_class() );\n\n\t\tupdate_term_meta( $cat_id, papi_get_page_type_key(), 'simple-page-type' );\n\t\t$this->assertSame( 'simple-page-type', papi_get_entry_type_css_class() );\n\t}\n\n\tpublic function test_papi_get_entry_type_count() {\n\t\t$this->assertSame( 0, papi_get_entry_type_count( 'simple-page-type' ) );\n\t\t$this->assertSame( 0, papi_get_entry_type_count( null ) );\n\t\t$this->assertSame( 0, papi_get_entry_type_count( true ) );\n\t\t$this->assertSame( 0, papi_get_entry_type_count( false ) );\n\t\t$this->assertSame( 0, papi_get_entry_type_count( [] ) );\n\t\t$this->assertSame( 0, papi_get_entry_type_count( new stdClass() ) );\n\t\t$this->assertSame( 0, papi_get_entry_type_count( 1 ) );\n\n\t\tadd_filter( 'papi/settings/directories', function () {\n\t\t\treturn [1,  PAPI_FIXTURE_DIR . '/page-types'];\n\t\t} );\n\n\t\tupdate_post_meta( $this->post_id, papi_get_page_type_key(), 'simple-page-type' );\n\t\t$this->assertSame( 1, papi_get_entry_type_count( 'simple-page-type' ) );\n\n\t\t$simple_page_type = papi_get_entry_type_by_id( 'simple-page-type' );\n\n\t\t$this->assertSame( 1, papi_get_entry_type_count( $simple_page_type ) );\n\t}\n\n\tpublic function test_papi_entry_type_exists() {\n\t\tadd_filter( 'papi/settings/directories', function () {\n\t\t\treturn [1,  PAPI_FIXTURE_DIR . '/page-types'];\n\t\t} );\n\n\t\t$this->assertFalse( papi_entry_type_exists( 'hello' ) );\n\t\t$this->assertTrue( papi_entry_type_exists( 'empty-page-type' ) );\n\t\t$this->assertTrue( papi_entry_type_exists( 'options/header-option-type' ) );\n\t}\n\n\tpublic function test_papi_get_all_entry_types() {\n\t\t$this->assertEmpty( papi_get_all_entry_types( ['cache' => false] ) );\n\n\t\tadd_filter( 'papi/settings/directories', function () {\n\t\t\treturn [1,  PAPI_FIXTURE_DIR . '/entry-types'];\n\t\t} );\n\n\t\tpapi()->reset();\n\n\t\t$this->assertNotEmpty( papi_get_all_entry_types() );\n\n\t\t$output = papi_get_all_entry_types( [\n\t\t\t'types' => 'entry'\n\t\t] );\n\t\t$this->assertNotEmpty( $output );\n\t\t$this->assertSame( 'Info entry type', $output[0]->name );\n\t}\n\n\tpublic function test_papi_get_all_entry_types_with_same_id() {\n\t\tadd_filter( 'papi/settings/directories', function () {\n\t\t\treturn [PAPI_FIXTURE_DIR . '/entry-types', PAPI_FIXTURE_DIR . '/entry-types2'];\n\t\t} );\n\n\t\tpapi()->reset();\n\n\t\t$output = papi_get_all_entry_types( [\n\t\t\t'types' => 'entry'\n\t\t] );\n\n\t\t$classes = array_map( 'get_class', array_values( $output ) );\n\t\t$this->assertTrue( in_array( 'Term_Entry_Type', $classes ) );\n\t\t$this->assertTrue( strpos( 'entry-types2/term-entry-type.php', $output[0]->get_file_path() ) !== -1 );\n\t}\n\n\tpublic function test_papi_get_all_entry_types_option() {\n\t\tadd_filter( 'papi/settings/directories', function () {\n\t\t\treturn [1,  PAPI_FIXTURE_DIR . '/page-types'];\n\t\t} );\n\n\t\tpapi()->reset();\n\n\t\t$output = papi_get_all_entry_types( [\n\t\t\t'types' => 'option'\n\t\t] );\n\n\t\t$this->assertNotEmpty( $output );\n\t\t$this->assertSame( 'Header', $output[0]->name );\n\t}\n\n\tpublic function test_papi_get_entry_type() {\n\t\t$this->assertNull( papi_get_entry_type( 'hello.php' ) );\n\n\t\t$path = PAPI_FIXTURE_DIR . '/page-types/boxes/simple.php';\n\t\t$this->assertNull( papi_get_entry_type( $path ) );\n\n\t\t$path = PAPI_FIXTURE_DIR . '/entry-types/base-entry-type.php';\n\t\t$this->assertNull( papi_get_entry_type( $path ) );\n\n\t\t$path = PAPI_FIXTURE_DIR . '/page-types/simple-page-type.php';\n\t\t$this->assertNotEmpty( papi_get_entry_type( $path ) );\n\n\t\t$path = PAPI_FIXTURE_DIR . '/page-types2/look-page-type.php';\n\t\t$page_type = papi_get_entry_type( $path );\n\t\t$this->assertTrue( $page_type instanceof Look_Module_Type );\n\t}\n\n\tpublic function test_papi_get_entry_type_by_id() {\n\t\t$this->assertNull( papi_get_entry_type_by_id( 0 ) );\n\t\t$this->assertNull( papi_get_entry_type_by_id( [] ) );\n\t\t$this->assertNull( papi_get_entry_type_by_id( (object) [] ) );\n\t\t$this->assertNull( papi_get_entry_type_by_id( true ) );\n\t\t$this->assertNull( papi_get_entry_type_by_id( false ) );\n\t\t$this->assertNull( papi_get_entry_type_by_id( null ) );\n\t\t$this->assertNull( papi_get_entry_type_by_id( 'page' ) );\n\n\t\tadd_filter( 'papi/settings/directories', function () {\n\t\t\treturn [1,  PAPI_FIXTURE_DIR . '/page-types'];\n\t\t} );\n\n\t\t$simple_page_type = papi_get_entry_type_by_id( 'simple-page-type' );\n\t\t$this->assertTrue( is_object( $simple_page_type ) );\n\n\t\t$settings_option_type = papi_get_entry_type_by_id( 'options/settings-option-type' );\n\t\t$this->assertTrue( is_object( $settings_option_type ) );\n\t}\n\n\tpublic function test_papi_get_entry_type_by_meta_id() {\n\t\t$this->assertNull( papi_get_entry_type_by_meta_id( 0 ) );\n\t\t$this->assertNull( papi_get_entry_type_by_meta_id( [] ) );\n\t\t$this->assertNull( papi_get_entry_type_by_meta_id( (object) [] ) );\n\t\t$this->assertNull( papi_get_entry_type_by_meta_id( true ) );\n\t\t$this->assertNull( papi_get_entry_type_by_meta_id( false ) );\n\t\t$this->assertNull( papi_get_entry_type_by_meta_id( null ) );\n\n\t\tadd_filter( 'papi/settings/directories', function () {\n\t\t\treturn [1,  PAPI_FIXTURE_DIR . '/page-types'];\n\t\t} );\n\n\t\tupdate_post_meta( $this->post_id, papi_get_page_type_key(), 'simple-page-type' );\n\n\t\t$this->assertTrue( is_object( papi_get_entry_type_by_meta_id( $this->post_id ) ) );\n\n\t\t$_GET['page_id'] = $this->post_id;\n\t\t$this->assertTrue( is_object( papi_get_entry_type_by_meta_id() ) );\n\t\tunset( $_GET['page_id'] );\n\t}\n\n\tpublic function test_papi_get_entry_type_template() {\n\t\t$this->assertNull( papi_get_entry_type_template( null ) );\n\t\t$this->assertNull( papi_get_entry_type_template( 0 ) );\n\n\t\tadd_filter( 'papi/settings/directories', function () {\n\t\t\treturn [1,  PAPI_FIXTURE_DIR . '/page-types',  PAPI_FIXTURE_DIR . '/taxonomy-types'];\n\t\t} );\n\n\t\tupdate_post_meta( $this->post_id, papi_get_page_type_key(), 'simple-page-type' );\n\t\t$this->assertSame( 'pages/simple-page.php', papi_get_entry_type_template( $this->post_id ) );\n\n\t\t$term_id = $this->factory->term->create();\n\t\tupdate_term_meta( $term_id, papi_get_page_type_key(), 'simple-taxonomy-type' );\n\t\t$this->assertSame( 'pages/simple-taxonomy.php', papi_get_entry_type_template( $term_id, 'term' ) );\n\t}\n\n\tpublic function test_papi_get_entry_type_template_array() {\n\t\tadd_filter( 'papi/settings/directories', function () {\n\t\t\treturn [1,  PAPI_FIXTURE_DIR . '/page-types',  PAPI_FIXTURE_DIR . '/taxonomy-types'];\n\t\t} );\n\n\t\tupdate_post_meta( $this->post_id, papi_get_page_type_key(), 'modules/video-module-type' );\n\t\t$this->assertSame( 'video-a.php', papi_get_entry_type_template( $this->post_id ) );\n\t}\n\n\tpublic function test_papi_get_entry_type_template_array2() {\n\t\tadd_filter( 'papi/settings/directories', function () {\n\t\t\treturn [1,  PAPI_FIXTURE_DIR . '/page-types',  PAPI_FIXTURE_DIR . '/taxonomy-types'];\n\t\t} );\n\n\t\tupdate_post_meta( $this->post_id, papi_get_page_type_key(), 'modules/string-module-type' );\n\t\t$this->assertSame( 'string-a.php', papi_get_entry_type_template( $this->post_id ) );\n\t}\n\n\tpublic function test_papi_get_entry_type_id() {\n\t\t$_GET['entry_type'] = 'simple-page-type';\n\t\t$this->assertSame( 'simple-page-type', papi_get_entry_type_id() );\n\t\tunset( $_GET['entry_type'] );\n\n\t\t$post_id = $this->factory->post->create();\n\t\t$this->assertEmpty( papi_get_entry_type_id( $post_id ) );\n\t}\n\n\tpublic function test_papi_is_entry_type() {\n\t\t$this->assertFalse( papi_is_entry_type( 'fake-entry-type' ) );\n\n\t\tadd_filter( 'papi/settings/directories', function () {\n\t\t\treturn [1,  PAPI_FIXTURE_DIR . '/page-types'];\n\t\t} );\n\n\t\t$this->assertFalse( papi_is_entry_type( 'info-entry-type' ) );\n\n\t\t$post_id = $this->factory->post->create();\n\t\tupdate_post_meta( $post_id, papi_get_page_type_key(), 'info-entry-type' );\n\n\t\tglobal $post;\n\t\t$post = get_post( $post_id );\n\n\t\t// Returns true if id equals entry type.\n\t\t$this->assertTrue( papi_is_entry_type( 'info-entry-type' ) );\n\n\t\t// Returns true if not empty.\n\t\t$this->assertTrue( papi_is_entry_type() );\n\t}\n}\n"
  },
  {
    "path": "tests/cases/lib/types/page-test.php",
    "content": "<?php\n\nclass Papi_Lib_Types_Page_Test extends WP_UnitTestCase {\n\n\tpublic function setUp() {\n\t\tparent::setUp();\n\t\t$this->post_id = $this->factory->post->create();\n\t}\n\n\tpublic function tearDown() {\n\t\tparent::tearDown();\n\t\tunset( $this->post_id );\n\t}\n\n\tpublic function test_papi_display_page_type() {\n\t\t$this->assertFalse( papi_display_page_type( 'fake-page-type1' ) );\n\n\t\t$_GET['post_type'] = 'page';\n\t\t$this->assertFalse( papi_display_page_type( 'fake-page-type2' ) );\n\n\t\tadd_filter( 'papi/settings/directories', function () {\n\t\t\treturn [1,  PAPI_FIXTURE_DIR . '/page-types'];\n\t\t} );\n\n\t\t$page_type = papi_get_entry_type_by_id( 'display-not-page-type' );\n\t\t$this->assertFalse( papi_display_page_type( $page_type ) );\n\n\t\t$page_type = papi_get_entry_type_by_id( 'empty-page-type' );\n\t\t$this->assertTrue( papi_display_page_type( $page_type ) );\n\n\t\tadd_filter( 'papi/settings/show_page_type_page', function ( $page_type ) {\n\t\t\tif ( $page_type == 'simple-page-type' ) {\n\t\t\t\treturn false;\n\t\t\t}\n\n\t\t\treturn true;\n\t\t} );\n\n\t\t$page_type = papi_get_entry_type_by_id( 'simple-page-type' );\n\t\t$this->assertFalse( papi_display_page_type( $page_type ) );\n\n\t\t$GET['post_type'] = 'module';\n\n\t\t$page_type = papi_get_entry_type_by_id( 'faq-page-type' );\n\t\t$this->assertFalse( papi_display_page_type( $page_type ) );\n\n\t\t$type          = 'papi-standard-page-type';\n\t\t$page_type     = new Papi_Page_Type( $type );\n\t\t$page_type->id = $type;\n\t\t$this->assertTrue( papi_display_page_type( $page_type ) );\n\t}\n\n\tpublic function test_display_for_child_page_type() {\n\t\tadd_filter( 'papi/settings/directories', function () {\n\t\t\treturn [1,  PAPI_FIXTURE_DIR . '/page-types'];\n\t\t} );\n\n\t\t$_GET['post_type'] = 'page';\n\t\t$post_id = $this->factory->post->create();\n\t\tupdate_post_meta( $post_id, papi_get_page_type_key(), 'faq-page-type' );\n\n\t\t$_GET['post_parent'] = $post_id;\n\t\t$page_type = papi_get_entry_type_by_id( 'simple-page-type' );\n\t\t$this->assertTrue( papi_display_page_type( $page_type ) );\n\t}\n\n\tpublic function test_display_for_child_page_type_2() {\n\t\tadd_filter( 'papi/settings/directories', function () {\n\t\t\treturn [1,  PAPI_FIXTURE_DIR . '/page-types'];\n\t\t} );\n\n\t\t$post_id1 = $this->factory->post->create( ['post_type' => 'page'] );\n\t\t$post_id2 = $this->factory->post->create( ['post_type' => 'page', 'parent_post' => $post_id1] );\n\n\t\t$_GET['post_type'] = 'page';\n\t\t$_GET['post_parent'] = $post_id1;\n\n\t\tupdate_post_meta( $post_id1, papi_get_page_type_key(), 'simple-page-type' );\n\t\tupdate_post_meta( $post_id2, papi_get_page_type_key(), 'simple-page-type' );\n\n\t\t$page_type = papi_get_entry_type_by_meta_id( $post_id2 );\n\t\t$this->assertTrue( papi_display_page_type( $page_type ) );\n\t}\n\n\tpublic function test_papi_get_all_page_types() {\n\t\tadd_filter( 'papi/settings/directories', function () {\n\t\t\treturn [1,  PAPI_FIXTURE_DIR . '/page-types'];\n\t\t} );\n\t\tpapi()->reset();\n\t\t$this->assertNotEmpty( papi_get_all_page_types() );\n\t\t$this->assertSame( 1, count( papi_get_all_page_types( 'book' ) ) );\n\t}\n\n\tpublic function test_papi_get_page_type_id_meta_value() {\n\t\t$this->assertEmpty( papi_get_page_type_id() );\n\n\t\tadd_filter( 'papi/settings/directories', function () {\n\t\t\treturn [1,  PAPI_FIXTURE_DIR . '/page-types'];\n\t\t} );\n\n\t\tupdate_post_meta( $this->post_id, papi_get_page_type_key(), 'simple-page-type' );\n\t\t$this->assertSame( 'simple-page-type', papi_get_page_type_id( $this->post_id ) );\n\t}\n\n\tpublic function test_papi_get_page_type_id_query_string() {;\n\t\t$_GET['page_type'] = 'simple-page-type';\n\t\t$this->assertSame( 'simple-page-type', papi_get_page_type_id() );\n\t\tunset( $_GET['page_type'] );\n\t}\n\n\tpublic function test_papi_get_page_type_id_post_parent() {\n\t\t$post_id = $this->factory->post->create();\n\n\t\tadd_filter( 'papi/settings/directories', function () {\n\t\t\treturn [1,  PAPI_FIXTURE_DIR . '/page-types'];\n\t\t} );\n\n\t\tupdate_post_meta( $post_id, papi_get_page_type_key(), 'simple-page-type' );\n\n\t\t$this->assertSame( 'simple-page-type', papi_get_page_type_id( $post_id ) );\n\n\t\t$_GET['post_parent'] = $post_id;\n\t\t$this->assertSame( 'simple-page-type', papi_get_page_type_id() );\n\t\tunset( $_GET['post_parent'] );\n\t}\n\n\tpublic function test_papi_get_page_type_id_container() {\n\t\t$_GET['post_type'] = 'attachment_test';\n\t\tpapi()->bind( 'entry_type_id.post_type.attachment_test', 'others/attachment-type' );\n\t\t$this->assertSame( 'others/attachment-type', papi_get_page_type_id( 0 ) );\n\t\tpapi()->remove( 'entry_type_id.post_type.attachment_test' );\n\t\tunset( $_GET['post_type'] );\n\t}\n\n\tpublic function test_papi_get_page_type_id_only_page_type_filter() {\n\t\t$this->assertEmpty( papi_get_page_type_id() );\n\n\t\t$_GET['post'] = $this->factory->post->create( ['post_type' => 'module'] );\n\t\t$_GET['post_type'] = 'module';\n\n\t\tadd_filter( 'papi/settings/only_page_type_module', function () {\n\t\t\treturn 'modules/feature-module-type';\n\t\t} );\n\n\t\t$this->assertSame( 'modules/feature-module-type', papi_get_page_type_id() );\n\t\tunset( $_GET['post'] );\n\t\tunset( $_GET['post_type'] );\n\t}\n\n\tpublic function test_papi_get_page_type_key() {\n\t\t$this->assertSame( '_papi_page_type', papi_get_page_type_key() );\n\t\t$this->assertSame( '_papi_page_type_switch', papi_get_page_type_key( '_switch' ) );\n\t\t$this->assertSame( '_papi_page_type_switch', papi_get_page_type_key( 'switch' ) );\n\t}\n\n\tpublic function test_papi_get_page_type_name() {\n\t\t$this->assertEmpty( papi_get_page_type_name() );\n\t\t$this->assertEmpty( papi_get_page_type_name( null ) );\n\t\t$this->assertEmpty( papi_get_page_type_name( 0 ) );\n\n\t\tadd_filter( 'papi/settings/directories', function () {\n\t\t\treturn [1,  PAPI_FIXTURE_DIR . '/page-types'];\n\t\t} );\n\n\t\tupdate_post_meta( $this->post_id, papi_get_page_type_key(), 'simple-page-type' );\n\n\t\tglobal $post;\n\t\t$post = get_post( $this->post_id );\n\n\t\t$this->assertSame( 'Simple page', papi_get_page_type_name() );\n\t\t$this->assertSame( 'Simple page', papi_get_page_type_name( $this->post_id ) );\n\t}\n\n\tpublic function test_papi_get_post_types() {\n\t\tpapi()->reset();\n\n\t\t$this->assertEmpty( papi_get_post_types() );\n\n\t\tadd_filter( 'papi/settings/directories', function () {\n\t\t\treturn [1,  PAPI_FIXTURE_DIR . '/page-types'];\n\t\t} );\n\n\t\tpapi()->reset();\n\n\t\t$post_types = papi_get_post_types();\n\n\t\t$this->assertTrue( in_array( 'page', $post_types ) );\n\t}\n\n\tpublic function test_papi_get_standard_page_type() {\n\t\t$page_type = papi_get_standard_page_type( null );\n\t\t$this->assertNull( $page_type );\n\n\t\t$page_type = papi_get_standard_page_type( 'page' );\n\n\t\t$this->assertSame( ['page'], $page_type->post_type );\n\t\t$this->assertSame( 'Standard Page', $page_type->name );\n\t}\n\n\tpublic function test_papi_get_standard_page_type_id() {\n\t\t$this->assertSame( 'papi-standard--type', papi_get_standard_page_type_id( '' ) );\n\t\t$this->assertSame( 'papi-standard-page-type', papi_get_standard_page_type_id( 'page' ) );\n\t}\n\n\tpublic function test_papi_is_page_type() {\n\t\t$this->assertFalse( papi_is_page_type( 'fake-page-type' ) );\n\n\t\tadd_filter( 'papi/settings/directories', function () {\n\t\t\treturn [1,  PAPI_FIXTURE_DIR . '/page-types'];\n\t\t} );\n\n\t\t$this->assertFalse( papi_is_page_type( 'simple-page-type' ) );\n\n\t\t$post_id = $this->factory->post->create();\n\t\tupdate_post_meta( $post_id, papi_get_page_type_key(), 'simple-page-type' );\n\n\t\tglobal $post;\n\t\t$post = get_post( $post_id );\n\n\t\t// Returns true if id equals page type id.\n\t\t$this->assertTrue( papi_is_page_type( 'simple-page-type' ) );\n\n\t\t// Returns true if not empty.\n\t\t$this->assertTrue( papi_is_page_type() );\n\t}\n\n\tpublic function test_papi_set_page_type_id() {\n\t\tadd_filter( 'papi/settings/directories', function () {\n\t\t\treturn [1,  PAPI_FIXTURE_DIR . '/page-types'];\n\t\t} );\n\n\t\t$this->assertFalse( papi_set_page_type_id( 0, 'hello' ) );\n\t\t$post_id = $this->factory->post->create();\n\t\t$this->assertFalse( papi_set_page_type_id( $post_id, 'hello' ) );\n\t\t$this->assertNotFalse( papi_set_page_type_id( $post_id, 'empty-page-type' ) );\n\t\t$this->assertSame( 'empty-page-type', papi_get_page_type_id( $post_id ) );\n\t}\n\n\tpublic function test_the_papi_page_type_name() {\n\t\tthe_papi_page_type_name();\n\t\t$this->expectOutputRegex( '//' );\n\n\t\tthe_papi_page_type_name( null );\n\t\t$this->expectOutputRegex( '//' );\n\n\t\tthe_papi_page_type_name( 0 );\n\t\t$this->expectOutputRegex( '//' );\n\n\t\tadd_filter( 'papi/settings/directories', function () {\n\t\t\treturn [1,  PAPI_FIXTURE_DIR . '/page-types'];\n\t\t} );\n\n\t\tupdate_post_meta( $this->post_id, papi_get_page_type_key(), 'simple-page-type' );\n\n\t\tglobal $post;\n\t\t$post = get_post( $this->post_id );\n\n\t\tthe_papi_page_type_name();\n\t\t$this->expectOutputRegex( '/Simple\\spage/' );\n\n\t\tthe_papi_page_type_name( $this->post_id );\n\t\t$this->expectOutputRegex( '/Simple\\spage/' );\n\n\t\tupdate_post_meta( $this->post_id, papi_get_page_type_key(), '' );\n\t\tthe_papi_page_type_name();\n\t\t$this->expectOutputRegex( '//' );\n\n\t\tupdate_post_meta( $this->post_id, papi_get_page_type_key(), 'random322-page-type' );\n\t\tthe_papi_page_type_name();\n\t\t$this->expectOutputRegex( '//' );\n\t}\n}\n"
  },
  {
    "path": "tests/cases/lib/types/taxonomy-test.php",
    "content": "<?php\n\nclass Papi_Lib_Types_Taxonomy_Test extends WP_UnitTestCase {\n\n\tpublic function setUp() {\n\t\tparent::setUp();\n\n\t\t$this->term_id = $this->factory->term->create();\n\t}\n\n\tpublic function tearDown() {\n\t\tparent::tearDown();\n\t\tunset( $this->term_id );\n\t}\n\n\tpublic function test_papi_get_taxonomy_type_id_meta_value() {\n\t\t$this->assertEmpty( papi_get_taxonomy_type_id() );\n\n\t\tadd_filter( 'papi/settings/directories', function () {\n\t\t\treturn [1,  PAPI_FIXTURE_DIR . '/taxonomy-types'];\n\t\t} );\n\n\t\tupdate_term_meta( $this->term_id, papi_get_page_type_key(), 'properties-taxonomy-type' );\n\t\t$this->assertSame( 'properties-taxonomy-type', papi_get_taxonomy_type_id( $this->term_id ) );\n\t\tdelete_term_meta( $this->term_id, papi_get_page_type_key() );\n\t}\n\n\tpublic function test_papi_get_taxonomy_type_id_container() {\n\t\t$_GET['taxonomy'] = 'post_tag';\n\t\tpapi()->bind( 'entry_type_id.taxonomy.post_tag', 'properties-taxonomy-type' );\n\t\t$this->assertSame( 'properties-taxonomy-type', papi_get_taxonomy_type_id( 0 ) );\n\t\tpapi()->remove( 'entry_type_id.taxonomy.post_tag' );\n\t\tunset( $_GET['taxonomy'] );\n\t}\n\n\tpublic function test_papi_get_taxonomy_type_name() {\n\t\t$this->assertEmpty( papi_get_taxonomy_type_name() );\n\t\t$this->assertEmpty( papi_get_taxonomy_type_name( null ) );\n\t\t$this->assertEmpty( papi_get_taxonomy_type_name( 0 ) );\n\n\t\tadd_filter( 'papi/settings/directories', function () {\n\t\t\treturn [1,  PAPI_FIXTURE_DIR . '/taxonomy-types'];\n\t\t} );\n\n\t\tupdate_term_meta( $this->term_id, papi_get_page_type_key(), 'simple-taxonomy-type' );\n\n\t\t$_GET['term_id'] = $this->term_id;\n\n\t\t$this->assertSame( 'Simple taxonomy', papi_get_taxonomy_type_name() );\n\t\t$this->assertSame( 'Simple taxonomy', papi_get_taxonomy_type_name( $this->term_id ) );\n\n\t\tupdate_term_meta( $this->term_id, papi_get_page_type_key(), 'simple-taxonomy-type2' );\n\n\t\t$this->assertEmpty( papi_get_taxonomy_type_name( $this->term_id ) );\n\t}\n\n\tpublic function test_papi_get_taxonomies() {\n\t\tpapi()->reset();\n\n\t\t$this->assertEmpty( papi_get_taxonomies() );\n\n\t\tadd_filter( 'papi/settings/directories', function () {\n\t\t\treturn [1,  PAPI_FIXTURE_DIR . '/taxonomy-types'];\n\t\t} );\n\n\t\tpapi()->reset();\n\n\t\t$taxonomies = papi_get_taxonomies();\n\n\t\t$this->assertTrue( in_array( 'post_tag', $taxonomies ) );\n\t}\n\n\tpublic function test_papi_load_taxonomy_type_id() {\n\t\t$this->assertEmpty( papi_load_taxonomy_type_id() );\n\n\t\tadd_filter( 'papi/settings/directories', function () {\n\t\t\treturn [1,  PAPI_FIXTURE_DIR . '/taxonomy-types'];\n\t\t} );\n\n\t\t$_GET['term_id'] = $this->term_id;\n\t\tupdate_term_meta( $this->term_id, papi_get_page_type_key(), 'properties-taxonomy-type' );\n\t\t$this->assertSame( 'properties-taxonomy-type', papi_load_taxonomy_type_id() );\n\t\tdelete_term_meta( $this->term_id, papi_get_page_type_key() );\n\t\tunset( $_GET['term_id'] );\n\n\t\t$_GET['taxonomy'] = 'post_tag';\n\t\tpapi()->bind( 'entry_type_id.taxonomy.post_tag', 'properties-taxonomy-type' );\n\t\t$this->assertSame( 'properties-taxonomy-type', papi_load_taxonomy_type_id() );\n\t\tpapi()->remove( 'entry_type_id.taxonomy.post_tag' );\n\t\tunset( $_GET['taxonomy'] );\n\t}\n\n\tpublic function test_papi_set_taxonomy_type_id() {\n\t\tadd_filter( 'papi/settings/directories', function () {\n\t\t\treturn [1,  PAPI_FIXTURE_DIR . '/taxonomy-types'];\n\t\t} );\n\n\t\t$this->assertFalse( papi_set_taxonomy_type_id( 0, 'hello' ) );\n\t\t$term_id = $this->factory->term->create();\n\t\t$this->assertFalse( papi_set_taxonomy_type_id( $term_id, 'hello' ) );\n\t\t$this->assertNotFalse( papi_set_taxonomy_type_id( $term_id, 'simple-taxonomy-type' ) );\n\t\t$this->assertSame( 'simple-taxonomy-type', papi_get_taxonomy_type_id( $term_id ) );\n\t}\n\n\tpublic function test_the_papi_taxonomy_type_name() {\n\t\tthe_papi_taxonomy_type_name();\n\t\t$this->expectOutputRegex( '//' );\n\n\t\tthe_papi_taxonomy_type_name( null );\n\t\t$this->expectOutputRegex( '//' );\n\n\t\tthe_papi_taxonomy_type_name( 0 );\n\t\t$this->expectOutputRegex( '//' );\n\n\t\tadd_filter( 'papi/settings/directories', function () {\n\t\t\treturn [1,  PAPI_FIXTURE_DIR . '/taxonomy-types'];\n\t\t} );\n\n\t\tupdate_post_meta( $this->term_id, papi_get_page_type_key(), 'simple-taxonomy-type' );\n\n\t\t$_GET['term_id'] = $this->term_id;\n\t\tthe_papi_taxonomy_type_name();\n\t\t$this->expectOutputRegex( '/Simple\\staxonomy/' );\n\n\t\tthe_papi_taxonomy_type_name( $this->term_id );\n\t\t$this->expectOutputRegex( '/Simple\\staxonomy/' );\n\n\t\tupdate_post_meta( $this->term_id, papi_get_page_type_key(), '' );\n\t\tthe_papi_taxonomy_type_name();\n\t\t$this->expectOutputRegex( '//' );\n\n\t\tupdate_post_meta( $this->term_id, papi_get_page_type_key(), 'random322-page-type' );\n\t\tthe_papi_taxonomy_type_name();\n\t\t$this->expectOutputRegex( '//' );\n\t}\n}\n"
  },
  {
    "path": "tests/cases/papi-loader-test.php",
    "content": "<?php\n\nclass Papi_Loader_Test extends WP_UnitTestCase {\n\n\tpublic function setUp() {\n\t\tparent::setUp();\n\t\tadd_filter( 'wp_die_ajax_handler', [$this, 'get_wp_die_handler'], 1, 1 );\n\t}\n\n\tpublic function tearDown() {\n\t\tparent::tearDown();\n\t\tremove_filter( 'wp_die_ajax_handler', [$this, 'get_wp_die_handler'], 1, 1 );\n\t}\n\n\tpublic function deprecated_function_run( $function ) {\n\t}\n\n\tpublic function wp_die_handler( $message ) {\n\t}\n\n\tpublic function test_plugins_loaded_action() {\n\t\t$this->assertSame( 10, has_action( 'plugins_loaded', 'papi' ) );\n\t}\n\n\tpublic function test_papi_before_init_action() {\n\t\tglobal $wp_actions;\n\n\t\tPapi_Loader::deactivate();\n\n\t\tunset( $wp_actions['papi/before_init'] );\n\n\t\tadd_action( 'papi/before_init', function () {\n\t\t\t$this->assertTrue( true );\n\t\t} );\n\n\t\tPapi_Loader::instance();\n\t}\n\n\tpublic function test_papi_init_action() {\n\t\tglobal $wp_actions;\n\n\t\tPapi_Loader::deactivate();\n\n\t\tunset( $wp_actions['papi/init'] );\n\n\t\tadd_action( 'papi/init', function () {\n\t\t\t$this->assertTrue( true );\n\t\t} );\n\n\t\tPapi_Loader::instance();\n\t}\n\n\t/**\n\t * Deprecated.\n\t *\n\t * @since 3.0.0\n\t */\n\tpublic function test_papi_include() {\n\t\tglobal $wp_actions;\n\n\t\tPapi_Loader::deactivate();\n\n\t\tunset( $wp_actions['papi/include'] );\n\n\t\tadd_action( 'papi/include', function () {\n\t\t\t$this->assertTrue( true );\n\t\t} );\n\n\t\tPapi_Loader::instance();\n\t}\n\n\tpublic function test_papi_loaded_action() {\n\t\tglobal $wp_actions;\n\n\t\tPapi_Loader::deactivate();\n\n\t\tunset( $wp_actions['papi/loaded'] );\n\n\t\tadd_action( 'papi/loaded', function () {\n\t\t\t$this->assertTrue( true );\n\t\t} );\n\n\t\tPapi_Loader::instance();\n\t}\n\n\tpublic function test_constants() {\n\t\t$this->assertTrue( defined( 'PAPI_PLUGIN_DIR' ) );\n\t\t$this->assertTrue( defined( 'PAPI_PLUGIN_URL' ) );\n\t\t$this->assertTrue( defined( 'PAPI_PAGE_TYPE_KEY' ) );\n\t}\n\n\tpublic function test_instance() {\n\t\t$this->assertClassHasStaticAttribute( 'instance', 'Papi_Loader' );\n\t}\n\n\tpublic function test_plugin_activated() {\n\t\t$this->assertTrue( class_exists( 'Papi_Loader' ) && class_exists( 'Papi_Admin' ) );\n\t}\n\n\tpublic function test_name() {\n\t\t$papi = Papi_Loader::instance();\n\t\t$this->assertSame( 'Papi', $papi->name );\n\t}\n}\n"
  },
  {
    "path": "tests/cases/properties/class-papi-property-bool-test.php",
    "content": "<?php\n\n/**\n * @group properties\n */\nclass Papi_Property_Bool_Test extends Papi_Property_Test_Case {\n\n\tpublic $slug = 'bool_test';\n\n\tpublic function get_value() {\n\t\treturn true;\n\t}\n\n\tpublic function get_expected() {\n\t\treturn true;\n\t}\n\n\tpublic function test_property_convert_type() {\n\t\t$this->assertSame( 'bool', $this->property->convert_type );\n\t}\n\n\tpublic function test_property_default_value() {\n\t\t$this->assertFalse( $this->property->default_value );\n\t}\n\n\tpublic function test_property_format_value() {\n\t\t$this->assertFalse( $this->property->format_value( 'false', '', 0 ) );\n\t\t$this->assertFalse( $this->property->format_value( '', '', 0 ) );\n\t\t$this->assertFalse( $this->property->format_value( null, '', 0 ) );\n\t\t$this->assertFalse( $this->property->format_value( (object) [], '', 0 ) );\n\t\t$this->assertFalse( $this->property->format_value( [], '', 0 ) );\n\t\t$this->assertTrue( $this->property->format_value( 'true', '', 0 ) );\n\t\t$this->assertTrue( $this->property->format_value( true, '', 0 ) );\n\t}\n\n\tpublic function test_property_options() {\n\t\t$this->assertSame( 'bool', $this->property->get_option( 'type' ) );\n\t\t$this->assertSame( 'Bool test', $this->property->get_option( 'title' ) );\n\t\t$this->assertSame( 'papi_bool_test', $this->property->get_option( 'slug' ) );\n\t}\n\n\tpublic function test_property_load_value() {\n\t\t$this->assertTrue( $this->property->load_value( '1', '', 0 ) );\n\t\t$this->assertFalse( $this->property->load_value( [], '', 0 ) );\n\t}\n\n\tpublic function test_property_update_value() {\n\t\t$this->assertNull( $this->property->update_value( 'false', '', 0 ) );\n\t\t$this->assertNull( $this->property->update_value( '', '', 0 ) );\n\t\t$this->assertNull( $this->property->update_value( null, '', 0 ) );\n\t\t$this->assertNull( $this->property->update_value( (object) [], '', 0 ) );\n\t\t$this->assertNull( $this->property->update_value( [], '', 0 ) );\n\t\t$this->assertTrue( $this->property->update_value( 'true', '', 0 ) );\n\t\t$this->assertTrue( $this->property->update_value( true, '', 0 ) );\n\t}\n}\n"
  },
  {
    "path": "tests/cases/properties/class-papi-property-checkbox-test.php",
    "content": "<?php\n\n/**\n * @group properties\n */\nclass Papi_Property_Checkbox_Test extends Papi_Property_Test_Case {\n\n\tpublic $slug = 'checkbox_test';\n\n\tpublic function get_value() {\n\t\treturn '#ffffff';\n\t}\n\n\tpublic function get_expected() {\n\t\treturn ['#ffffff'];\n\t}\n\n\tpublic function test_property_convert_type() {\n\t\t$this->assertSame( 'array', $this->property->convert_type );\n\t}\n\n\tpublic function test_property_default_value() {\n\t\t$this->assertSame( [], $this->property->default_value );\n\t}\n\n\tpublic function test_property_format_value() {\n\t\t$this->assertSame( ['hello'], $this->property->format_value( 'hello', '', 0 ) );\n\t\t$this->assertSame( ['hello'], $this->property->format_value( ['hello'], '', 0 ) );\n\t\t$this->assertSame( [2014], $this->property->format_value( ['2014'], '', 0 ) );\n\t\t$this->assertSame( [12.3], $this->property->format_value( ['12.3'], '', 0 ) );\n\t\t$this->assertSame( [true], $this->property->format_value( ['true'], '', 0 ) );\n\t\t$this->assertSame( [false], $this->property->format_value( ['false'], '', 0 ) );\n\t\t$this->assertSame( [2014], $this->property->format_value( '2014', '', 0 ) );\n\t\t$this->assertSame( [12.3], $this->property->format_value( '12.3', '', 0 ) );\n\t\t$this->assertSame( [true], $this->property->format_value( 'true', '', 0 ) );\n\t\t$this->assertSame( [false], $this->property->format_value( 'false', '', 0 ) );\n\t\t$this->assertEmpty( $this->property->format_value( null, '', 0 ) );\n\t}\n\n\tpublic function test_property_options() {\n\t\t$this->assertSame( 'checkbox', $this->property->get_option( 'type' ) );\n\t\t$this->assertSame( 'Checkbox test', $this->property->get_option( 'title' ) );\n\t\t$this->assertSame( 'papi_checkbox_test', $this->property->get_option( 'slug' ) );\n\t}\n\n\tpublic function test_property_settings() {\n\t\t$this->assertNotEmpty( $this->property->get_setting( 'items' ) );\n\t}\n}\n"
  },
  {
    "path": "tests/cases/properties/class-papi-property-color-test.php",
    "content": "<?php\n\n/**\n * @group properties\n */\nclass Papi_Property_Color_Test extends Papi_Property_Test_Case {\n\n\tpublic $slug = 'color_test';\n\n\tpublic function get_value() {\n\t\treturn '#000000';\n\t}\n\n\tpublic function get_expected() {\n\t\treturn '#000000';\n\t}\n\n\tpublic function test_property_format_value() {\n\t\t$this->assertSame( '#000000', $this->property->format_value( '#000000', '', 0 ) );\n\t\t$this->assertSame( '#ffffff', $this->property->format_value( '#ffffff', '', 0 ) );\n\t\t$this->assertEmpty( $this->property->format_value( null, '', 0 ) );\n\t}\n\n\tpublic function test_property_options() {\n\t\t$this->assertSame( 'color', $this->property->get_option( 'type' ) );\n\t\t$this->assertSame( 'Color test', $this->property->get_option( 'title' ) );\n\t\t$this->assertSame( 'papi_color_test', $this->property->get_option( 'slug' ) );\n\t}\n}\n"
  },
  {
    "path": "tests/cases/properties/class-papi-property-datetime-test.php",
    "content": "<?php\n\n/**\n * @group properties\n */\nclass Papi_Property_Datetime_Test extends Papi_Property_Test_Case {\n\n\tpublic $slug = 'datetime_test';\n\n\tpublic function get_value() {\n\t\treturn '2014-11-23';\n\t}\n\n\tpublic function get_expected() {\n\t\treturn '2014-11-23';\n\t}\n\n\tpublic function test_property_format_value() {\n\t\t$this->assertSame( '2014-11-23', $this->property->format_value( '2014-11-23', '', 0 ) );\n\t\t$this->assertSame( '2014-11-24', $this->property->format_value( '2014-11-24', '', 0 ) );\n\t\t$this->assertEmpty( $this->property->format_value( null, '', 0 ) );\n\t}\n\n\tpublic function test_property_options() {\n\t\t$this->assertSame( 'datetime', $this->property->get_option( 'type' ) );\n\t\t$this->assertSame( 'Datetime test', $this->property->get_option( 'title' ) );\n\t\t$this->assertSame( 'papi_datetime_test', $this->property->get_option( 'slug' ) );\n\t}\n}\n"
  },
  {
    "path": "tests/cases/properties/class-papi-property-divider-test.php",
    "content": "<?php\n\n/**\n * @group properties\n */\nclass Papi_Property_Divider_Test extends Papi_Property_Test_Case {\n\n\tpublic $slug = 'divider_test';\n\n\tpublic function get_value() {\n\t\treturn;\n\t}\n\n\tpublic function get_expected() {\n\t\treturn;\n\t}\n\n\tpublic function test_property_options() {\n\t\t$this->assertSame( 'divider', $this->property->get_option( 'type' ) );\n\t\t$this->assertSame( 'Divider test', $this->property->get_option( 'title' ) );\n\t\t$this->assertSame( 'papi_divider_test', $this->property->get_option( 'slug' ) );\n\t}\n}\n"
  },
  {
    "path": "tests/cases/properties/class-papi-property-dropdown-test.php",
    "content": "<?php\n\n/**\n * @group properties\n */\nclass Papi_Property_Dropdown_Test extends Papi_Property_Test_Case {\n\n\tpublic $slugs = ['dropdown_test', 'dropdown_test_2', 'dropdown_test_3', 'dropdown_test_4'];\n\n\tpublic function get_value() {\n\t\t$args = func_get_args();\n\t\t$args[0] = isset( $args[0] ) ? $args[0] : $this->slugs[0];\n\t\tswitch ( $args[0] ) {\n\t\t\tcase 'dropdown_test_4':\n\t\t\t\treturn 2;\n\t\t\tcase 'dropdown_test_3':\n\t\t\t\treturn [1, 2, 3];\n\t\t\tdefault:\n\t\t\t\treturn '#ffffff';\n\t\t}\n\t}\n\n\tpublic function get_expected() {\n\t\t$args = func_get_args();\n\t\t$args[0] = isset( $args[0] ) ? $args[0] : $this->slugs[0];\n\t\tswitch ( $args[0] ) {\n\t\t\tcase 'dropdown_test_4':\n\t\t\t\treturn 2;\n\t\t\tcase 'dropdown_test_3':\n\t\t\t\treturn [1, 2, 3];\n\t\t\tdefault:\n\t\t\t\treturn '#ffffff';\n\t\t}\n\t}\n\n\tpublic function test_property_convert_type() {\n\t\t$this->assertSame( 'mixed', $this->properties[0]->convert_type );\n\t\t$this->assertSame( 'mixed', $this->properties[1]->convert_type );\n\t\t$this->assertSame( 'mixed', $this->properties[2]->convert_type );\n\t}\n\n\tpublic function test_property_format_value() {\n\t\t$this->assertSame( '#ffffff', $this->properties[0]->format_value( '#ffffff', '', 0 ) );\n\t\t$this->assertSame( '#000000', $this->properties[1]->format_value( '#000000', '', 0 ) );\n\t\t$this->assertSame( '2014', $this->properties[1]->format_value( '2014', '', 0 ) );\n\t\t$this->assertSame( '12.3', $this->properties[1]->format_value( '12.3', '', 0 ) );\n\t\t$this->assertSame( 'true', $this->properties[1]->format_value( 'true', '', 0 ) );\n\t\t$this->assertSame( 'false', $this->properties[1]->format_value( 'false', '', 0 ) );\n\t\t$this->assertSame( [1, 2, 3], $this->properties[1]->format_value( [1, 2, 3], '', 0 ) );\n\t\t$this->assertSame( ['1', '2', '3'], $this->properties[1]->format_value( ['1', '2', '3'], '', 0 ) );\n\t\t$this->assertEmpty( $this->properties[0]->format_value( null, '', 0 ) );\n\t\t$this->assertEmpty( $this->properties[1]->format_value( null, '', 0 ) );\n\t\t$this->assertEmpty( $this->properties[2]->format_value( null, '', 0 ) );\n\t}\n\n\tpublic function test_property_options() {\n\t\t$this->assertSame( 'dropdown', $this->properties[0]->get_option( 'type' ) );\n\t\t$this->assertSame( 'Dropdown test', $this->properties[0]->get_option( 'title' ) );\n\t\t$this->assertSame( 'papi_dropdown_test', $this->properties[0]->get_option( 'slug' ) );\n\n\t\t$this->assertSame( 'dropdown', $this->properties[1]->get_option( 'type' ) );\n\t\t$this->assertSame( 'Dropdown test 2', $this->properties[1]->get_option( 'title' ) );\n\t\t$this->assertSame( 'papi_dropdown_test_2', $this->properties[1]->get_option( 'slug' ) );\n\n\t\t$this->assertSame( 'dropdown', $this->properties[2]->get_option( 'type' ) );\n\t\t$this->assertSame( 'Dropdown test 3', $this->properties[2]->get_option( 'title' ) );\n\t\t$this->assertSame( 'papi_dropdown_test_3', $this->properties[2]->get_option( 'slug' ) );\n\t}\n\n\tpublic function test_property_settings() {\n\t\t$settings = $this->properties[0]->get_settings();\n\t\t$this->assertEmpty( $settings->placeholder );\n\t\t$this->assertSame( '#ffffff', $settings->items['White'] );\n\t\t$this->assertSame( '#000000', $settings->items['Black'] );\n\t\t$this->assertTrue( $settings->select2 );\n\n\t\t$settings = $this->properties[1]->get_settings();\n\t\t$this->assertSame( 'Pick one', $settings->placeholder );\n\t\t$this->assertSame( '#ffffff', $settings->items['White'] );\n\t\t$this->assertSame( '#000000', $settings->items['Black'] );\n\t\t$this->assertTrue( $settings->select2 );\n\n\t\t$settings = $this->properties[2]->get_settings();\n\t\t$this->assertSame( 'Pick multiple', $settings->placeholder );\n\t\t$this->assertSame( [0, 1, 2, 3, 4, 5], $settings->items );\n\t\t$this->assertTrue( $settings->select2 );\n\t}\n}\n"
  },
  {
    "path": "tests/cases/properties/class-papi-property-editor-test.php",
    "content": "<?php\n\n/**\n * @group properties\n */\nclass Papi_Property_Editor_Test extends Papi_Property_Test_Case {\n\n\tpublic $slug = 'editor_test';\n\n\tpublic function get_value() {\n\t\treturn '<p>a bit of text with html tags hello, world</p>';\n\t}\n\n\tpublic function get_expected() {\n\t\treturn \"<p>a bit of text with html tags hello, world</p>\\n\";\n\t}\n\n\tpublic function test_mce_buttons() {\n\t\t$property = papi_property( [\n\t\t\t'title' => 'Editor',\n\t\t\t'type'  => 'editor'\n\t\t] );\n\t\t$this->assertEmpty( $property->mce_buttons() );\n\n\t\tglobal $wp_current_filter;\n\n\t\t$wp_current_filter[] = 'mce_buttons';\n\t\t$property = papi_property( [\n\t\t\t'title'    => 'Editor',\n\t\t\t'type'     => 'editor',\n\t\t\t'settings' => [\n\t\t\t\t'mce_buttons' => ['bold']\n\t\t\t]\n\t\t] );\n\t\t$this->assertSame( ['bold'], $property->mce_buttons() );\n\n\t\t$wp_current_filter[] = 'mce_buttons_2';\n\t\t$property = papi_property( [\n\t\t\t'title'    => 'Editor',\n\t\t\t'type'     => 'editor',\n\t\t\t'settings' => [\n\t\t\t\t'mce_buttons'   => ['bold'],\n\t\t\t\t'mce_buttons_2' => 'wp_help'\n\t\t\t]\n\t\t] );\n\t\t$this->assertSame( ['wp_help'], $property->mce_buttons() );\n\t}\n\n\tpublic function test_property_default_value() {\n\t\t$post_id = $this->factory->post->create( ['post_status' => 'auto-draft'] );\n\t\t$property = clone $this->property;\n\t\t$property->set_post_id( $post_id );\n\t\t$property->set_option( 'default', 'You need to update your profile, <a href=\"/profile/update\">click here!</a>' );\n\t\tpapi_render_property( $property );\n\t\t$this->expectOutputRegex( '/\\>You need to update your profile, \\<a href\\=\\\"\\/profile\\/update\\\"\\>click here!\\<\\/a\\>\\<\\/textarea\\>/' );\n\t}\n\n\tpublic function test_property_format_value() {\n\t\t$this->assertSame( $this->get_expected(), $this->property->format_value( $this->get_value(), '', 0 ) );\n\t}\n\n\tpublic function test_property_options() {\n\t\t$this->assertSame( 'editor', $this->property->get_option( 'type' ) );\n\t\t$this->assertSame( 'Editor test', $this->property->get_option( 'title' ) );\n\t\t$this->assertSame( 'papi_editor_test', $this->property->get_option( 'slug' ) );\n\t}\n}\n"
  },
  {
    "path": "tests/cases/properties/class-papi-property-email-test.php",
    "content": "<?php\n\n/**\n * @group properties\n */\nclass Papi_Property_Email_Test extends Papi_Property_Test_Case {\n\n\tpublic $slug = 'email_test';\n\n\tpublic function get_value() {\n\t\treturn 'info@github.com';\n\t}\n\n\tpublic function get_expected() {\n\t\treturn 'info@github.com';\n\t}\n\n\tpublic function test_property_options() {\n\t\t$this->assertSame( 'email', $this->property->get_option( 'type' ) );\n\t\t$this->assertSame( 'Email test', $this->property->get_option( 'title' ) );\n\t\t$this->assertSame( 'papi_email_test', $this->property->get_option( 'slug' ) );\n\t}\n}\n"
  },
  {
    "path": "tests/cases/properties/class-papi-property-file-test.php",
    "content": "<?php\n\n/**\n * @group properties\n */\nclass Papi_Property_File_Test extends Papi_Property_Test_Case {\n\n\tpublic $slugs = ['file_test', 'file_test_3'];\n\n\tpublic function get_value() {\n\t\t$args = func_get_args();\n\t\t$args[0] = isset( $args[0] ) ? $args[0] : $this->slugs[0];\n\t\tswitch ( $args[0] ) {\n\t\t\tdefault:\n\t\t\t\treturn 23;\n\t\t}\n\t}\n\n\tpublic function get_expected() {\n\t\t$args = func_get_args();\n\t\t$args[0] = isset( $args[0] ) ? $args[0] : $this->slugs[0];\n\t\tswitch ( $args[0] ) {\n\t\t\tdefault:\n\t\t\t\treturn 23;\n\t\t}\n\t}\n\n\tpublic function assert_values( $expected, $actual, $slug ) {\n\t\tswitch ( $slug ) {\n\t\t\tcase 'file_test_3';\n\t\t\t\tbreak; // fail ok since we don't save a custom id.\n\t\t\tdefault:\n\t\t\t\t$this->assertSame( $expected, $actual );\n\t\t\t\tbreak;\n\t\t}\n\t}\n\n\tpublic function test_property_convert_type() {\n\t\t$this->assertSame( 'object', $this->property->convert_type );\n\t}\n\n\tpublic function test_property_default_value() {\n\t\t$this->assertSame( [], $this->property->default_value );\n\t}\n\n\tpublic function test_property_format_value() {\n\t\t$this->assertSame( $this->get_expected(), $this->property->format_value( $this->get_value(), '', 0 ) );\n\t}\n\n\tpublic function test_property_format_value_wrong_values() {\n\t\t$this->assertNull( $this->property->format_value( new stdClass, '', 0 ) );\n\t\t$this->assertTrue( $this->property->format_value( true, '', 0 ) );\n\t\t$this->assertFalse( $this->property->format_value( false, '', 0 ) );\n\t\t$this->assertNull( $this->property->format_value( null, '', 0 ) );\n\t}\n\n\tpublic function test_property_format_value_meta_key() {\n\t\t$post_id = $this->factory->post->create( ['post_type' => 'attachment'] );\n\t\t$this->assertSame( 0, intval( $this->properties[1]->format_value( 0, '', 0 ) ) );\n\t\tupdate_post_meta( $post_id, 'custom_id', 1 );\n\t\t$this->assertNotSame( 1, intval( $this->properties[1]->format_value( 1, '', 0 ) ) );\n\t}\n\n\tpublic function test_property_options() {\n\t\t$this->assertSame( 'file', $this->property->get_option( 'type' ) );\n\t\t$this->assertSame( 'File test', $this->property->get_option( 'title' ) );\n\t\t$this->assertSame( 'papi_file_test', $this->property->get_option( 'slug' ) );\n\t}\n\n\tpublic function test_property_render_file_template() {\n\t\t$this->property->render_file_template();\n\t\t$this->expectOutputRegex( '/.*\\S.*/' );\n\t}\n\n\tpublic function test_property_settings() {\n\t\t$this->assertFalse( $this->property->get_setting( 'multiple' ) );\n\t}\n\n\tpublic function test_property_wp_get_attachment_metadata() {\n\t\t$post_id = $this->factory->post->create( ['post_type' => 'attachment'] );\n\t\t$this->assertSame( '', $this->property->wp_get_attachment_metadata( null, $post_id ) );\n\t\t$this->assertSame( '', $this->property->wp_get_attachment_metadata( [], $post_id ) );\n\t\t$this->assertSame( '', $this->property->wp_get_attachment_metadata( '', $post_id ) );\n\n\t\tupdate_post_meta( $post_id, '_wp_attached_file', 'file.jpg' );\n\t\t$this->assertSame( 'file.jpg', $this->property->wp_get_attachment_metadata( null, $post_id ) );\n\t\t$this->assertSame( 'file.jpg', $this->property->wp_get_attachment_metadata( [], $post_id ) );\n\t\t$this->assertSame( 'file.jpg', $this->property->wp_get_attachment_metadata( '', $post_id ) );\n\t}\n\n\tpublic function test_property_wp_get_attachment_metadata_wrong_values() {\n\t\t$this->assertSame( true, $this->property->wp_get_attachment_metadata( true, 0 ) );\n\t\t$this->assertSame( false, $this->property->wp_get_attachment_metadata( false, 0 ) );\n\t\t$this->assertEquals( new stdClass, $this->property->wp_get_attachment_metadata( new stdClass, 0 ) );\n\t\t$this->assertSame( 1, $this->property->wp_get_attachment_metadata( 1, 0 ) );\n\t}\n}\n"
  },
  {
    "path": "tests/cases/properties/class-papi-property-flexible-test.php",
    "content": "<?php\n\n/**\n * @group properties\n */\nclass Papi_Property_Flexible_Test extends Papi_Property_Test_Case {\n\n\tpublic $slug = 'flexible_test';\n\n\tpublic function assert_values( $expected, $actual, $slug ) {\n\t\tfor ( $i = 0, $l = count( $expected ); $i < $l; $i++ ) {\n\t\t\tforeach ( $expected[$i] as $key => $value ) {\n\t\t\t\tif ( $value instanceof WP_Post ) {\n\t\t\t\t\t$this->assertSame( $expected[$i][$key]->ID, $actual[$i][$key]->ID );\n\t\t\t\t} else {\n\t\t\t\t\t$this->assertSame( $expected[$i][$key], $actual[$i][$key] );\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\tpublic function get_value() {\n\t\t$items = $this->property->get_setting( 'items' );\n\n\t\t$value_slug1         = unpapify( $items['twitter']['items'][0]->slug );\n\t\t$value_type_slug1    = papi_get_property_type_key( $value_slug1 );\n\t\t$value_slug2_1       = unpapify( $items['posts']['items'][0]->slug );\n\t\t$value_type_slug2_1  = papi_get_property_type_key( $value_slug2_1 );\n\t\t$value_slug2_2       = unpapify( $items['posts']['items'][1]->slug );\n\t\t$value_type_slug2_2  = papi_get_property_type_key( $value_slug2_2 );\n\n\t\t$item1 = [];\n\t\t$item1[$value_slug1] = '@frozzare';\n\t\t$item1[$value_type_slug1] = $items['twitter']['items'][0];\n\t\t$item1[$value_slug1 . '_flexible_layout'] = '_flexible_layout_twitter';\n\n\t\t$item2 = [];\n\t\t$item2[$value_slug2_1] = $this->post_id;\n\t\t$item2[$value_type_slug2_1] = $items['posts']['items'][0];\n\t\t$item2[$value_slug2_1 . '_flexible_layout'] = '_flexible_layout_posts';\n\t\t$item2[$value_slug2_2] = $this->post_id;\n\t\t$item2[$value_type_slug2_2] = $items['posts']['items'][0];\n\t\t$item2[$value_slug2_2 . '_flexible_layout'] = '_flexible_layout_posts';\n\n\t\treturn [$item1, $item2];\n\t}\n\n\tpublic function get_expected() {\n\t\treturn [\n\t\t\t[ 'twitter_name' => '@frozzare', '_layout' => 'twitter' ],\n\t\t\t[ 'post_one' => get_post( $this->post_id ), 'post_two' => get_post( $this->post_id ), '_layout' => 'posts' ]\n\t\t];\n\t}\n\n\tpublic function test_property_convert_type() {\n\t\t$this->assertSame( 'array', $this->property->convert_type );\n\t}\n\n\tpublic function test_property_default_value() {\n\t\t$this->assertSame( [], $this->property->default_value );\n\t}\n\n\tpublic function test_property_format_value() {\n\t\t$this->assertEmpty( $this->property->format_value( '', $this->slug, $this->post_id ) );\n\t\t$this->assertEmpty( $this->property->format_value( (object) [], $this->slug, $this->post_id ) );\n\t\t$this->assertEmpty( $this->property->format_value( 1, $this->slug, $this->post_id ) );\n\t\t$this->assertEmpty( $this->property->format_value( null, $this->slug, $this->post_id ) );\n\t\t$this->assertEmpty( $this->property->format_value( [], $this->slug, $this->post_id ) );\n\t\t$this->assertEmpty( $this->property->format_value( true, $this->slug, $this->post_id ) );\n\t\t$this->assertEmpty( $this->property->format_value( false, $this->slug, $this->post_id ) );\n\t}\n\n\tpublic function test_property_load_value() {\n\t\t$this->assertSame( [], $this->property->load_value( [], '', 0 ) );\n\t}\n\n\tpublic function test_property_options() {\n\t\t$this->assertSame( 'flexible', $this->property->get_option( 'type' ) );\n\t\t$this->assertSame( 'Flexible test', $this->property->get_option( 'title' ) );\n\t\t$this->assertSame( 'papi_flexible_test', $this->property->get_option( 'slug' ) );\n\t}\n\n\tpublic function test_property_setting_add_new_label() {\n\t\t$property = Papi_Property::factory( [\n\t\t\t'settings' => [\n\t\t\t\t'add_new_label' => 'Add new slide'\n\t\t\t],\n\t\t\t'title'    => 'Flexible',\n\t\t\t'type'     => 'flexible'\n\t\t] );\n\n\t\t$this->assertSame( 'Add new slide', $property->get_setting( 'add_new_label' ) );\n\t}\n\n\tpublic function test_property_render_repeater_rows_template() {\n\t\t$this->property->render_repeater_rows_template();\n\t\t$this->expectOutputRegex( '/.*\\S.*/' );\n\t}\n\n\tpublic function test_property_settings() {\n\t\t$items = $this->property->get_setting( 'items' );\n\t\t$this->assertNotEmpty( $items );\n\n\t\t$this->assertSame( 'Twitter', $items['twitter']['title'] );\n\t\t$this->assertSame( 'string', $items['twitter']['items'][0]->type );\n\t\t$this->assertSame( 'papi_twitter_name', $items['twitter']['items'][0]->slug );\n\t\t$this->assertSame( 'Twitter name', $items['twitter']['items'][0]->title );\n\t\t$this->assertSame( 'Posts', $items['posts']['title'] );\n\n\t\t$this->assertSame( 'post', $items['posts']['items'][0]->type );\n\t\t$this->assertSame( 'papi_post_one', $items['posts']['items'][0]->slug );\n\t\t$this->assertSame( 'Post one', $items['posts']['items'][0]->title );\n\t\t$this->assertSame( 'post', $items['posts']['items'][1]->type );\n\t\t$this->assertSame( 'papi_post_two', $items['posts']['items'][1]->slug );\n\t\t$this->assertSame( 'Post two', $items['posts']['items'][1]->title );\n\t}\n}\n"
  },
  {
    "path": "tests/cases/properties/class-papi-property-gallery-test.php",
    "content": "<?php\n\n/**\n * @group properties\n */\nclass Papi_Property_Gallery_Test extends Papi_Property_Test_Case {\n\n\tpublic $slug = 'gallery_test';\n\n\tpublic function get_value() {\n\t\treturn [23];\n\t}\n\n\tpublic function get_expected() {\n\t\treturn [23];\n\t}\n\n\tpublic function test_property_convert_type() {\n\t\t$this->assertSame( 'array', $this->property->convert_type );\n\t}\n\n\tpublic function test_property_default_value() {\n\t\t$this->assertSame( [], $this->property->default_value );\n\t}\n\n\tpublic function test_property_format_value() {\n\t\t$post_id = $this->factory->post->create( ['post_type' => 'attachment', 'post_mime_type' => 'image/jpeg'] );\n\t\t$meta    = [\n\t\t\t'width'      => 2900,\n\t\t\t'height'     => 1559,\n\t\t\t'file'       => '2011/12/press_image.jpg',\n\t\t\t'sizes'      => [\n\t\t\t\t'thumbnail' => [\n\t\t\t\t\t'file'      => 'press_image-150x150.jpg',\n\t\t\t\t\t'width'     => 150,\n\t\t\t\t\t'height'    => 150,\n\t\t\t\t\t'mime-type' => 'image/jpeg'\n\t\t\t\t]\n\t\t\t],\n\t\t\t'image_meta' => [\n\t\t\t\t'aperture'          => 5,\n\t\t\t\t'credit'            => '',\n\t\t\t\t'camera'            => 'Super',\n\t\t\t\t'caption'           => '',\n\t\t\t\t'created_timestamp'\t=> 1323190643,\n\t\t\t\t'copyright'         => '',\n\t\t\t\t'focal_length'      => 35,\n\t\t\t\t'iso'               => 800,\n\t\t\t\t'shutter_speed'     => 0.016666666666667,\n\t\t\t\t'title'             => ''\n\t\t\t]\n\t\t];\n\n\t\tupdate_post_meta( $post_id, '_wp_attachment_metadata', $meta );\n\t\tupdate_post_meta( $post_id, '_wp_attachment_image_alt', 'alt text' );\n\t\tupdate_post_meta( $post_id, '_wp_attached_file', '2011/12/press_image.jpg' );\n\t\t$thumbnail_url = home_url( '/wp-content/uploads/2011/12/press_image-150x150.jpg' );\n\n\t\tadd_filter( 'image_downsize', function( $image, $attachment_id, $size ) use ( $thumbnail_url ) {\n\t\t\treturn [$thumbnail_url, 150, 150, false];\n\t\t}, 10, 3 );\n\n\t\t$images = $this->property->format_value( [$post_id, $post_id], '', $post_id );\n\n\t\t$this->assertTrue( is_object( $images[0] ) );\n\t\t$this->assertSame( 2900, $images[0]->width );\n\t\t$this->assertSame( 1559, $images[0]->height );\n\t\t$this->assertSame( '2011/12/press_image.jpg', $images[0]->file );\n\n\t\t$this->assertTrue( is_array( $images[0]->sizes ) );\n\t\t$this->assertTrue( isset( $images[0]->sizes['thumbnail'] ) );\n\t\t$this->assertSame( 'press_image-150x150.jpg', $images[0]->sizes['thumbnail']['file'] );\n\t\t$this->assertSame( 150, $images[0]->sizes['thumbnail']['width'] );\n\t\t$this->assertSame( 150, $images[0]->sizes['thumbnail']['height'] );\n\t\t$this->assertSame( 'image/jpeg', $images[0]->sizes['thumbnail']['mime-type'] );\n\t\t$this->assertSame( $thumbnail_url, $images[0]->sizes['thumbnail']['url'] );\n\n\t\t$this->assertTrue( is_array( $images[0]->image_meta ) );\n\t\t$this->assertSame( 5, $images[0]->image_meta['aperture'] );\n\t\t$this->assertSame( '', $images[0]->image_meta['credit'] );\n\t\t$this->assertSame( 'Super', $images[0]->image_meta['camera'] );\n\t\t$this->assertSame( '', $images[0]->image_meta['caption'] );\n\t\t$this->assertSame( 1323190643, $images[0]->image_meta['created_timestamp'] );\n\t\t$this->assertSame( '', $images[0]->image_meta['copyright'] );\n\t\t$this->assertSame( 35, $images[0]->image_meta['focal_length'] );\n\t\t$this->assertSame( 800, $images[0]->image_meta['iso'] );\n\t\t$this->assertSame( 0.016666666666667, $images[0]->image_meta['shutter_speed'] );\n\t\t$this->assertSame( '', $images[0]->image_meta['title'] );\n\n\t\t$this->assertTrue( is_object( $images[1] ) );\n\t\t$this->assertSame( 2900, $images[1]->width );\n\t\t$this->assertSame( 1559, $images[1]->height );\n\t\t$this->assertSame( '2011/12/press_image.jpg', $images[1]->file );\n\n\t\t$this->assertTrue( is_array( $images[1]->sizes ) );\n\t\t$this->assertTrue( isset( $images[1]->sizes['thumbnail'] ) );\n\t\t$this->assertSame( 'press_image-150x150.jpg', $images[1]->sizes['thumbnail']['file'] );\n\t\t$this->assertSame( 150, $images[1]->sizes['thumbnail']['width'] );\n\t\t$this->assertSame( 150, $images[1]->sizes['thumbnail']['height'] );\n\t\t$this->assertSame( 'image/jpeg', $images[1]->sizes['thumbnail']['mime-type'] );\n\t\t$this->assertSame( $thumbnail_url, $images[1]->sizes['thumbnail']['url'] );\n\n\t\t$this->assertTrue( is_array( $images[1]->image_meta ) );\n\t\t$this->assertSame( 5, $images[1]->image_meta['aperture'] );\n\t\t$this->assertSame( '', $images[1]->image_meta['credit'] );\n\t\t$this->assertSame( 'Super', $images[1]->image_meta['camera'] );\n\t\t$this->assertSame( '', $images[1]->image_meta['caption'] );\n\t\t$this->assertSame( 1323190643, $images[1]->image_meta['created_timestamp'] );\n\t\t$this->assertSame( '', $images[1]->image_meta['copyright'] );\n\t\t$this->assertSame( 35, $images[1]->image_meta['focal_length'] );\n\t\t$this->assertSame( 800, $images[1]->image_meta['iso'] );\n\t\t$this->assertSame( 0.016666666666667, $images[1]->image_meta['shutter_speed'] );\n\t\t$this->assertSame( '', $images[1]->image_meta['title'] );\n\t}\n\n\tpublic function test_property_options() {\n\t\t$this->assertSame( 'gallery', $this->property->get_option( 'type' ) );\n\t\t$this->assertSame( 'Gallery test', $this->property->get_option( 'title' ) );\n\t\t$this->assertSame( 'papi_gallery_test', $this->property->get_option( 'slug' ) );\n\t}\n\n\tpublic function test_property_settings() {\n\t\t$this->assertTrue( $this->property->get_setting( 'multiple' ) );\n\t}\n}\n"
  },
  {
    "path": "tests/cases/properties/class-papi-property-group-test.php",
    "content": "<?php\n\n/**\n * @group properties\n */\nclass Papi_Property_Group_Test extends Papi_Property_Test_Case {\n\n\tpublic $slug = 'group_test';\n\n\tpublic function assert_values( $expected, $actual, $slug ) {\n\t\tforeach ( $expected as $key => $value ) {\n\t\t\tif ( $value instanceof WP_Post ) {\n\t\t\t\t$this->assertSame( $expected[$key]->ID, $actual[$key]->ID );\n\t\t\t} else {\n\t\t\t\t$this->assertSame( $expected[$key], $actual[$key] );\n\t\t\t}\n\t\t}\n\t}\n\n\tpublic function get_value() {\n\t\t$items = $this->property->get_setting( 'items' );\n\t\t$value_slug1         = unpapify( $items[0]->slug );\n\t\t$value_type_slug1    = papi_get_property_type_key( $value_slug1 );\n\t\t$value_slug2         = unpapify( $items[1]->slug );\n\t\t$value_type_slug2    = papi_get_property_type_key( $value_slug2 );\n\n\t\t$item = [];\n\t\t$item[$value_slug1] = $this->post_id;\n\t\t$item[$value_type_slug1] = $items[0];\n\t\t$item[$value_slug2] = 'Test page';\n\t\t$item[$value_type_slug2] = $items[1];\n\n\t\treturn [$item];\n\t}\n\n\tpublic function get_expected() {\n\t\treturn [\n\t\t\t'page'       => get_post( $this->post_id ),\n\t\t\t'page_title' => 'Test page'\n\t\t];\n\t}\n\n\tpublic function test_property_convert_type() {\n\t\t$this->assertSame( 'array', $this->property->convert_type );\n\t}\n\n\tpublic function test_property_default_value() {\n\t\t$this->assertSame( [], $this->property->default_value );\n\t}\n\n\tpublic function test_property_format_value() {\n\t\t$actual = $this->property->format_value( $this->get_value(), $this->slug, $this->post_id );\n\t\t$this->assertEquals( $this->get_expected(), $actual );\n\t\t$this->assertEmpty( $this->property->format_value( '', $this->slug, $this->post_id ) );\n\t\t$this->assertEmpty( $this->property->format_value( (object) [], $this->slug, $this->post_id ) );\n\t\t$this->assertEmpty( $this->property->format_value( 1, $this->slug, $this->post_id ) );\n\t\t$this->assertEmpty( $this->property->format_value( null, $this->slug, $this->post_id ) );\n\t\t$this->assertEmpty( $this->property->format_value( [], $this->slug, $this->post_id ) );\n\t\t$this->assertEmpty( $this->property->format_value( true, $this->slug, $this->post_id ) );\n\t\t$this->assertEmpty( $this->property->format_value( false, $this->slug, $this->post_id ) );\n\t}\n\n\tpublic function test_property_load_value() {\n\t\t$this->assertSame( [], $this->property->load_value( [], '', 0 ) );\n\t}\n\n\tpublic function test_property_options() {\n\t\t$this->assertSame( 'group', $this->property->get_option( 'type' ) );\n\t\t$this->assertSame( 'Group test', $this->property->get_option( 'title' ) );\n\t\t$this->assertSame( 'papi_group_test', $this->property->get_option( 'slug' ) );\n\t}\n\n\tpublic function test_property_settings() {\n\t\t$items = $this->property->get_setting( 'items' );\n\t\t$this->assertNotEmpty( $items );\n\n\t\t$this->assertSame( 'post', $items[0]->type );\n\t\t$this->assertSame( 'papi_page', $items[0]->slug );\n\t\t$this->assertSame( 'Page', $items[0]->title );\n\n\t\t$this->assertSame( 'string', $items[1]->type );\n\t\t$this->assertSame( 'papi_page_title', $items[1]->slug );\n\t\t$this->assertSame( 'Page title', $items[1]->title );\n\t}\n\n\tpublic function test_property_update_value() {\n\t\t$input    = null;\n\t\t$output   = $this->property->update_value( $input, 'test', $this->post_id );\n\t\t$expected = ['test' => 0];\n\t\t$this->assertSame( $expected, $output );\n\n\t\t$input    = [];\n\t\t$output   = $this->property->update_value( $input, 'test', $this->post_id );\n\t\t$expected = ['test' => 0];\n\t\t$this->assertSame( $expected, $output );\n\n\t\t$input    = $this->get_value();\n\t\t$output   = $this->property->update_value( $input, 'test', $this->post_id );\n\t\t$expected = [\n\t\t\t'test_0_page'       => $this->post_id,\n\t\t\t'test_0_page_title' => 'Test page',\n\t\t\t'test'              => 1\n\t\t];\n\t\t$this->assertSame( $expected, $output );\n\t}\n}\n"
  },
  {
    "path": "tests/cases/properties/class-papi-property-hidden-test.php",
    "content": "<?php\n\n/**\n * @group properties\n */\nclass Papi_Property_Hidden_Test extends Papi_Property_Test_Case {\n\n\tpublic $slug = 'hidden_test';\n\n\tpublic function get_value() {\n\t\treturn 'hidden value';\n\t}\n\n\tpublic function get_expected() {\n\t\treturn 'hidden value';\n\t}\n\n\tpublic function test_property_options() {\n\t\t$this->assertSame( 'hidden', $this->property->get_option( 'type' ) );\n\t\t$this->assertSame( 'Hidden test', $this->property->get_option( 'title' ) );\n\t\t$this->assertSame( 'papi_hidden_test', $this->property->get_option( 'slug' ) );\n\t}\n}\n"
  },
  {
    "path": "tests/cases/properties/class-papi-property-html-test.php",
    "content": "<?php\n\n/**\n * @group properties\n */\nclass Papi_Property_Html_Test extends Papi_Property_Test_Case {\n\n\tpublic $slugs = ['html_test', 'html_test_2', 'html_save_test'];\n\n\tpublic function get_value() {\n\t\t$args = func_get_args();\n\t\tswitch ( $args[0] ) {\n\t\t\tcase 'html_save_test':\n\t\t\t\treturn '<p>Hello, world!</p>';\n\t\t\tdefault:\n\t\t\t\treturn;\n\t\t}\n\t}\n\n\tpublic function get_update_value() {\n\t\t$args = func_get_args();\n\t\tswitch ( $args[0] ) {\n\t\t\tcase 'html_save_test':\n\t\t\t\treturn '<p>Hello, world 2!</p>';\n\t\t\tdefault:\n\t\t\t\treturn;\n\t\t}\n\t}\n\n\tpublic function get_expected() {\n\t\t$args = func_get_args();\n\t\tswitch ( $args[0] ) {\n\t\t\tcase 'html_save_test':\n\t\t\t\treturn '<p>Hello, world!</p>';\n\t\t\tdefault:\n\t\t\t\treturn;\n\t\t}\n\t}\n\n\tpublic function test_property_format_value() {\n\t\t$this->assertSame( $this->get_expected( 'html_test' ), $this->properties[0]->format_value( $this->get_value( 'html_test' ), '', 0 ) );\n\t\t$this->assertSame( $this->get_expected( 'html_test_2' ), $this->properties[1]->format_value( $this->get_value( 'html_test_2' ), '', 0 ) );\n\t}\n\n\tpublic function test_property_options() {\n\t\t$this->assertSame( 'html', $this->properties[0]->get_option( 'type' ) );\n\t\t$this->assertSame( 'Html test', $this->properties[0]->get_option( 'title' ) );\n\t\t$this->assertSame( 'papi_html_test', $this->properties[0]->get_option( 'slug' ) );\n\t\t$this->assertSame( '<p>Hello, world!</p>', $this->properties[0]->get_setting( 'html' ) );\n\n\t\t$this->assertSame( 'html', $this->properties[1]->get_option( 'type' ) );\n\t\t$this->assertSame( 'Html test 2', $this->properties[1]->get_option( 'title' ) );\n\t\t$this->assertSame( 'papi_html_test_2', $this->properties[1]->get_option( 'slug' ) );\n\n\t\t$this->assertSame( 'html', $this->properties[2]->get_option( 'type' ) );\n\t\t$this->assertSame( 'Html save test', $this->properties[2]->get_option( 'title' ) );\n\t\t$this->assertSame( 'papi_html_save_test', $this->properties[2]->get_option( 'slug' ) );\n\t}\n\n\tpublic function test_property_output() {\n\t\tparent::test_property_output();\n\n\t\tpapi_render_property( $this->properties[0] );\n\t\t$this->expectOutputRegex( '/\\<p\\>Hello, world!\\<\\/p\\>/' );\n\n\t\tpapi_render_property( $this->properties[1] );\n\t\t$this->expectOutputRegex( '/\\<p\\>Hello, callable!\\<\\/p\\>/' );\n\n\t\tpapi_render_property( $this->properties[2] );\n\t\t$this->expectOutputRegex( '/\\<p\\>Hello, world!\\<\\/p\\>/' );\n\t}\n}\n"
  },
  {
    "path": "tests/cases/properties/class-papi-property-image-test.php",
    "content": "<?php\n\n/**\n * @group properties\n */\nclass Papi_Property_Image_Test extends Papi_Property_Test_Case {\n\n\tpublic $slug = 'image_test';\n\n\tpublic function get_value() {\n\t\treturn 23;\n\t}\n\n\tpublic function get_expected() {\n\t\treturn 23;\n\t}\n\n\tpublic function test_property_convert_type() {\n\t\t$this->assertSame( 'object', $this->property->convert_type );\n\t}\n\n\tpublic function test_property_default_value() {\n\t\t$this->assertSame( [], $this->property->default_value );\n\t}\n\n\tpublic function test_property_format_value() {\n\t\t$post_id = $this->factory->post->create( ['post_type' => 'attachment', 'post_mime_type' => 'image/jpeg'] );\n\t\t$meta    = [\n\t\t\t'width'      => 2900,\n\t\t\t'height'     => 1559,\n\t\t\t'file'       => '2011/12/press_image.jpg',\n\t\t\t'sizes'      => [\n\t\t\t\t'thumbnail' => [\n\t\t\t\t\t'file'      => 'press_image-150x150.jpg',\n\t\t\t\t\t'width'     => 150,\n\t\t\t\t\t'height'    => 150,\n\t\t\t\t\t'mime-type' => 'image/jpeg'\n\t\t\t\t]\n\t\t\t],\n\t\t\t'image_meta' => [\n\t\t\t\t'aperture'          => 5,\n\t\t\t\t'credit'            => '',\n\t\t\t\t'camera'            => 'Super',\n\t\t\t\t'caption'           => '',\n\t\t\t\t'created_timestamp'\t=> 1323190643,\n\t\t\t\t'copyright'         => '',\n\t\t\t\t'focal_length'      => 35,\n\t\t\t\t'iso'               => 800,\n\t\t\t\t'shutter_speed'     => 0.016666666666667,\n\t\t\t\t'title'             => ''\n\t\t\t]\n\t\t];\n\n\t\tupdate_post_meta( $post_id, '_wp_attachment_metadata', $meta );\n\t\tupdate_post_meta( $post_id, '_wp_attachment_image_alt', 'alt text' );\n\t\tupdate_post_meta( $post_id, '_wp_attached_file', '2011/12/press_image.jpg' );\n\t\t$thumbnail_url = home_url( '/wp-content/uploads/2011/12/press_image-150x150.jpg' );\n\n\t\tadd_filter( 'image_downsize', function( $image, $attachment_id, $size ) use ( $thumbnail_url ) {\n\t\t\treturn [$thumbnail_url, 150, 150, false];\n\t\t}, 10, 3 );\n\n\t\t$image = $this->property->format_value( $post_id, '', $post_id );\n\n\t\t$this->assertTrue( is_object( $image ) );\n\t\t$this->assertSame( 2900, $image->width );\n\t\t$this->assertSame( 1559, $image->height );\n\t\t$this->assertSame( '2011/12/press_image.jpg', $image->file );\n\n\t\t$this->assertTrue( is_array( $image->sizes ) );\n\t\t$this->assertTrue( isset( $image->sizes['thumbnail'] ) );\n\t\t$this->assertSame( 'press_image-150x150.jpg', $image->sizes['thumbnail']['file'] );\n\t\t$this->assertSame( 150, $image->sizes['thumbnail']['width'] );\n\t\t$this->assertSame( 150, $image->sizes['thumbnail']['height'] );\n\t\t$this->assertSame( 'image/jpeg', $image->sizes['thumbnail']['mime-type'] );\n\t\t$this->assertSame( $thumbnail_url, $image->sizes['thumbnail']['url'] );\n\n\t\t$this->assertTrue( is_array( $image->image_meta ) );\n\t\t$this->assertSame( 5, $image->image_meta['aperture'] );\n\t\t$this->assertSame( '', $image->image_meta['credit'] );\n\t\t$this->assertSame( 'Super', $image->image_meta['camera'] );\n\t\t$this->assertSame( '', $image->image_meta['caption'] );\n\t\t$this->assertSame( 1323190643, $image->image_meta['created_timestamp'] );\n\t\t$this->assertSame( '', $image->image_meta['copyright'] );\n\t\t$this->assertSame( 35, $image->image_meta['focal_length'] );\n\t\t$this->assertSame( 800, $image->image_meta['iso'] );\n\t\t$this->assertSame( 0.016666666666667, $image->image_meta['shutter_speed'] );\n\t\t$this->assertSame( '', $image->image_meta['title'] );\n\t}\n\n\tpublic function test_property_options() {\n\t\t$this->assertSame( 'image', $this->property->get_option( 'type' ) );\n\t\t$this->assertSame( 'Image test', $this->property->get_option( 'title' ) );\n\t\t$this->assertSame( 'papi_image_test', $this->property->get_option( 'slug' ) );\n\t}\n\n\tpublic function test_property_settings() {\n\t\t$this->assertFalse( $this->property->get_setting( 'multiple' ) );\n\t}\n}\n"
  },
  {
    "path": "tests/cases/properties/class-papi-property-link-test.php",
    "content": "<?php\n\n/**\n * @group properties\n */\nclass Papi_Property_Link_Test extends Papi_Property_Test_Case {\n\n\tpublic $slug = 'link_test';\n\n\tpublic function assert_values( $expected, $actual, $slug ) {\n\t\t$this->assertSame( $expected->target, $actual->target );\n\t\t$this->assertSame( $expected->title, $actual->title );\n\t\t$this->assertSame( $expected->url, $actual->url );\n\t\t$this->assertSame( 0, $actual->post_id );\n\t}\n\n\tpublic function get_value() {\n\t\treturn [\n\t\t\t'url'    => 'http://example.org',\n\t\t\t'title'  => 'Example site',\n\t\t\t'target' => '_blank'\n\t\t];\n\t}\n\n\tpublic function get_expected() {\n\t\treturn (object) [\n\t\t\t'url'    => 'http://example.org',\n\t\t\t'title'  => 'Example site',\n\t\t\t'target' => '_blank'\n\t\t];\n\t}\n\n\tpublic function test_property_convert_type() {\n\t\t$this->assertSame( 'object', $this->property->convert_type );\n\t}\n\n\tpublic function test_property_default_value() {\n\t\t$this->assertSame( [], $this->property->default_value );\n\t}\n\n\tpublic function test_property_delete_value() {\n\t\t$this->save_properties_value( $this->property );\n\t\t$result = $this->property->delete_value( $this->property->slug, $this->post_id, 'post' );\n\t\t$this->assertTrue( $result );\n\t}\n\n\tpublic function test_property_format_value() {\n\t\t$post_id = $this->factory->post->create();\n\t\t$value   = [\n\t\t\t'_papi_link_target' => '_blank',\n\t\t\t'_papi_link_title'  => 'Example site',\n\t\t\t'_papi_link_url'    => 'http://example.org'\n\t\t];\n\n\t\t$output = $this->property->format_value( $value, '_papi_link', $post_id );\n\n\t\t$this->assertSame( '_blank', $output->target );\n\t\t$this->assertSame( 'Example site', $output->title );\n\t\t$this->assertSame( 'http://example.org', $output->url );\n\n\t\t$output = $this->property->format_value( (object) $value, '_papi_link', $post_id );\n\n\t\t$this->assertSame( '_blank', $output->target );\n\t\t$this->assertSame( 'Example site', $output->title );\n\t\t$this->assertSame( 'http://example.org', $output->url );\n\t}\n\n\tpublic function test_property_format_value_empty_target() {\n\t\t$post_id = $this->factory->post->create();\n\t\t$value   = [\n\t\t\t'_papi_link_title'  => 'Example site',\n\t\t\t'_papi_link_url'    => 'http://example.org'\n\t\t];\n\n\t\t$output = $this->property->format_value( $value, '_papi_link', $post_id );\n\n\t\t$this->assertSame( '_self', $output->target );\n\t\t$this->assertSame( 'Example site', $output->title );\n\t\t$this->assertSame( 'http://example.org', $output->url );\n\t}\n\n\tpublic function test_property_format_value_real_post() {\n\t\t$post_id   = $this->factory->post->create();\n\t\t$input     = [\n\t\t\t'url'    => rtrim( get_permalink( $post_id ), '/' ),\n\t\t\t'title'  => get_the_title( $post_id ),\n\t\t\t'target' => '_blank'\n\t\t];\n\t\t$expected  = (object) [\n\t\t\t'url'     => get_permalink( $post_id ),\n\t\t\t'title'   => get_the_title( $post_id ),\n\t\t\t'target'  => '_blank',\n\t\t\t'post_id' => $post_id\n\t\t];\n\t\t$this->assertEquals( $expected, $this->property->format_value( $input, $this->property->slug, 0 ) );\n\t}\n\n\tpublic function test_property_load_value() {\n\t\t$post_id = $this->factory->post->create();\n\t\t$value   = [\n\t\t\t'_papi_link_target' => '_blank',\n\t\t\t'_papi_link_title'  => 'Example site',\n\t\t\t'_papi_link_url'    => 'http://example.org'\n\t\t];\n\n\t\tforeach ( $value as $k => $v ) {\n\t\t\tupdate_post_meta( $post_id, unpapify( $k ) , $v );\n\t\t}\n\n\t\t$output = $this->property->load_value( null, '_papi_link', $post_id );\n\n\t\t$this->assertSame( '_blank', $output->target );\n\t\t$this->assertSame( 'Example site', $output->title );\n\t\t$this->assertSame( 'http://example.org', $output->url );\n\t}\n\n\tpublic function test_property_load_value_empty_target() {\n\t\t$post_id = $this->factory->post->create();\n\t\t$value   = [\n\t\t\t'_papi_link_title'  => 'Example site',\n\t\t\t'_papi_link_url'    => 'http://example.org'\n\t\t];\n\n\t\tforeach ( $value as $k => $v ) {\n\t\t\tupdate_post_meta( $post_id, unpapify( $k ) , $v );\n\t\t}\n\n\t\t$output = $this->property->load_value( $value, '_papi_link', $post_id );\n\n\t\t$this->assertSame( '_self', $output->target );\n\t\t$this->assertSame( 'Example site', $output->title );\n\t\t$this->assertSame( 'http://example.org', $output->url );\n\t}\n\n\tpublic function test_property_load_value_real_post() {\n\t\t$post_id   = $this->factory->post->create();\n\t\t$permalink = get_permalink( $post_id );\n\t\t$value     = [\n\t\t\t'papi_link_test_url'    => rtrim( get_permalink( $post_id ), '/' ),\n\t\t\t'papi_link_test_title'  => get_the_title( $post_id ),\n\t\t\t'papi_link_test_target' => '_blank'\n\t\t];\n\n\t\tforeach ( $value as $k => $v ) {\n\t\t\tupdate_post_meta( $post_id, unpapify( $k ) , $v );\n\t\t}\n\n\t\t$output = $this->property->load_value( null, $this->property->slug, $post_id );\n\t\t$expected = (object) [\n\t\t\t'url'     => get_permalink( $post_id ),\n\t\t\t'title'   => get_the_title( $post_id ),\n\t\t\t'target'  => '_blank',\n\t\t\t'post_id' => $post_id\n\t\t];\n\n\t\t$this->assertEquals( $expected, $output );\n\t}\n\n\tpublic function test_property_options() {\n\t\t$this->assertSame( 'link', $this->property->get_option( 'type' ) );\n\t\t$this->assertSame( 'Link test', $this->property->get_option( 'title' ) );\n\t\t$this->assertSame( 'papi_link_test', $this->property->get_option( 'slug' ) );\n\t}\n\n\tpublic function test_render_link_template() {\n\t\t$this->property->render_link_template();\n\t\t$this->expectOutputRegex( '/.*\\S.*/' );\n\t}\n\n\tpublic function test_property_update_value_empty_value() {\n\t\t$post_id = $this->factory->post->create();\n\t\t$values = $this->property->update_value( [], $this->property->slug, $post_id );\n\t\t$this->assertEmpty( $values['papi_link_test_post_id'] );\n\t\t$this->assertEmpty( $values['papi_link_test_url'] );\n\t\t$this->assertEmpty( $values['papi_link_test_title'] );\n\t\t$this->assertEmpty( $values['papi_link_test_target'] );\n\t}\n\n\tpublic function test_property_update_value_real_post() {\n\t\t$post_id = $this->factory->post->create();\n\t\t$values = $this->property->update_value( [], $this->property->slug, $post_id );\n\n\t\t$post_id   = $this->factory->post->create();\n\t\t$permalink = get_permalink( $post_id );\n\t\t$input     = [\n\t\t\t'url'    => get_permalink( $post_id ),\n\t\t\t'title'  => get_the_title( $post_id ),\n\t\t\t'target' => '_blank'\n\t\t];\n\t\t$expected  = [\n\t\t\t'papi_link_test_url'     => get_permalink( $post_id ),\n\t\t\t'papi_link_test_title'   => get_the_title( $post_id ),\n\t\t\t'papi_link_test_target'  => '_blank',\n\t\t\t'papi_link_test_post_id' => $post_id,\n\t\t\t'papi_link_test'         => 1,\n\t\t];\n\t\t$this->assertEquals( $expected, $this->property->update_value( $input, $this->property->slug, 0 ) );\n\t}\n}\n"
  },
  {
    "path": "tests/cases/properties/class-papi-property-module-test.php",
    "content": "<?php\n\n/**\n * @group properties\n */\nclass Papi_Property_Module_Test extends Papi_Property_Test_Case {\n\n\tpublic $slug = 'module_test';\n\n\tpublic function assert_values( $expected, $actual, $slug ) {\n\t\t$this->assertSame( $expected->module->ID, $actual->module->ID );\n\t\t$this->assertSame( $expected->template, $actual->template );\n\t}\n\n\tpublic function get_value() {\n\t\treturn (object) [\n\t\t\t'module'   => $this->post_id,\n\t\t\t'template' => 'video-a.php'\n\t\t];\n\t}\n\n\tpublic function get_expected() {\n\t\treturn (object) [\n\t\t\t'module'   => get_post( $this->post_id ),\n\t\t\t'template' => 'video-a.php'\n\t\t];\n\t}\n\n\tpublic function test_property_convert_type() {\n\t\t$this->assertSame( 'object', $this->property->convert_type );\n\t}\n\n\tpublic function test_property_default_value() {\n\t\t$this->assertSame( [\n\t\t\t'module'   => null,\n\t\t\t'template' => null\n\t\t], $this->property->default_value );\n\t}\n\n\tpublic function test_property_format_value() {\n\t\t$this->assertEquals( (object)[\n\t\t\t'module'   => get_post( $this->post_id ),\n\t\t\t'template' => 'pages/properties-page.php'\n\t\t], $this->property->format_value( $this->post_id, '', 0 ) );\n\n\t\t$this->assertEquals( (object)[\n\t\t\t'module'   => null,\n\t\t\t'template' => ''\n\t\t], $this->property->format_value( 0, '', 0 ) );\n\t}\n\n\tpublic function test_property_options() {\n\t\t$this->assertSame( 'module', $this->property->get_option( 'type' ) );\n\t\t$this->assertSame( 'Module test', $this->property->get_option( 'title' ) );\n\t\t$this->assertSame( 'papi_module_test', $this->property->get_option( 'slug' ) );\n\t}\n\n\tpublic function test_save_properties_value() {\n\t\t$_POST[$this->property->slug] = $this->get_value()->module;\n\t\t$_POST[$this->property->slug.'_template'] = $this->get_value()->template;\n\t\tparent::test_save_properties_value();\n\t}\n}\n"
  },
  {
    "path": "tests/cases/properties/class-papi-property-number-test.php",
    "content": "<?php\n\n/**\n * @group properties\n */\nclass Papi_Property_Number_Test extends Papi_Property_Test_Case {\n\n\tpublic $slug = 'number_test';\n\n\tpublic function get_value() {\n\t\treturn 42;\n\t}\n\n\tpublic function get_expected() {\n\t\treturn 42;\n\t}\n\n\tpublic function test_property_get_default_settings() {\n\t\t$settings = (object) $this->property->get_default_settings();\n\t\t$this->assertSame( '', $settings->max );\n\t\t$this->assertSame( '', $settings->min );\n\t\t$this->assertSame( 'any', $settings->step );\n\t\t$this->assertSame( 'number', $settings->type );\n\t}\n\n\tpublic function test_property_convert_type() {\n\t\t$this->assertSame( 'int', $this->property->convert_type );\n\t}\n\n\tpublic function test_property_format_value() {\n\t\t$this->assertSame( 42, $this->property->format_value( '42', '', 0 ) );\n\t\t$this->assertSame( 0, $this->property->format_value( 'hello', '', 0 ) );\n\t\t$this->assertSame( 0, $this->property->format_value( null, '', 0 ) );\n\t}\n\n\tpublic function test_property_options() {\n\t\t$this->assertSame( 'number', $this->property->get_option( 'type' ) );\n\t\t$this->assertSame( 'Number test', $this->property->get_option( 'title' ) );\n\t\t$this->assertSame( 'papi_number_test', $this->property->get_option( 'slug' ) );\n\t}\n}\n"
  },
  {
    "path": "tests/cases/properties/class-papi-property-post-test.php",
    "content": "<?php\n\n/**\n * @group properties\n */\nclass Papi_Property_Post_Test extends Papi_Property_Test_Case {\n\n\tpublic $slugs = ['post_test', 'post_test_2'];\n\n\tpublic function get_value() {\n\t\t$args = func_get_args();\n\t\t$args[0] = isset( $args[0] ) ? $args[0] : $this->slugs[0];\n\t\tswitch ( $args[0] ) {\n\t\t\tdefault:\n\t\t\t\treturn $this->post_id;\n\t\t}\n\t}\n\n\tpublic function get_expected() {\n\t\t$args = func_get_args();\n\t\t$args[0] = isset( $args[0] ) ? $args[0] : $this->slugs[0];\n\t\tswitch ( $args[0] ) {\n\t\t\tdefault:\n\t\t\t\treturn get_post( $this->post_id );\n\t\t}\n\t}\n\n\tpublic function assert_values( $expected, $actual, $slug ) {\n\t\tswitch ( $slug ) {\n\t\t\tcase 'post_test_2';\n\t\t\t\tbreak; // fail ok since we don't save a custom id.\n\t\t\tdefault:\n\t\t\t\t$this->assertSame( $expected->ID, $actual->ID );\n\t\t\t\tbreak;\n\t\t}\n\t}\n\n\tpublic function test_property_convert_type() {\n\t\t$this->assertSame( 'object', $this->properties[0]->convert_type );\n\t}\n\n\tpublic function test_property_format_value() {\n\t\t$this->assertEquals( get_post( $this->post_id ), $this->properties[0]->format_value( $this->post_id, '', 0 ) );\n\t\t$this->assertEquals( get_post( $this->post_id ), $this->properties[0]->format_value( strval( $this->post_id ), '', 0 ) );\n\t\t$this->assertNull( $this->properties[0]->format_value( 'hello', '', 0 ) );\n\t\t$this->assertNull( $this->properties[0]->format_value( null, '', 0 ) );\n\t\t$this->assertNull( $this->properties[0]->format_value( true, '', 0 ) );\n\t\t$this->assertNull( $this->properties[0]->format_value( false, '', 0 ) );\n\t\t$this->assertNull( $this->properties[0]->format_value( [], '', 0 ) );\n\t\t$this->assertNull( $this->properties[0]->format_value( (object) [], '', 0 ) );\n\t}\n\n\tpublic function test_property_format_value_meta_key() {\n\t\t$this->assertSame( 0, intval( $this->properties[1]->format_value( 1, '', 0 ) ) );\n\t\tupdate_post_meta( $this->post_id, 'custom_id', 1 );\n\t\t$this->assertSame( 1, intval( $this->properties[1]->format_value( 1, '', 0 ) ) );\n\t}\n\n\tpublic function test_property_options() {\n\t\t$this->assertSame( 'post', $this->properties[0]->get_option( 'type' ) );\n\t\t$this->assertSame( 'Post test', $this->properties[0]->get_option( 'title' ) );\n\t\t$this->assertSame( 'papi_post_test', $this->properties[0]->get_option( 'slug' ) );\n\t}\n\n\tpublic function test_property_settings() {\n\t\t$settings = $this->properties[0]->get_settings();\n\t\t$this->assertEmpty( $settings->placeholder );\n\t\t$this->assertTrue( $settings->select2 );\n\t}\n}\n"
  },
  {
    "path": "tests/cases/properties/class-papi-property-radio-test.php",
    "content": "<?php\n\n/**\n * @group properties\n */\nclass Papi_Property_Radio_Test extends Papi_Property_Test_Case {\n\n\tpublic $slug = 'radio_test';\n\n\tpublic function get_value() {\n\t\treturn '#ffffff';\n\t}\n\n\tpublic function get_expected() {\n\t\treturn '#ffffff';\n\t}\n\n\tpublic function test_property_options() {\n\t\t$this->assertSame( 'radio', $this->property->get_option( 'type' ) );\n\t\t$this->assertSame( 'Radio test', $this->property->get_option( 'title' ) );\n\t\t$this->assertSame( 'papi_radio_test', $this->property->get_option( 'slug' ) );\n\t}\n\n\tpublic function test_property_format_value() {\n\t\t$this->assertSame( 2014, $this->property->format_value( '2014', '', 0 ) );\n\t\t$this->assertSame( 12.3, $this->property->format_value( '12.3', '', 0 ) );\n\t\t$this->assertSame( true, $this->property->format_value( 'true', '', 0 ) );\n\t\t$this->assertSame( false, $this->property->format_value( 'false', '', 0 ) );\n\t}\n}\n"
  },
  {
    "path": "tests/cases/properties/class-papi-property-reference-test.php",
    "content": "<?php\n\n/**\n * @group properties\n */\nclass Papi_Property_Reference_Test extends Papi_Property_Test_Case {\n\n\tpublic $slug = 'reference_test';\n\n\tpublic function get_value() {\n\t\treturn;\n\t}\n\n\tpublic function get_expected() {\n\t\treturn;\n\t}\n\n\tpublic function test_property_options() {\n\t\t$this->assertSame( 'reference', $this->property->get_option( 'type' ) );\n\t\t$this->assertSame( 'Reference test', $this->property->get_option( 'title' ) );\n\t\t$this->assertSame( 'papi_reference_test', $this->property->get_option( 'slug' ) );\n\t}\n\n\tpublic function test_property_settings() {\n\t\t$this->assertTrue( is_array( $this->property->get_setting( 'slug' ) ) );\n\t\t$this->assertTrue( is_array( $this->property->get_setting( 'page_type' ) ) );\n\t}\n}\n"
  },
  {
    "path": "tests/cases/properties/class-papi-property-relationship-test.php",
    "content": "<?php\n\n/**\n * @group properties\n */\nclass Papi_Property_Relationship_Test extends Papi_Property_Test_Case {\n\n\tpublic $slugs = ['relationship_test', 'relationship_test_2'];\n\n\tpublic function assert_values( $expected, $actual, $slug ) {\n\t\tif ( isset( $expected[0]->ID ) ) {\n\t\t\t$this->assertSame( $expected[0]->ID, $actual[0]->ID );\n\t\t} else {\n\t\t\t$this->assertSame( $expected[0]->id, $actual[0]->id );\n\t\t}\n\t}\n\n\tpublic function get_value() {\n\t\t$args = func_get_args();\n\t\tswitch ( $args[0] ) {\n\t\t\tcase 'relationship_test_2':\n\t\t\t\treturn [1];\n\t\t\tdefault:\n\t\t\t\treturn [$this->post_id];\n\t\t}\n\t}\n\n\tpublic function get_expected() {\n\t\t$args = func_get_args();\n\t\tswitch ( $args[0] ) {\n\t\t\tcase 'relationship_test_2':\n\t\t\t\treturn [\n\t\t\t\t\t(object) [\n\t\t\t\t\t\t'id'    => 1,\n\t\t\t\t\t\t'title' => 'One'\n\t\t\t\t\t]\n\t\t\t\t];\n\t\t\tdefault:\n\t\t\t\treturn [get_post( $this->post_id )];\n\t\t}\n\t}\n\n\tpublic function test_property_convert_type() {\n\t\t$this->assertSame( 'array', $this->properties[0]->convert_type );\n\t\t$this->assertSame( 'array', $this->properties[1]->convert_type );\n\t}\n\n\tpublic function test_property_default_value() {\n\t\t$this->assertSame( [], $this->properties[0]->default_value );\n\t\t$this->assertSame( [], $this->properties[1]->default_value );\n\t}\n\n\tpublic function test_property_format_value() {\n\t\t$this->assertEmpty( $this->properties[0]->format_value( [9230], '', 0 ) );\n\t\t$this->assertEmpty( $this->properties[0]->format_value( [], '', 0 ) );\n\t\t$this->assertEmpty( $this->properties[0]->format_value( ['id' => null], '', 0 ) );\n\t\t$this->assertEmpty( $this->properties[0]->format_value( null, '', 0 ) );\n\t\t$this->assertEmpty( $this->properties[0]->format_value( true, '', 0 ) );\n\t\t$this->assertEmpty( $this->properties[0]->format_value( false, '', 0 ) );\n\t\t$this->assertEmpty( $this->properties[0]->format_value( 'hello', '', 0 ) );\n\n\t\t$out = $this->properties[0]->format_value( [$this->post_id], '', 0 );\n\t\t$this->assertEquals( [get_post( $this->post_id )], $out );\n\t}\n\n\tpublic function test_property_format_value_custom_items() {\n\t\t$categories = array_map( function ( $cat ) {\n\t\t  return [\n\t\t    'id'    => (int) $cat->term_id,\n\t\t    'title' => $cat->name\n\t\t  ];\n\t\t}, get_categories() );\n\n\t\t$property = papi_property( [\n\t\t\t'type'     => 'relationship',\n\t\t\t'title'    => 'Relationship',\n\t\t\t'settings' => [\n\t\t\t\t'items' => $categories\n\t\t\t]\n\t\t] );\n\n\t\t$out = $property->format_value( [\n\t\t\t[\n\t\t\t\t'id'    => 1,\n\t\t\t\t'title' => 'Uncategorized'\n\t\t\t]\n\t\t], '', 0 );\n\n\t\t$this->assertEquals( [\n\t\t\t(object) [\n\t\t\t\t'id'    => 1,\n\t\t\t\t'title' => 'Uncategorized'\n\t\t\t]\n\t\t], $out );\n\n\t\t$out = $property->format_value( [\n\t\t\t(object) [\n\t\t\t\t'id'    => 1,\n\t\t\t\t'title' => 'Uncategorized'\n\t\t\t]\n\t\t], '', 0 );\n\n\t\t$this->assertEquals( [\n\t\t\t(object) [\n\t\t\t\t'id'    => 1,\n\t\t\t\t'title' => 'Uncategorized'\n\t\t\t]\n\t\t], $out );\n\n\t\t$out = $property->format_value( [\n\t\t\t(object) [\n\t\t\t\t'id'    => null,\n\t\t\t\t'title' => 'Uncategorized'\n\t\t\t]\n\t\t], '', 0 );\n\n\t\t$this->assertEmpty( $out );\n\t}\n\n\tpublic function test_get_sort_options_name_alphabetically() {\n\t\t$sort_options = Papi_Property_Relationship::get_sort_options();\n\n\t\t$post_id  = $this->factory->post->create( ['post_title' => 'Alfa'] );\n\t\t$post_id2 = $this->factory->post->create( ['post_title' => 'Beta'] );\n\n\t\t$arr = [get_post( $post_id2 ), get_post( $post_id )];\n\t\t$out = [get_post( $post_id ), get_post( $post_id2 )];\n\t\tusort( $arr, $sort_options['Name (alphabetically)'] );\n\t\t$this->assertEquals( $out, $arr );\n\t}\n\n\tpublic function test_get_sort_options_post_created_date_ascending() {\n\t\t$sort_options = Papi_Property_Relationship::get_sort_options();\n\n\t\t$post_id  = $this->factory->post->create( ['post_title' => 'Alfa', 'post_date' => '2015-01-01 12:00'] );\n\t\t$post_id2 = $this->factory->post->create( ['post_title' => 'Beta', 'post_date' => '2015-01-01 15:00'] );\n\n\t\t$arr = [get_post( $post_id2 ), get_post( $post_id )];\n\t\t$out = [get_post( $post_id ), get_post( $post_id2 )];\n\t\tusort( $arr, $sort_options['Post created date (ascending)'] );\n\t\t$this->assertEquals( $out, $arr );\n\t}\n\n\tpublic function test_get_sort_options_post_created_date_descending() {\n\t\t$sort_options = Papi_Property_Relationship::get_sort_options();\n\n\t\t$post_id  = $this->factory->post->create( ['post_title' => 'Alfa', 'post_date' => '2015-01-01 12:00'] );\n\t\t$post_id2 = $this->factory->post->create( ['post_title' => 'Beta', 'post_date' => '2015-01-01 15:00'] );\n\n\t\t$arr = [get_post( $post_id ), get_post( $post_id2 )];\n\t\t$out = [get_post( $post_id2 ), get_post( $post_id )];\n\t\tusort( $arr, $sort_options['Post created date (descending)'] );\n\t\t$this->assertEquals( $out, $arr );\n\t}\n\n\tpublic function test_get_sort_options_post_id_ascending() {\n\t\t$sort_options = Papi_Property_Relationship::get_sort_options();\n\n\t\t$post_id  = $this->factory->post->create( ['post_title' => 'Alfa'] );\n\t\t$post_id2 = $this->factory->post->create( ['post_title' => 'Beta'] );\n\n\t\t$arr = [get_post( $post_id2 ), get_post( $post_id )];\n\t\t$out = [get_post( $post_id ), get_post( $post_id2 )];\n\t\tusort( $arr, $sort_options['Post id (ascending)'] );\n\t\t$this->assertEquals( $out, $arr );\n\t}\n\n\tpublic function test_get_sort_options_post_id_descending() {\n\t\t$sort_options = Papi_Property_Relationship::get_sort_options();\n\n\t\t$post_id  = $this->factory->post->create( ['post_title' => 'Alfa'] );\n\t\t$post_id2 = $this->factory->post->create( ['post_title' => 'Beta'] );\n\n\t\t$arr = [get_post( $post_id ), get_post( $post_id2 )];\n\t\t$out = [get_post( $post_id2 ), get_post( $post_id )];\n\t\tusort( $arr, $sort_options['Post id (descending)'] );\n\t\t$this->assertEquals( $out, $arr );\n\t}\n\n\tpublic function test_get_sort_options_post_order_value_ascending() {\n\t\t$sort_options = Papi_Property_Relationship::get_sort_options();\n\n\t\t$post_id  = $this->factory->post->create( ['post_title' => 'Alfa', 'menu_order' => 3] );\n\t\t$post_id2 = $this->factory->post->create( ['post_title' => 'Beta', 'menu_order' => 5] );\n\n\t\t$arr = [get_post( $post_id2 ), get_post( $post_id )];\n\t\t$out = [get_post( $post_id ), get_post( $post_id2 )];\n\t\tusort( $arr, $sort_options['Post order value (ascending)'] );\n\t\t$this->assertEquals( $out, $arr );\n\t}\n\n\tpublic function test_get_sort_options_post_order_value_descending() {\n\t\t$sort_options = Papi_Property_Relationship::get_sort_options();\n\n\t\t$post_id  = $this->factory->post->create( ['post_title' => 'Alfa', 'menu_order' => 3] );\n\t\t$post_id2 = $this->factory->post->create( ['post_title' => 'Beta', 'menu_order' => 5] );\n\n\t\t$arr = [get_post( $post_id ), get_post( $post_id2 )];\n\t\t$out = [get_post( $post_id2 ), get_post( $post_id )];\n\t\tusort( $arr, $sort_options['Post order value (descending)'] );\n\t\t$this->assertEquals( $out, $arr );\n\t}\n\n\tpublic function test_get_sort_options_post_modified_date_ascending() {\n\t\t$sort_options = Papi_Property_Relationship::get_sort_options();\n\n\t\t$post_id  = $this->factory->post->create( ['post_title' => 'Alfa', 'post_date' => '2015-01-01 12:00'] );\n\t\t$post_id2 = $this->factory->post->create( ['post_title' => 'Beta', 'post_date' => '2015-01-01 15:00'] );\n\n\t\t$arr = [get_post( $post_id2 ), get_post( $post_id )];\n\t\t$out = [get_post( $post_id ), get_post( $post_id2 )];\n\t\tusort( $arr, $sort_options['Post modified date (ascending)'] );\n\t\t$this->assertEquals( $out, $arr );\n\t}\n\n\tpublic function test_get_sort_options_post_modified_date_descending() {\n\t\t$sort_options = Papi_Property_Relationship::get_sort_options();\n\n\t\t$post_id  = $this->factory->post->create( ['post_title' => 'Alfa', 'post_date' => '2015-01-01 12:00'] );\n\t\t$post_id2 = $this->factory->post->create( ['post_title' => 'Beta', 'post_date' => '2015-01-01 15:00'] );\n\n\t\t$arr = [get_post( $post_id ), get_post( $post_id2 )];\n\t\t$out = [get_post( $post_id2 ), get_post( $post_id )];\n\t\tusort( $arr, $sort_options['Post modified date (descending)'] );\n\t\t$this->assertEquals( $out, $arr );\n\t}\n\n\tpublic function test_property_load_value() {\n\t\t$this->assertSame( ['yes' => true], $this->properties[0]->load_value( '{\"yes\":true}', '', 0 ) );\n\t\t$this->assertSame( [], $this->properties[0]->load_value( '{}', '', 0 ) );\n\t\t$this->assertSame( [1, 2, 3], $this->properties[0]->load_value( '[1, 2, 3]', '', 0 ) );\n\t\t$this->assertSame( [], $this->properties[0]->load_value( '[]', '', 0 ) );\n\t}\n\n\tpublic function test_property_options() {\n\t\t$this->assertSame( 'relationship', $this->properties[0]->get_option( 'type' ) );\n\t\t$this->assertSame( 'Relationship test', $this->properties[0]->get_option( 'title' ) );\n\t\t$this->assertSame( 'papi_relationship_test', $this->properties[0]->get_option( 'slug' ) );\n\n\t\t$this->assertSame( 'relationship', $this->properties[1]->get_option( 'type' ) );\n\t\t$this->assertSame( 'Relationship test 2', $this->properties[1]->get_option( 'title' ) );\n\t\t$this->assertSame( 'papi_relationship_test_2', $this->properties[1]->get_option( 'slug' ) );\n\t}\n\n\tpublic function test_property_sort_value() {\n\t\t$post_id  = $this->factory->post->create( ['post_title' => 'Alfa'] );\n\t\t$post_id2 = $this->factory->post->create( ['post_title' => 'Beta'] );\n\n\t\t$slug = $this->properties[0]->html_id( 'sort_option' );\n\t\tupdate_post_meta( $post_id, unpapify( $slug ) , 'Name (alphabetically)' );\n\n\t\t$arr = [get_post( $post_id2 ), get_post( $post_id )];\n\t\t$out = [get_post( $post_id ), get_post( $post_id2 )];\n\t\t$this->assertEquals( $out, $this->properties[0]->sort_value( $arr, '', $post_id ) );\n\t}\n\n\tpublic function test_property_sort_value_2() {\n\t\t$post_id = $this->factory->post->create( ['post_title' => 'Alfa'] );\n\t\t$slug    = $this->properties[1]->html_id( 'sort_option' );\n\t\tupdate_post_meta( $post_id, unpapify( $slug ), 'Name (alphabetically)' );\n\n\t\t$arr = $this->properties[1]->get_setting( 'items' );\n\t\t$arr = array_map( function( $a ) {\n\t\t\treturn (object) $a;\n\t\t}, $arr );\n\t\t$out = array_reverse( $arr );\n\t\t$this->assertEquals( $out, $this->properties[1]->sort_value( $arr, '', $post_id ) );\n\t}\n}\n"
  },
  {
    "path": "tests/cases/properties/class-papi-property-repeater-test.php",
    "content": "<?php\n\n/**\n * @group properties\n */\nclass Papi_Property_Repeater_Test extends Papi_Property_Test_Case {\n\n\tpublic $slugs = [\n\t\t'repeater_test',\n\t\t'repeater_with_child_test'\n\t];\n\n\tpublic function get_value() {\n\t\t$args = func_get_args();\n\t\t$args[0] = isset( $args[0] ) ? $args[0] : $this->slugs[0];\n\t\tswitch ( $args[0] ) {\n\t\t\tcase 'repeater_with_child_test':\n\t\t\t\treturn [\n\t\t\t\t\t[\n\t\t\t\t\t\t'repeater_child_test'          => $this->get_expected( 'repeater_test' ),\n\t\t\t\t\t\t'repeater_child_test_property' => $this->properties[1]\n\t\t\t\t\t]\n\t\t\t\t];\n\t\t\tdefault:\n\t\t\t\t$items = $this->property->get_setting( 'items' );\n\t\t\t\t$value_slug1         = unpapify( $items[0]->slug );\n\t\t\t\t$value_type_slug1    = papi_get_property_type_key( $value_slug1 );\n\t\t\t\t$value_slug2         = unpapify( $items[1]->slug );\n\t\t\t\t$value_type_slug2    = papi_get_property_type_key( $value_slug2 );\n\n\t\t\t\t$item = [];\n\t\t\t\t$item[$value_slug1] = 'Harry Potter';\n\t\t\t\t$item[$value_type_slug1] = $items[0];\n\t\t\t\t$item[$value_slug2] = '';\n\t\t\t\t$item[$value_type_slug2] = $items[1];\n\n\t\t\t\treturn [$item];\n\t\t}\n\t}\n\n\tpublic function get_expected() {\n\t\t$args = func_get_args();\n\t\t$args[0] = isset( $args[0] ) ? $args[0] : $this->slugs[0];\n\t\tswitch ( $args[0] ) {\n\t\t\tcase 'repeater_with_child_test':\n\t\t\t\treturn [\n\t\t\t\t\t[\n\t\t\t\t\t\t'repeater_child_test' => $this->get_expected( 'repeater_test' )\n\t\t\t\t\t]\n\t\t\t\t];\n\t\t\tdefault:\n\t\t\t\treturn [\n\t\t\t\t\t[\n\t\t\t\t\t\t'book_name' => 'Harry Potter',\n\t\t\t\t\t\t'is_open'   => false\n\t\t\t\t\t]\n\t\t\t\t];\n\t\t}\n\t}\n\n\tpublic function test_property_convert_type() {\n\t\t$this->assertSame( 'array', $this->property->convert_type );\n\t}\n\n\tpublic function test_property_default_value() {\n\t\t$this->assertSame( [], $this->property->default_value );\n\t}\n\n\tpublic function test_property_format_value() {\n\t\t$actual = $this->property->format_value( $this->get_value(), $this->slug, $this->post_id );\n\t\t$this->assertSame( $this->get_expected(), $actual );\n\t\t$this->assertEmpty( $this->property->format_value( '', $this->slug, $this->post_id ) );\n\t\t$this->assertEmpty( $this->property->format_value( (object) [], $this->slug, $this->post_id ) );\n\t\t$this->assertEmpty( $this->property->format_value( 1, $this->slug, $this->post_id ) );\n\t\t$this->assertEmpty( $this->property->format_value( null, $this->slug, $this->post_id ) );\n\t\t$this->assertEmpty( $this->property->format_value( [], $this->slug, $this->post_id ) );\n\t\t$this->assertEmpty( $this->property->format_value( true, $this->slug, $this->post_id ) );\n\t\t$this->assertEmpty( $this->property->format_value( false, $this->slug, $this->post_id ) );\n\t}\n\n\tpublic function test_property_load_value() {\n\t\t$this->assertSame( [], $this->property->load_value( [], '', 0 ) );\n\t}\n\n\tpublic function test_property_options() {\n\t\t$this->assertSame( 'repeater', $this->property->get_option( 'type' ) );\n\t\t$this->assertSame( 'Repeater test', $this->property->get_option( 'title' ) );\n\t\t$this->assertSame( 'papi_repeater_test', $this->property->get_option( 'slug' ) );\n\t}\n\n\tpublic function test_property_setting_add_new_label() {\n\t\t$property = Papi_Property::factory( [\n\t\t\t'settings' => [\n\t\t\t\t'add_new_label' => 'Add new slide'\n\t\t\t],\n\t\t\t'title'    => 'Repeater',\n\t\t\t'type'     => 'repeater'\n\t\t] );\n\n\t\t$this->assertSame( 'Add new slide', $property->get_setting( 'add_new_label' ) );\n\t}\n\n\tpublic function test_property_render_ajax_request() {\n\t\t$this->property->render_ajax_request();\n\t\t$this->expectOutputRegex( '/.*\\S.*/' );\n\t}\n\n\tpublic function test_property_render_repeater_rows_template() {\n\t\t$this->property->render_repeater_rows_template();\n\t\t$this->expectOutputRegex( '/.*\\S.*/' );\n\t}\n\n\tpublic function test_property_settings() {\n\t\t$items = $this->property->get_setting( 'items' );\n\t\t$this->assertNotEmpty( $items );\n\n\t\t$this->assertSame( 'string', $items[0]->type );\n\t\t$this->assertSame( 'papi_book_name', $items[0]->slug );\n\t\t$this->assertSame( 'Book name', $items[0]->title );\n\n\t\t$this->assertSame( 'bool', $items[1]->type );\n\t\t$this->assertSame( 'papi_is_open', $items[1]->slug );\n\t\t$this->assertSame( 'Is open?', $items[1]->title );\n\t}\n\n\tpublic function test_property_update_value() {\n\t\t$input    = null;\n\t\t$output   = $this->property->update_value( $input, 'test', $this->post_id );\n\t\t$expected = ['test' => 0];\n\t\t$this->assertSame( $expected, $output );\n\n\t\t$input    = [];\n\t\t$output   = $this->property->update_value( $input, 'test', $this->post_id );\n\t\t$expected = ['test' => 0];\n\t\t$this->assertSame( $expected, $output );\n\n\t\t$input    = $this->get_value();\n\t\t$output   = $this->property->update_value( $input, 'test', $this->post_id );\n\t\t$expected = [\n\t\t\t'test_0_book_name' => 'Harry Potter',\n\t\t\t'test_0_is_open'   => null,\n\t\t\t'test'             => 1\n\t\t];\n\t\t$this->assertSame( $expected, $output );\n\t}\n}\n"
  },
  {
    "path": "tests/cases/properties/class-papi-property-sidebar-test.php",
    "content": "<?php\n\n/**\n * @group properties\n */\nclass Papi_Property_Sidebar_Test extends Papi_Property_Test_Case {\n\n\tpublic $slug = 'sidebar_test';\n\n\tpublic function assert_values( $expected, $actual, $slug ) {\n\t\t$this->assertSame( $expected, $actual );\n\t}\n\n\tpublic function get_value() {\n\t\treturn 'home_right_1';\n\t}\n\n\tpublic function get_expected() {\n\t\treturn 'home_right_1';\n\t}\n\n\tpublic function test_property_convert_type() {\n\t\t$this->assertSame( 'string', $this->property->convert_type );\n\t}\n\n\tpublic function test_property_options() {\n\t\t$this->assertSame( 'sidebar', $this->property->get_option( 'type' ) );\n\t\t$this->assertSame( 'Sidebar test', $this->property->get_option( 'title' ) );\n\t\t$this->assertSame( 'papi_sidebar_test', $this->property->get_option( 'slug' ) );\n\t}\n\n\tpublic function test_property_settings() {\n\t\t$settings = $this->property->get_settings();\n\t\t$this->assertSame( 'Select sidebar', $settings->placeholder );\n\t\t$this->assertTrue( $settings->select2 );\n\t}\n}\n"
  },
  {
    "path": "tests/cases/properties/class-papi-property-string-test.php",
    "content": "<?php\n\n/**\n * @group properties\n */\nclass Papi_Property_String_Test extends Papi_Property_Test_Case {\n\n\tpublic $slugs = ['string_test', 'string_html_test'];\n\n\tpublic function get_value() {\n\t\t$args = func_get_args();\n\t\tswitch ( $args[0] ) {\n\t\t\tcase 'string_html_test':\n\t\t\t\treturn '<p>hej</p>';\n\t\t\tdefault:\n\t\t\t\treturn 'hello';\n\t\t}\n\t}\n\n\tpublic function get_expected() {\n\t\t$args = func_get_args();\n\t\tswitch ( $args[0] ) {\n\t\t\tcase 'string_html_test':\n\t\t\t\treturn '<p>hej</p>';\n\t\t\tdefault:\n\t\t\t\treturn 'hello';\n\t\t}\n\t}\n\n\tpublic function test_property_format_value() {\n\t\t$this->assertSame( $this->get_expected( 'string_test' ), $this->properties[0]->format_value( $this->get_value( 'string_test' ), '', 0 ) );\n\t\t$this->assertSame( $this->get_expected( 'string_html_test' ), $this->properties[1]->format_value( $this->get_value( 'string_html_test' ), '', 0 ) );\n\t\t$this->assertSame( 'Hello', $this->properties[0]->format_value( '<p>Hello</p>', '', 0 ) );\n\t\t$this->assertSame( '<p>Hello</p>', $this->properties[1]->format_value( '<p>Hello</p>', '', 0 ) );\n\t}\n\n\tpublic function test_property_options() {\n\t\t$this->assertSame( 'string', $this->properties[0]->get_option( 'type' ) );\n\t\t$this->assertSame( 'String test', $this->properties[0]->get_option( 'title' ) );\n\t\t$this->assertSame( 'papi_string_test', $this->properties[0]->get_option( 'slug' ) );\n\n\t\t$this->assertSame( 'string', $this->properties[1]->get_option( 'type' ) );\n\t\t$this->assertSame( 'String html test', $this->properties[1]->get_option( 'title' ) );\n\t\t$this->assertSame( 'papi_string_html_test', $this->properties[1]->get_option( 'slug' ) );\n\t}\n}\n"
  },
  {
    "path": "tests/cases/properties/class-papi-property-term-test.php",
    "content": "<?php\n\n/**\n * @group properties\n */\nclass Papi_Property_Term_Test extends Papi_Property_Test_Case {\n\n\tpublic $slugs = ['term_test', 'term_test_2'];\n\n\tpublic function setUp() {\n\t\tparent::setUp();\n\n\t\tregister_taxonomy( 'test_taxonomy', 'post' );\n\n\t\t$this->term_id = $this->factory->term->create( ['taxonomy' => 'test_taxonomy'] );\n\t}\n\n\tpublic function tearDown() {\n\t\tparent::tearDown();\n\n\t\tunset( $this->term_id );\n\t}\n\n\tpublic function get_value() {\n\t\t$args = func_get_args();\n\t\t$args[0] = isset( $args[0] ) ? $args[0] : $this->slugs[0];\n\t\tswitch ( $args[0] ) {\n\t\t\tdefault:\n\t\t\t\treturn $this->term_id;\n\t\t}\n\t}\n\n\tpublic function get_expected() {\n\t\t$args = func_get_args();\n\t\t$args[0] = isset( $args[0] ) ? $args[0] : $this->slugs[0];\n\t\tswitch ( $args[0] ) {\n\t\t\tdefault:\n\t\t\t\treturn get_term( $this->term_id, 'test_taxonomy' );\n\t\t}\n\t}\n\n\tpublic function assert_values( $expected, $actual, $slug ) {\n\t\tswitch ( $slug ) {\n\t\t\tcase 'term_test_2';\n\t\t\t\tbreak; // fail ok since we don't save a custom id.\n\t\t\tdefault:\n\t\t\t\t$this->assertSame( $expected->term_id, $actual->term_id );\n\t\t\t\tbreak;\n\t\t}\n\t}\n\n\tpublic function test_property_convert_type() {\n\t\t$this->assertSame( 'object', $this->property->convert_type );\n\t}\n\n\tpublic function test_property_format_value() {\n\t\t$this->assertEquals( get_term( $this->term_id, 'test_taxonomy' ), $this->property->format_value( $this->term_id, '', 0 ) );\n\t\t$this->assertEquals( get_term( $this->term_id, 'test_taxonomy' ), $this->property->format_value( strval( $this->term_id ), '', 0 ) );\n\t\t$this->assertNull( $this->property->format_value( 'hello', '', 0 ) );\n\t\t$this->assertNull( $this->property->format_value( null, '', 0 ) );\n\t\t$this->assertNull( $this->property->format_value( true, '', 0 ) );\n\t\t$this->assertNull( $this->property->format_value( false, '', 0 ) );\n\t\t$this->assertNull( $this->property->format_value( [], '', 0 ) );\n\t\t$this->assertNull( $this->property->format_value( (object) [], '', 0 ) );\n\t}\n\n\tpublic function test_property_format_value_meta_key() {\n\t\t$this->assertSame( 0, intval( $this->properties[1]->format_value( 1, '', 0 ) ) );\n\t\tupdate_term_meta( $this->term_id, 'custom_id', 1 );\n\t\t$this->assertSame( 1, intval( $this->properties[1]->format_value( 1, '', 0 ) ) );\n\t}\n\n\tpublic function test_property_options() {\n\t\t$this->assertSame( 'term', $this->property->get_option( 'type' ) );\n\t\t$this->assertSame( 'Term test', $this->property->get_option( 'title' ) );\n\t\t$this->assertSame( 'papi_term_test', $this->property->get_option( 'slug' ) );\n\t}\n\n\tpublic function test_property_settings() {\n\t\t$settings = $this->property->get_settings();\n\t\t$this->assertTrue( $settings->select2 );\n\t}\n}\n"
  },
  {
    "path": "tests/cases/properties/class-papi-property-test.php",
    "content": "<?php\n\nclass Papi_Property_Test extends WP_UnitTestCase {\n\n\tpublic function setUp() {\n\t\tparent::setUp();\n\n\t\tadd_filter( 'papi/settings/directories', function () {\n\t\t\treturn [1,  PAPI_FIXTURE_DIR . '/page-types'];\n\t\t} );\n\n\t\t$this->post_id = $this->factory->post->create();\n\t\t$_GET['post'] = $this->post_id;\n\t}\n\n\tpublic function tearDown() {\n\t\tparent::tearDown();\n\t\tunset( $_GET, $this->post_id );\n\t}\n\n\tpublic function test_get_value() {\n\t\t$property = new Papi_Property();\n\n\t\t$this->assertNull( $property->get_value() );\n\n\t\t$property = Papi_Property::factory();\n\n\t\t$this->assertNull( $property->get_value() );\n\n\t\t$property->set_options( [\n\t\t\t'type'  => 'string',\n\t\t\t'slug'  => 'name',\n\t\t\t'value' => 'Fredrik'\n\t\t] );\n\n\t\t$this->assertSame( 'Fredrik', $property->get_value() );\n\t}\n\n\tpublic function test_get_value_hardcoded() {\n\t\t$property = Papi_Property::factory( [\n\t\t\t'type'  => 'string',\n\t\t\t'slug'  => 'name',\n\t\t\t'value' => 'hello value'\n\t\t] );\n\n\t\t$this->assertSame( 'hello value', $property->get_value() );\n\n\t\tupdate_post_meta( $this->post_id, papi_get_page_type_key(), 'simple-page-type' );\n\n\t\t$page_type = papi_get_entry_type_by_id( 'simple-page-type' );\n\t\t$property  = $page_type->get_property( 'name_default' );\n\n\t\t$this->assertSame( 'Fredrik', $property->get_value() );\n\t}\n\n\tpublic function test_get_value_option() {\n\t\tglobal $current_screen;\n\n\t\t$current_screen = WP_Screen::get( 'admin_init' );\n\n\t\tadd_filter( 'papi/settings/directories', function () {\n\t\t\treturn [1,  PAPI_FIXTURE_DIR . '/page-types'];\n\t\t} );\n\n\t\t$_GET['page'] = 'papi/option/options/header-option-type';\n\n\t\t$store = papi_get_meta_store( 0, 'option' );\n\t\t$property = $store->get_property( 'name' );\n\t\t$this->assertSame( 'string', $property->get_option( 'type' ) );\n\t\t$this->assertSame( 'string', $property->type );\n\t\t$this->assertSame( 'papi_name', $property->slug );\n\t\t$this->assertSame( 'papi_name', $property->get_option( 'slug' ) );\n\t\t$this->assertSame( 'Name', $property->get_option( 'title' ) );\n\t\t$this->assertSame( 'Name', $property->title );\n\n\t\t$this->assertEmpty( $property->get_value() );\n\n\t\t$_SERVER['REQUEST_URI'] = 'http://site.com/wp-admin/options-general.php?page=papi/options/header-option-type';\n\n\t\tupdate_option( 'name', 'Fredrik' );\n\n\t\t$this->assertSame( 'Fredrik', $property->get_value() );\n\n\t\t$current_screen = null;\n\n\t\tunset( $_GET['page'] );\n\t}\n\n\tpublic function test_html() {\n\t\t$property = Papi_Property::factory();\n\t\t$this->assertEmpty( $property->html() );\n\t}\n\n\tpublic function test_render_with_description() {\n\t\t$property = Papi_Property::factory( [\n\t\t\t'description' => 'Hello, world',\n\t\t\t'slug'        => 'render_with_description_test',\n\t\t\t'type'        => 'string'\n\t\t] );\n\n\t\t$property->render();\n\n\t\t$this->expectOutputRegex( '/Hello\\,\\sworld/' );\n\t}\n\n\tpublic function test_render_with_html_array_slug() {\n\t\t$property = Papi_Property::factory( [\n\t\t\t'slug' => 'sections[0][render_with_description_test]',\n\t\t\t'type' => 'string'\n\t\t] );\n\n\t\t$this->assertTrue( $property->can_render() );\n\n\t\t$property->render();\n\n\t\t$this->expectOutputRegex( '/papi\\_sections\\[0\\]\\[render\\_with\\_description\\_test\\_property\\]/' );\n\t}\n\n\tpublic function test_render_with_lang() {\n\t\t$property = Papi_Property::factory( [\n\t\t\t'lang'  => 'dk',\n\t\t\t'raw'   => true,\n\t\t\t'slug'  => 'hidden_test_2',\n\t\t\t'title' => 'Hidden test 2',\n\t\t\t'type'  => 'hidden'\n\t\t] );\n\n\t\t$this->assertFalse( $property->can_render() );\n\n\t\t$property->render();\n\n\t\t$this->expectOutputRegex( '//' );\n\n\t\t$property = Papi_Property::factory( [\n\t\t\t'lang'  => 'dk',\n\t\t\t'raw'   => true,\n\t\t\t'slug'  => 'hidden_test_2',\n\t\t\t'title' => 'Hidden test 2',\n\t\t\t'type'  => 'hidden'\n\t\t] );\n\n\t\t$_GET['lang'] = 'dk';\n\n\t\t$this->assertTrue( $property->can_render() );\n\n\t\t$property->render();\n\n\t\t$this->expectOutputRegex( '/class=\\\"papi\\-hide/' );\n\n\t\tunset( $_GET['lang'] );\n\t}\n\n\tpublic function test_render_fail() {\n\t\t$property = Papi_Property::factory( [\n\t\t\t'disabled' => true,\n\t\t\t'slug'     => 'render_fail',\n\t\t\t'type'     => 'string',\n\t\t\t'title'    => 'Render fail'\n\t\t] );\n\n\t\t$property->render();\n\n\t\t$this->expectOutputRegex( '//' );\n\n\t\t$property = Papi_Property::factory( [\n\t\t\t'rules' => [\n\t\t\t\t[\n\t\t\t\t\t'operator' => '=',\n\t\t\t\t\t'slug'     => 'render_fail',\n\t\t\t\t\t'value'\t   => 'Fredrik'\n\t\t\t\t]\n\t\t\t],\n\t\t\t'slug'  => 'render_fail',\n\t\t\t'type'  => 'string',\n\t\t\t'title' => 'Render fail'\n\t\t] );\n\n\t\t$property->render();\n\n\t\t$this->expectOutputRegex( '//' );\n\t}\n\n\tpublic function test_render_with_after_class() {\n\t\t$page_type = papi_get_entry_type_by_id( 'simple-page-type' );\n\t\t$property  = $page_type->get_property( 'siffran' );\n\t\t$property->render();\n\t\t$this->expectOutputRegex( '/class=\\\"papi-after-html tva-siffra\\\"/' );\n\t}\n\n\tpublic function test_render_with_after_html() {\n\t\t$page_type = papi_get_entry_type_by_id( 'simple-page-type' );\n\t\t$property  = $page_type->get_property( 'siffran' );\n\t\t$property->render();\n\t\t$this->expectOutputRegex( '/\\>Två siffra\\<\\/div\\>/' );\n\t}\n\n\tpublic function test_render_with_before_class() {\n\t\t$page_type = papi_get_entry_type_by_id( 'simple-page-type' );\n\t\t$property  = $page_type->get_property( 'siffran' );\n\t\t$property->render();\n\t\t$this->expectOutputRegex( '/class=\\\"papi-before-html en-siffra\\\"/' );\n\t}\n\n\tpublic function test_render_with_before_html() {\n\t\t$page_type = papi_get_entry_type_by_id( 'simple-page-type' );\n\t\t$property  = $page_type->get_property( 'siffran' );\n\t\t$property->render();\n\t\t$this->expectOutputRegex( '/\\>En siffra\\<\\/div\\>/' );\n\t}\n\n\tpublic function test_format_cb_property() {\n\t\t$post_id = $this->factory->post->create();\n\n\t\tupdate_post_meta( $post_id, papi_get_page_type_key(), 'simple-page-type' );\n\t\tupdate_post_meta( $post_id, 'format_cb', 'Fredrik' );\n\n\t\t$value = papi_get_field( $post_id, 'format_cb' );\n\t\t$this->assertNotSame( $value, 'Fredrik' );\n\t\t$this->assertSame( $value, 'Hello Fredrik' );\n\t}\n}\n"
  },
  {
    "path": "tests/cases/properties/class-papi-property-text-test.php",
    "content": "<?php\n\n/**\n * @group properties\n */\nclass Papi_Property_Text_Test extends Papi_Property_Test_Case {\n\n\tpublic $slugs = ['text_test', 'text_html_test'];\n\n\tpublic function get_value() {\n\t\t$args = func_get_args();\n\t\tswitch ( $args[0] ) {\n\t\t\tcase 'text_html_test':\n\t\t\t\treturn '<p>hej</p>';\n\t\t\tdefault:\n\t\t\t\treturn 'hello';\n\t\t}\n\t}\n\n\tpublic function get_expected() {\n\t\t$args = func_get_args();\n\t\tswitch ( $args[0] ) {\n\t\t\tcase 'text_html_test':\n\t\t\t\treturn '<p>hej</p>';\n\t\t\tdefault:\n\t\t\t\treturn 'hello';\n\t\t}\n\t}\n\n\tpublic function test_property_format_value() {\n\t\t$this->assertSame( $this->get_expected( 'text_test' ), $this->properties[0]->format_value( $this->get_value( 'text_test' ), '', 0 ) );\n\t\t$this->assertSame( $this->get_expected( 'text_html_test' ), $this->properties[1]->format_value( $this->get_value( 'text_html_test' ), '', 0 ) );\n\t\t$this->assertSame( 'Hello', $this->properties[0]->format_value( '<p>Hello</p>', '', 0 ) );\n\t\t$this->assertSame( '<p>Hello</p>', $this->properties[1]->format_value( '<p>Hello</p>', '', 0 ) );\n\t\t$this->assertSame( \"Hello<br />\\nWorld\", $this->properties[0]->format_value( \"Hello\\nWorld\", '', 0 ) );\n\t}\n\n\tpublic function test_property_options() {\n\t\t$this->assertSame( 'text', $this->properties[0]->get_option( 'type' ) );\n\t\t$this->assertSame( 'Text test', $this->properties[0]->get_option( 'title' ) );\n\t\t$this->assertSame( 'papi_text_test', $this->properties[0]->get_option( 'slug' ) );\n\n\t\t$this->assertSame( 'text', $this->properties[1]->get_option( 'type' ) );\n\t\t$this->assertSame( 'Text html test', $this->properties[1]->get_option( 'title' ) );\n\t\t$this->assertSame( 'papi_text_html_test', $this->properties[1]->get_option( 'slug' ) );\n\t}\n}\n"
  },
  {
    "path": "tests/cases/properties/class-papi-property-url-test.php",
    "content": "<?php\n\n/**\n * @group properties\n */\nclass Papi_Property_Url_Test extends Papi_Property_Test_Case {\n\n\tpublic $slugs = ['url_test', 'url_mediauploader_test'];\n\n\tpublic function get_value() {\n\t\treturn 'http://github.com';\n\t}\n\n\tpublic function get_expected() {\n\t\treturn 'http://github.com';\n\t}\n\n\tpublic function test_property_format_value() {\n\t\t$this->assertSame( $this->get_expected( 'url_test' ), $this->properties[0]->format_value( $this->get_value( 'url_test' ), '', 0 ) );\n\t\t$this->assertSame( $this->get_expected( 'url_mediauploader_test' ), $this->properties[1]->format_value( $this->get_value( 'url_mediauploader_test' ), '', 0 ) );\n\t}\n\n\tpublic function test_property_load_value() {\n\t\t$this->assertSame( 'http://wordpress.org', $this->properties[0]->load_value( 'http://wordpress.org', '', 0 ) );\n\t\t$this->assertNull( $this->properties[0]->load_value( 'hello', '', 0 ) );\n\t\t$this->assertNull( $this->properties[0]->load_value( null, '', 0 ) );\n\n\t\t$this->assertSame( 'http://wordpress.org', $this->properties[1]->load_value( 'http://wordpress.org', '', 0 ) );\n\t\t$this->assertNull( $this->properties[1]->load_value( 'hello', '', 0 ) );\n\t\t$this->assertNull( $this->properties[1]->load_value( null, '', 0 ) );\n\t}\n\n\tpublic function test_property_options() {\n\t\t$this->assertSame( 'url', $this->properties[0]->get_option( 'type' ) );\n\t\t$this->assertSame( 'Url test', $this->properties[0]->get_option( 'title' ) );\n\t\t$this->assertSame( 'papi_url_test', $this->properties[0]->get_option( 'slug' ) );\n\n\t\t$this->assertSame( 'url', $this->properties[1]->get_option( 'type' ) );\n\t\t$this->assertSame( 'Url mediauploader test', $this->properties[1]->get_option( 'title' ) );\n\t\t$this->assertSame( 'papi_url_mediauploader_test', $this->properties[1]->get_option( 'slug' ) );\n\t}\n\n\tpublic function test_property_output() {\n\t\tparent::test_property_output();\n\t\t$this->expectOutputRegex( '/class\\=\\\"button papi-url-media-button\\\"/' );\n\t}\n\n\tpublic function test_property_update_value() {\n\t\t$this->assertSame( 'http://wordpress.org', $this->properties[0]->update_value( 'http://wordpress.org', '', 0 ) );\n\t\t$this->assertNull( $this->properties[0]->update_value( 'hello', '', 0 ) );\n\t\t$this->assertNull( $this->properties[0]->update_value( null, '', 0 ) );\n\n\t\t$this->assertSame( 'http://wordpress.org', $this->properties[1]->update_value( 'http://wordpress.org', '', 0 ) );\n\t\t$this->assertNull( $this->properties[1]->update_value( 'hello', '', 0 ) );\n\t\t$this->assertNull( $this->properties[1]->update_value( null, '', 0 ) );\n\t}\n}\n"
  },
  {
    "path": "tests/cases/properties/class-papi-property-user-test.php",
    "content": "<?php\n\n/**\n * @group properties\n */\nclass Papi_Property_User_Test extends Papi_Property_Test_Case {\n\n\tpublic $slug = 'user_test';\n\n\tpublic function assert_values( $expected, $actual, $slug ) {\n\t\t$this->assertSame( $expected->ID, $actual->ID );\n\t}\n\n\tpublic function get_value() {\n\t\treturn new WP_User( 1 );\n\t}\n\n\tpublic function get_expected() {\n\t\treturn new WP_User( 1 );\n\t}\n\n\tpublic function test_property_convert_type() {\n\t\t$this->assertSame( 'int', $this->property->convert_type );\n\t}\n\n\tpublic function test_property_format_value() {\n\t\t$this->assertEquals( new WP_User( 1 ), $this->property->format_value( '1', '', 0 ) );\n\t\t$this->assertEquals( new WP_User( 1 ), $this->property->format_value( 1, '', 0 ) );\n\t\t$this->assertEquals( new WP_User( 1 ), $this->property->format_value( (object) ['ID' => 1], '', 0 ) );\n\t\t$this->assertEquals( new WP_User( 1 ), $this->property->format_value( new WP_User( 1 ), '', 0 ) );\n\t\t$this->assertNull( $this->property->format_value( null, '', 0 ) );\n\t}\n\n\tpublic function test_property_options() {\n\t\t$this->assertSame( 'user', $this->property->get_option( 'type' ) );\n\t\t$this->assertSame( 'User test', $this->property->get_option( 'title' ) );\n\t\t$this->assertSame( 'papi_user_test', $this->property->get_option( 'slug' ) );\n\t}\n\n\tpublic function test_property_settings() {\n\t\t$settings = $this->property->get_settings();\n\t\t$this->assertSame( 'Select user', $settings->placeholder );\n\t\t$this->assertTrue( $settings->select2 );\n\t}\n}\n"
  },
  {
    "path": "tests/cases/query/class-papi-query-test.php",
    "content": "<?php\n\n/**\n * @group query\n */\nclass Papi_Query_Test extends WP_UnitTestCase {\n\n\tpublic function test_empty_query() {\n\t\t$query = new Papi_Query();\n\n\t\t$this->assertEmpty( $query->get_result() );\n\t}\n\n\tpublic function test_missing_query() {\n\t\t$query = new Papi_Query( [\n\t\t\t'entry_type' => 'fake-page-type',\n\t\t\t'fields'     => 'ids'\n\t\t] );\n\n\t\t$this->assertEmpty( $query->get_result() );\n\n\t\t$post_id = $this->factory->post->create( ['post_type' => 'page'] );\n\t\tupdate_post_meta( $post_id, papi_get_page_type_key(), 'fake-page-type' );\n\n\t\t$this->assertEmpty( $query->get_result() );\n\t}\n\n\tpublic function test_first() {\n\t\t$query = new Papi_Query( [\n\t\t\t'entry_type' => 'simple-page-type',\n\t\t\t'fields'     => 'ids'\n\t\t] );\n\n\t\t$this->assertEmpty( $query->first() );\n\n\t\tadd_filter( 'papi/settings/directories', function () {\n\t\t\treturn PAPI_FIXTURE_DIR . '/page-types';\n\t\t} );\n\n\t\t$post_id1 = $this->factory->post->create( ['post_type' => 'page'] );\n\t\tupdate_post_meta( $post_id1, papi_get_page_type_key(), 'simple-page-typee' );\n\n\t\t$post_id2 = $this->factory->post->create( ['post_type' => 'page'] );\n\t\tupdate_post_meta( $post_id2, papi_get_page_type_key(), 'simple-page-type' );\n\n\t\t$result = $query->get_result();\n\t\t$first  = array_shift( $result );\n\n\t\t$this->assertSame( $first, $query->first() );\n\t}\n\n\tpublic function test_last() {\n\t\t$query = new Papi_Query( [\n\t\t\t'entry_type' => 'simple-page-type',\n\t\t\t'fields'     => 'ids'\n\t\t] );\n\n\t\t$this->assertEmpty( $query->last() );\n\n\t\tadd_filter( 'papi/settings/directories', function () {\n\t\t\treturn PAPI_FIXTURE_DIR . '/page-types';\n\t\t} );\n\n\t\t$post_id1 = $this->factory->post->create( ['post_type' => 'page'] );\n\t\tupdate_post_meta( $post_id1, papi_get_page_type_key(), 'simple-page-type' );\n\n\t\t$post_id2 = $this->factory->post->create( ['post_type' => 'page'] );\n\t\tupdate_post_meta( $post_id2, papi_get_page_type_key(), 'simple-page-type' );\n\n\t\t$result = $query->get_result();\n\t\t$last   = array_pop( $result );\n\n\t\t$this->assertSame( $last, $query->last() );\n\t}\n\n\tpublic function test_simple_query() {\n\t\t$query = new Papi_Query( [\n\t\t\t'entry_type' => 'simple-page-type',\n\t\t\t'fields'     => 'ids'\n\t\t] );\n\n\n\t\t$this->assertEmpty( $query->get_result() );\n\n\t\tadd_filter( 'papi/settings/directories', function () {\n\t\t\treturn PAPI_FIXTURE_DIR . '/page-types';\n\t\t} );\n\n\t\t$post_id = $this->factory->post->create( ['post_type' => 'page'] );\n\t\tupdate_post_meta( $post_id, papi_get_page_type_key(), 'simple-page-type' );\n\n\t\t$this->assertSame( [$post_id], $query->get_result() );\n\t}\n\n\tpublic function test_real_page_type_query_fail() {\n\t\t$query = new Papi_Query( [\n\t\t\t'fields'    => 'ids',\n\t\t\t'page_type' => 'simple-page-type'\n\t\t] );\n\n\t\tadd_filter( 'papi/settings/directories', function () {\n\t\t\treturn PAPI_FIXTURE_DIR . '/page-types';\n\t\t} );\n\n\t\t$post_id = $this->factory->post->create();\n\t\tupdate_post_meta( $post_id, papi_get_page_type_key(), 'simple-page-type' );\n\n\t\t// Wrong post type.\n\t\t$this->assertEmpty( $query->posts );\n\t}\n\n\tpublic function test_real_page_type_query_success() {\n\t\t$query = new Papi_Query( [\n\t\t\t'fields'    => 'ids',\n\t\t\t'page_type' => 'simple-page-type'\n\t\t] );\n\n\t\tadd_filter( 'papi/settings/directories', function () {\n\t\t\treturn PAPI_FIXTURE_DIR . '/page-types';\n\t\t} );\n\n\t\t$post_id = $this->factory->post->create( ['post_type' => 'page'] );\n\t\tupdate_post_meta( $post_id, papi_get_page_type_key(), 'simple-page-type' );\n\n\t\t$this->assertSame( [$post_id], $query->posts );\n\t}\n\n\tpublic function test_real_page_type_meta_query() {\n\t\t$query = new Papi_Query( [\n\t\t\t'fields'     => 'ids',\n\t\t\t'page_type'  => 'simple-page-type',\n\t\t\t'meta_query' => [\n\t\t\t\t[\n\t\t\t\t\t'key'   => 'name',\n\t\t\t\t\t'value' => 'Fredrik'\n\t\t\t\t]\n\t\t\t]\n\t\t] );\n\n\t\tadd_filter( 'papi/settings/directories', function () {\n\t\t\treturn PAPI_FIXTURE_DIR . '/page-types';\n\t\t} );\n\n\t\t$post_id = $this->factory->post->create( ['post_type' => 'page'] );\n\t\tupdate_post_meta( $post_id, papi_get_page_type_key(), 'simple-page-type' );\n\n\t\t$this->assertEmpty( $query->posts );\n\n\t\tupdate_post_meta( $post_id, 'name', 'Fredrik' );\n\n\t\t$this->assertSame( [$post_id], $query->posts );\n\t}\n\n\tpublic function test_real_page_type_meta_key_value() {\n\t\t$query = new Papi_Query( [\n\t\t\t'fields'       => 'ids',\n\t\t\t'page_type'    => 'simple-page-type',\n\t\t\t'meta_key'     => 'name',\n\t\t\t'meta_value'   => 'Fredrik',\n\t\t    'meta_compare' => '='\n\t\t] );\n\n\t\tadd_filter( 'papi/settings/directories', function () {\n\t\t\treturn PAPI_FIXTURE_DIR . '/page-types';\n\t\t} );\n\n\t\t$post_id = $this->factory->post->create( ['post_type' => 'page'] );\n\t\tupdate_post_meta( $post_id, papi_get_page_type_key(), 'simple-page-type' );\n\n\t\t$this->assertEmpty( $query->posts );\n\n\t\tupdate_post_meta( $post_id, 'name', 'Fredrik' );\n\n\t\t$this->assertSame( [$post_id], $query->posts );\n\t}\n\n\tpublic function test_real_taxonomy_type_query_fail() {\n\t\t$query = new Papi_Query( [\n\t\t\t'fields'        => 'ids',\n\t\t\t'taxonomy_type' => 'simple-taxonomy-type'\n\t\t], 'term' );\n\n\t\tadd_filter( 'papi/settings/directories', function () {\n\t\t\treturn PAPI_FIXTURE_DIR . '/taxonomy-types';\n\t\t} );\n\n\t\t$term_id = $this->factory->term->create( ['taxonomy' => 'fake'] );\n\t\tupdate_term_meta( $term_id, papi_get_page_type_key(), 'simple-taxonomy-type' );\n\n\t\t// Wrong taxonomy.\n\t\t$this->assertEmpty( $query->terms );\n\t}\n\n\tpublic function test_real_taxonomy_type_query_success() {\n\t\t$query = new Papi_Query( [\n\t\t\t'fields'        => 'ids',\n\t\t\t'hide_empty'    => false,\n\t\t\t'taxonomy_type' => 'simple-taxonomy-type'\n\t\t], 'term' );\n\n\t\tadd_filter( 'papi/settings/directories', function () {\n\t\t\treturn PAPI_FIXTURE_DIR . '/taxonomy-types';\n\t\t} );\n\n\t\t$term_id = $this->factory->term->create( ['taxonomy' => 'category'] );\n\t\tupdate_term_meta( $term_id, papi_get_page_type_key(), 'simple-taxonomy-type' );\n\n\t\t$this->assertEquals( [$term_id], $query->terms );\n\t}\n\n\tpublic function test_real_taxonomy_type_meta_query() {\n\t\t$query = new Papi_Query( [\n\t\t\t'fields'     => 'ids',\n\t\t\t'entry_type' => 'simple-taxonomy-type',\n\t\t\t'hide_empty' => false,\n\t\t\t'meta_query' => [\n\t\t\t\t[\n\t\t\t\t\t'key'   => 'name',\n\t\t\t\t\t'value' => 'Fredrik'\n\t\t\t\t]\n\t\t\t]\n\t\t], 'term' );\n\n\t\tadd_filter( 'papi/settings/directories', function () {\n\t\t\treturn PAPI_FIXTURE_DIR . '/taxonomy-types';\n\t\t} );\n\n\t\t$term_id = $this->factory->term->create( ['taxonomy' => 'category'] );\n\t\tupdate_term_meta( $term_id, papi_get_page_type_key(), 'simple-taxonomy-type' );\n\n\t\t$this->assertEmpty( $query->terms );\n\n\t\tupdate_term_meta( $term_id, 'name', 'Fredrik' );\n\n\t\t$this->assertEquals( [$term_id], $query->terms );\n\t}\n\n\tpublic function test_real_taxonomy_type_meta_key_value() {\n\t\t$query = new Papi_Query( [\n\t\t\t'fields'     => 'ids',\n\t\t\t'entry_type' => 'simple-taxonomy-type',\n\t\t\t'meta_key'   => 'name',\n\t\t\t'meta_value' => 'Fredrik',\n\t\t\t'hide_empty' => false\n\t\t], 'term' );\n\n\t\tadd_filter( 'papi/settings/directories', function () {\n\t\t\treturn PAPI_FIXTURE_DIR . '/taxonomy-types';\n\t\t} );\n\n\t\t$term_id = $this->factory->term->create( ['taxonomy' => 'category'] );\n\t\tupdate_term_meta( $term_id, papi_get_page_type_key(), 'simple-taxonomy-type' );\n\n\t\t$this->assertEmpty( $query->terms );\n\n\t\tupdate_term_meta( $term_id, 'name', 'Fredrik' );\n\n\t\t$this->assertEquals( [$term_id], $query->terms );\n\t}\n}\n"
  },
  {
    "path": "tests/cases/rest-api/class-papi-rest-api-post-test.php",
    "content": "<?php\n\n/**\n * @group rest-api\n */\nclass Papi_REST_API_Post_Test extends WP_UnitTestCase {\n\n\t/**\n\t * @var Papi_REST_API_Post\n\t */\n\tprotected $class;\n\n\tpublic function setUp() {\n\t\tparent::setUp();\n\n\t\tadd_filter( 'papi/settings/directories', function () {\n\t\t\treturn PAPI_FIXTURE_DIR . '/page-types';\n\t\t} );\n\n\t\t$this->class = new Papi_REST_API_Post;\n\t}\n\n\tpublic function tearDown() {\n\t\tparent::tearDown();\n\n\t\tunset( $this->class );\n\t}\n\n\tpublic function test_actions() {\n\t\t$this->assertSame( 10, has_action( 'the_post', [$this->class, 'get_post'] ) );\n\t\t$this->assertSame( 10, has_action( 'rest_api_init', [$this->class, 'setup_fields'] ) );\n\t}\n\n\tpublic function test_get_page_type() {\n\t\t$this->assertEmpty( $this->class->get_page_type( [], 'page_type', null ) );\n\n\t\t$post_id = $this->factory->post->create( [\n\t\t\t'post_type' => 'page'\n\t\t] );\n\n\t\t$page_type = $this->class->get_page_type( ['ID' => $post_id], 'page_type', null );\n\n\t\t$this->assertSame( '', $page_type );\n\t\tupdate_post_meta( $post_id, papi_get_page_type_key(), 'simple-content-page-type' );\n\n\t\t$page_type = $this->class->get_page_type( ['ID' => $post_id], 'page_type', null );\n\t\t$this->assertSame( $page_type, get_post_meta( $post_id, papi_get_page_type_key(), true ) );\n\t}\n\n\tpublic function test_get_post() {\n\t\t$post_id = $this->factory->post->create( ['post_type' => 'page'] );\n\t\t$post = get_post( $post_id );\n\t\t$this->assertSame( $post, $this->class->get_post( $post ) );\n\n\t\t$post_id = $this->factory->post->create();\n\t\t$post = get_post( $post_id );\n\t\tupdate_post_meta( $post_id, papi_get_page_type_key(), 'simple-page-type' );\n\n\t\t$this->assertSame( $post, $this->class->get_post( $post ) );\n\t\t$this->assertSame( 10, has_filter( 'rest_prepare_' . $post->post_type, [$this->class, 'prepare_response'] ) );\n\n\t\t$page_type = papi_get_entry_type_by_meta_id( $post->ID );\n\n\t\tglobal $wp_meta_keys;\n\n\t\tif ( ! is_array( $wp_meta_keys ) ) {\n\t\t\t$this->markTestSkipped( '`$wp_meta_keys` is not a array' );\n\t\t}\n\n\t\t$meta_keys = $wp_meta_keys[$post->post_type];\n\t\t$meta_keys = count( $meta_keys ) === 1 ? array_shift( $meta_keys ) : $meta_keys;\n\n\t\tforeach ( $page_type->get_properties() as $property ) {\n\t\t\t$this->assertArrayHasKey( $property->get_slug( true ), $meta_keys );\n\t\t}\n\t}\n\n\tpublic function test_prepare_response() {\n\t\t$this->assertEmpty( $this->class->prepare_response( [] ) );\n\n\t\t$post_id = $this->factory->post->create();\n\t\tglobal $post;\n\t\t$post = get_post( $post_id );\n\n\t\tupdate_post_meta( $post_id, papi_get_page_type_key(), 'properties-page-type' );\n\t\t$this->assertSame( $post, $this->class->get_post( $post ) );\n\n\t\tupdate_post_meta( $post_id, 'post_test', $post_id );\n\t\t$response = new \\stdClass;\n\t\t$response->data = [\n\t\t\t'meta' => [\n\t\t\t\t'post_test' => $post_id\n\t\t\t]\n\t\t];\n\n\t\t$response = $this->class->prepare_response( $response );\n\t\t$this->assertSame( $post->ID, $response->data['meta']['post_test']->ID );\n\t}\n\n\tpublic function test_setup_fields() {\n\t\tglobal $wp_rest_additional_fields;\n\n\t\t$this->class->setup_fields();\n\n\t\tif ( ! is_array( $wp_rest_additional_fields ) ) {\n\t\t\t$this->markTestSkipped( '`register_rest_field` is only supported in WordPress 4.7 and later' );\n\t\t}\n\n\t\t$this->assertArrayHasKey( 'page_type', $wp_rest_additional_fields['page'] );\n\t}\n}\n"
  },
  {
    "path": "tests/cases/rest-api/class-papi-rest-api-settings-test.php",
    "content": "<?php\n\n/**\n * @group rest-api\n */\nclass Papi_REST_API_Settings_Test extends WP_UnitTestCase {\n\n\tpublic function setUp() {\n\t\tparent::setUp();\n\n\t\tif ( version_compare( get_bloginfo( 'version' ), '4.6', '<=' ) ) {\n\t\t\t$this->markTestSkipped( '`register_settings` is only supported in WordPress 4.7 and later' );\n\t\t}\n\n\t\tadd_filter( 'papi/settings/directories', function () {\n\t\t\treturn PAPI_FIXTURE_DIR . '/page-types/options';\n\t\t} );\n\n\t\t$this->class = new Papi_REST_API_Settings;\n\t}\n\n\tpublic function tearDown() {\n\t\tparent::tearDown();\n\n\t\tunset( $this->class );\n\t}\n\n\tpublic function test_actions() {\n\t\t$this->assertSame( 10, has_action( 'rest_api_init', [$this->class, 'register'] ) );\n\t\t$this->assertSame( 10, has_filter( 'rest_pre_get_setting', [$this->class, 'pre_get_setting'] ) );\n\t}\n\n\tpublic function test_pre_get_setting() {\n\t\t$this->assertFalse( has_filter( 'rest_request_after_callbacks', [$this->class, 'prepare_response'] ) );\n\t\t$this->class->pre_get_setting( null );\n\t\t$this->assertSame( 10, has_filter( 'rest_request_after_callbacks', [$this->class, 'prepare_response'] ) );\n\t}\n\n\tpublic function test_get_setting() {\n\t\t$this->assertNull( $this->class->get_setting( 'name', null ) );\n\n\t\tupdate_option( 'name', 'Fredrik' );\n\t\t$this->class->register();\n\n\t\t$this->assertSame( 'Fredrik', $this->class->get_setting( 'name', null ) );\n\t}\n\n\tpublic function test_prepare_response() {\n\t\t$response = [\n\t\t\t'name' => null\n\t\t];\n\n\t\t$response = $this->class->prepare_response( $response );\n\t\t$this->assertSame( ['name' => null], $response );\n\n\t\t$response = [\n\t\t\t'name' => null\n\t\t];\n\n\t\t$this->class->register();\n\t\t$response = $this->class->prepare_response( $response );\n\t\t$this->assertSame( ['name' => null], $response );\n\n\t\t$response = [\n\t\t\t'name' => null\n\t\t];\n\n\t\tupdate_option( 'name', 'Fredrik' );\n\t\t$this->class->register();\n\t\t$response = $this->class->prepare_response( $response );\n\t\t$this->assertSame( ['name' => 'Fredrik'], $response );\n\t}\n}\n"
  },
  {
    "path": "tests/cases/rest-api/class-papi-rest-api-test.php",
    "content": "<?php\n\n/**\n * @group rest-api\n */\nclass Papi_REST_API_Test extends WP_UnitTestCase {\n\n\tpublic function setUp() {\n\t\tparent::setUp();\n\n\t\t$this->class = new Papi_REST_API;\n\t}\n\n\tpublic function tearDown() {\n\t\tparent::tearDown();\n\n\t\tunset( $this->class );\n\t}\n\n\tpublic function test_actions() {\n\t\t$this->assertSame( 10, has_action( 'rest_api_init', [$this->class, 'rest_api_init'] ) );\n\t\t$this->assertSame( 10, has_action( 'rest_api_init', [$this->class, 'register_routes'] ) );\n\t}\n}\n"
  },
  {
    "path": "tests/cases/stores/class-papi-option-store-test.php",
    "content": "<?php\n\n/**\n * @group stores\n */\nclass Papi_Option_Store_Test extends WP_UnitTestCase {\n\n\tpublic function setUp() {\n\t\tparent::setUp();\n\n\t\tadd_filter( 'papi/settings/directories', function () {\n\t\t\treturn [1,  PAPI_FIXTURE_DIR . '/page-types'];\n\t\t} );\n\n\t\t$this->store = papi_get_meta_store( 0, 'option' );\n\n\t\t$_GET = [];\n\t}\n\n\tpublic function tearDown() {\n\t\tparent::tearDown();\n\t\tunset( $_GET, $this->store );\n\t}\n\n\tpublic function test_get_property() {\n\t\t$this->assertNull( $this->store->get_property( 'fake' ) );\n\n\t\t$property = $this->store->get_property( 'name' );\n\t\t$this->assertSame( 'string', $property->get_option( 'type' ) );\n\t\t$this->assertSame( 'string', $property->type );\n\t\t$this->assertSame( 'papi_name', $property->slug );\n\t\t$this->assertSame( 'papi_name', $property->get_option( 'slug' ) );\n\t\t$this->assertSame( 'Name', $property->get_option( 'title' ) );\n\t\t$this->assertSame( 'Name', $property->title );\n\n\t\t$_GET['page'] = 'papi/option/options/header-option-type';\n\n\t\t$property = $this->store->get_property( 'name' );\n\t\t$this->assertSame( 'string', $property->get_option( 'type' ) );\n\t\t$this->assertSame( 'string', $property->type );\n\t\t$this->assertSame( 'papi_name', $property->slug );\n\t\t$this->assertSame( 'papi_name', $property->get_option( 'slug' ) );\n\t\t$this->assertSame( 'Name', $property->get_option( 'title' ) );\n\t\t$this->assertSame( 'Name', $property->title );\n\n\t\t$_GET['page'] = 'papi/page/modules/top-module-type';\n\t\t$this->assertNull( $this->store->get_property( 'name' ) );\n\t}\n\n\tpublic function test_get_value() {\n\t\t$this->assertNull( $this->store->get_value( 1 ) );\n\t\t$this->assertNull( $this->store->get_value( '' ) );\n\t\t$this->assertNull( $this->store->get_value( 99999, 'fake' ) );\n\n\t\t$property = $this->store->get_property( 'name' );\n\t\t$this->assertEmpty( $property->get_value() );\n\n\t\tupdate_option( 'name', 'Fredrik' );\n\t\t$this->assertSame( 'Fredrik', $this->store->get_value( 'name' ) );\n\n\t\tupdate_option( 'hello', 'Fredrik' );\n\t\t$this->assertNull( $this->store->get_value( 'hello' ) );\n\t}\n\n\tpublic function test_get_value_cache() {\n\t\tpapi_data_update( 0, 'name', 'fredrik', 'option' );\n\n\t\t$this->assertSame( 'fredrik', $this->store->get_value( 0, 'name' ) );\n\t\t$this->assertSame( 'fredrik', papi_cache_get( 'name', 0 ) );\n\n\t\t// Turn off property cache.\n\t\tadd_filter( 'papi/get_property', function ( $property ) {\n\t\t\t$property->set_option( 'cache', false );\n\t\t\treturn $property;\n\t\t} );\n\n\t\t$this->assertSame( 'fredrik', $this->store->get_value( 0, 'name' ) );\n\t\t$this->assertEmpty( papi_cache_get( 'name', 0 ) );\n\t}\n\n\tpublic function test_valid() {\n\t\t$this->assertTrue( $this->store->valid() );\n\t}\n}\n"
  },
  {
    "path": "tests/cases/stores/class-papi-post-store-test.php",
    "content": "<?php\n\n/**\n * @group stores\n */\nclass Papi_Post_Store_Test extends WP_UnitTestCase {\n\n\tpublic function setUp() {\n\t\tparent::setUp();\n\n\t\tadd_filter( 'papi/settings/directories', function () {\n\t\t\treturn [1,  PAPI_FIXTURE_DIR . '/page-types'];\n\t\t} );\n\n\t\t$this->post_id = $this->factory->post->create();\n\t\t$_GET['post'] = $this->post_id;\n\n\t\t$this->store = papi_get_meta_store( $this->post_id );\n\n\t\tupdate_post_meta( $this->post_id, papi_get_page_type_key(), 'simple-page-type' );\n\t}\n\n\tpublic function tearDown() {\n\t\tparent::tearDown();\n\t\tunset( $this->post_id, $_GET['post'], $this->store );\n\t}\n\n\tpublic function test_post_store_construct() {\n\t\t$this->assertTrue( ( new Papi_Post_Store )->valid() );\n\t\t$this->assertTrue( ( new Papi_Post_Store( $this->post_id ) )->valid() );\n\t}\n\n\tpublic function test_get_type_class() {\n\t\tupdate_post_meta( $this->post_id, papi_get_page_type_key(), 'simple-page-type' );\n\n\t\t$store = papi_get_meta_store( $this->post_id );\n\n\t\t$this->assertSame( $store->get_type_class()->name, 'Simple page' );\n\t}\n\n\tpublic function test_get_permalink() {\n\t\t$permalink = $this->store->get_permalink();\n\t\t$this->assertFalse( empty( $permalink ) );\n\t}\n\n\tpublic function test_get_post() {\n\t\t$this->assertTrue( is_object( $this->store->get_post() ) );\n\t\t$this->assertSame( $this->post_id, $this->store->get_post()->ID );\n\t}\n\n\tpublic function test_get_status() {\n\t\t$this->assertSame( 'publish', $this->store->get_status() );\n\t}\n\n\tpublic function test_get_value() {\n\t\tupdate_post_meta( $this->post_id, 'name', 'fredrik' );\n\n\t\t$this->assertNull( $this->store->get_value( 1 ) );\n\t\t$this->assertNull( $this->store->get_value( '' ) );\n\t\t$this->assertNull( $this->store->get_value( $this->post_id, '' ) );\n\t\t$this->assertNull( $this->store->get_value( 99999, 'fake' ) );\n\n\t\t$this->assertSame( 'fredrik', $this->store->get_value( $this->post_id, 'name' ) );\n\t\t$this->assertSame( 'fredrik', $this->store->get_value( $this->post_id, 'name', '', 'post' ) );\n\n\t\t$this->assertSame( 'world', $this->store->get_value( $this->post_id, 'hello', 'world' ) );\n\t}\n\n\tpublic function test_get_value_cache() {\n\t\tpapi_data_update( $this->post_id, 'name', 'fredrik' );\n\n\t\t$this->assertSame( 'fredrik', $this->store->get_value( $this->post_id, 'name' ) );\n\t\t$this->assertSame( 'fredrik', papi_cache_get( 'name', $this->post_id ) );\n\n\t\t// Turn off property cache.\n\t\tadd_filter( 'papi/get_property', function ( $property ) {\n\t\t\t$property->set_option( 'cache', false );\n\t\t\treturn $property;\n\t\t} );\n\n\t\t$this->assertSame( 'fredrik', $this->store->get_value( $this->post_id, 'name' ) );\n\t\t$this->assertEmpty( papi_cache_get( 'name', $this->post_id ) );\n\t}\n\n\tpublic function test_load_value() {\n\t\tupdate_post_meta( $this->post_id, papi_get_page_type_key(), 'simple-page-type' );\n\n\t\tupdate_post_meta( $this->post_id, 'name', 'Janni' );\n\t\t$this->assertSame( 'Janni', $this->store->load_value( 'name' ) );\n\n\t\tupdate_post_meta( $this->post_id, 'name', 'Fredrik' );\n\n\t\t$this->assertSame( 'Fredrik', $this->store->load_value( 'name' ) );\n\t}\n\n\tpublic function test_format_value() {\n\t\t$handler = new Papi_Admin_Meta_Handler();\n\t\tupdate_post_meta( $this->post_id, papi_get_page_type_key(), 'simple-page-type' );\n\n\t\tadd_filter( 'papi/settings/directories', function () {\n\t\t\treturn [1,  PAPI_FIXTURE_DIR . '/page-types'];\n\t\t} );\n\n\t\t$page_type = papi_get_entry_type_by_id( 'simple-page-type' );\n\t\t$property  = $page_type->get_property( 'name' );\n\n\t\t$_POST = papi_test_create_property_post_data( [\n\t\t\t'slug'  => $property->get_option( 'slug' ),\n\t\t\t'type'  => $property,\n\t\t\t'value' => 'Fredrik'\n\t\t], $_POST );\n\n\t\t$handler->save_properties( $this->post_id );\n\n\t\t$value = $this->store->load_value( $property->get_option( 'slug' ) );\n\t\t$value = $this->store->format_value( $property->get_option( 'slug' ), $value );\n\n\t\t$this->assertSame( 'Fredrik', $value );\n\t}\n\n\tpublic function test_post_store_editor_value() {\n\t\t$handler = new Papi_Admin_Meta_Handler();\n\t\tupdate_post_meta( $this->post_id, papi_get_page_type_key(), 'editor-page-type' );\n\n\t\ttests_add_filter( 'papi/settings/directories', function () {\n\t\t\treturn [1,  PAPI_FIXTURE_DIR . '/page-types'];\n\t\t} );\n\n\t\t$page_type = papi_get_entry_type_by_id( 'editor-page-type' );\n\t\t$property  = $page_type->get_property( 'editor' );\n\n\t\t$_POST = papi_test_create_property_post_data( [\n\t\t\t'slug'  => $property->get_option( 'slug' ),\n\t\t\t'type'  => $property,\n\t\t\t'value' => '[embed width=\"123\" height=\"456\"]http://wordpress.org[/embed]'\n\t\t], $_POST );\n\n\t\t$handler->save_properties( $this->post_id );\n\n\t\t$value = $this->store->load_value( $property->get_option( 'slug' ) );\n\t\t$this->assertSame( '[embed width=\"123\" height=\"456\"]http://wordpress.org[/embed]', $value );\n\n\t\t$value = $this->store->format_value( $property->get_option( 'slug' ), $value );\n\t\t$this->assertSame( '<p><a href=\"http://wordpress.org\">http://wordpress.org</a></p>', trim( $value ) );\n\t}\n\n\tpublic function test_get_property() {\n\t\tupdate_post_meta( $this->post_id, papi_get_page_type_key(), 'random322-page-type' );\n\t\t$store = papi_get_meta_store( $this->post_id );\n\t\t$this->assertNull( $store->get_property( 'fake' ) );\n\t}\n\n\tpublic function test_valid() {\n\t\t$this->assertTrue( $this->store->valid() );\n\t}\n}\n"
  },
  {
    "path": "tests/cases/stores/class-papi-term-store-test.php",
    "content": "<?php\n\n/**\n * @group stores\n */\nclass Papi_Term_Store_Test extends WP_UnitTestCase {\n\n\tpublic function setUp() {\n\t\tparent::setUp();\n\n\t\tadd_filter( 'papi/settings/directories', function () {\n\t\t\treturn [1,  PAPI_FIXTURE_DIR . '/taxonomy-types'];\n\t\t} );\n\n\t\t$this->term_id = $this->factory->term->create();\n\t\t$_GET['term_id'] = $this->term_id;\n\t\t$_GET['meta_type'] = 'term';\n\t\t$this->store = papi_get_meta_store( $this->term_id, 'term' );\n\t}\n\n\tpublic function tearDown() {\n\t\tparent::tearDown();\n\t\tunset( $this->term_id, $_GET['term_id'], $_GET['meta_type'], $this->store );\n\t}\n\n\tpublic function test_term_store_construct() {\n\t\t$this->assertTrue( ( new Papi_Term_Store )->valid() );\n\t\t$this->assertTrue( ( new Papi_Term_Store( $this->term_id ) )->valid() );\n\t}\n\n\tpublic function test_get_permalink() {\n\t\t$permalink = $this->store->get_permalink();\n\t\t$this->assertFalse( empty( $permalink ) );\n\t}\n\n\tpublic function test_get_term() {\n\t\t$this->assertTrue( is_object( $this->store->get_term() ) );\n\t\t$this->assertSame( $this->term_id, $this->store->get_term()->term_id );\n\t}\n\n\tpublic function test_load_value() {\n\t\tupdate_term_meta( $this->term_id, papi_get_page_type_key(), 'properties-taxonomy-type' );\n\n\t\tupdate_term_meta( $this->term_id, 'string_test', 'Janni' );\n\t\t$this->assertSame( 'Janni', $this->store->load_value( 'string_test' ) );\n\n\t\tupdate_term_meta( $this->term_id, 'string_test', 'Fredrik' );\n\n\t\t$this->assertSame( 'Fredrik', $this->store->load_value( 'string_test' ) );\n\t}\n\n\tpublic function test_format_value() {\n\t\t$handler = new Papi_Admin_Meta_Handler();\n\n\t\tupdate_term_meta( $this->term_id, papi_get_page_type_key(), 'properties-taxonomy-type' );\n\n\t\t$page_type = papi_get_entry_type_by_id( 'properties-taxonomy-type' );\n\t\t$property  = $page_type->get_property( 'string_test' );\n\n\t\t$_POST = papi_test_create_property_post_data( [\n\t\t\t'slug'  => $property->get_option( 'slug' ),\n\t\t\t'type'  => $property,\n\t\t\t'value' => 'Fredrik'\n\t\t], $_POST );\n\t\t$handler->save_properties( $this->term_id );\n\n\t\t$value = $this->store->load_value( $property->get_option( 'slug' ) );\n\t\t$value = $this->store->format_value( $property->get_option( 'slug' ), $value );\n\t\t$this->assertSame( 'Fredrik', $value );\n\t}\n\n\tpublic function test_get_property() {\n\t\t$store = papi_get_meta_store( $this->term_id, 'term' );\n\t\t$this->assertNull( $store->get_property( 'string_test' ) );\n\t}\n\n\tpublic function test_valid() {\n\t\t$this->assertTrue( $this->store->valid() );\n\t}\n}\n"
  },
  {
    "path": "tests/cases/types/class-papi-attachment-type-test.php",
    "content": "<?php\n\n/**\n * @group types\n */\nclass Papi_Attachment_Type_Test extends WP_UnitTestCase {\n\n\tpublic function setUp() {\n\t\tparent::setUp();\n\n\t\t$_GET = [];\n\n\t\tadd_filter( 'papi/settings/directories', function () {\n\t\t\treturn [1,  PAPI_FIXTURE_DIR . '/page-types'];\n\t\t} );\n\n\t\t$this->attachment_type = papi_get_entry_type_by_id( 'others/attachment-type' );\n\t}\n\n\tpublic function tearDown() {\n\t\tparent::tearDown();\n\t\tunset(\n\t\t\t$_GET,\n\t\t\t$this->attachment_type\n\t\t);\n\t}\n\n\tpublic function test_filters() {\n\t\t$attachment_type = new Papi_Attachment_Type();\n\t\t$attachment_type->boot();\n\t\t$this->assertGreaterThan( 0, has_filter( 'attachment_fields_to_edit', [$attachment_type, 'edit_attachment'] ) );\n\t\t$this->assertGreaterThan( 0, has_filter( 'attachment_fields_to_save', [$attachment_type, 'save_attachment'] ) );\n\t}\n\n\tpublic function test_post_type() {\n\t\t$this->assertSame( 'attachment', $this->attachment_type->post_type[0] );\n\t}\n\n\tpublic function test_get_post_type() {\n\t\t$this->assertSame( 'attachment', $this->attachment_type->get_post_type() );\n\t}\n\n\tpublic function test_meta() {\n\t\t$this->assertSame( 'Attachment', $this->attachment_type->name );\n\t}\n\n\tpublic function test_edit_attachment() {\n\t\t$post_id = $this->factory->post->create();\n\t\t$post = get_post( $post_id );\n\t\t$form_fields = $this->attachment_type->edit_attachment( [], $post );\n\n\t\t$this->assertTrue( isset( $form_fields['papi_name'] ) );\n\t\t$this->assertTrue( isset( $form_fields['papi_post'] ) );\n\t\t$this->assertTrue( isset( $form_fields['papi_text'] ) );\n\t\t$this->assertTrue( isset( $form_fields['papi_meta_nonce'] ) );\n\t}\n\n\tpublic function test_save_attachment() {\n\t\t$post_id = $this->factory->post->create();\n\t\t$post = (array) get_post( $post_id );\n\t\t$property = $this->attachment_type->get_property( 'name' );\n\n\t\t$_POST = papi_test_create_property_post_data( [\n\t\t\t'slug'  => $property->get_option( 'slug' ),\n\t\t\t'type'  => $property,\n\t\t\t'value' => 'Fredrik'\n\t\t], $_POST );\n\t\t$_POST['action'] = 'save-attachment-compat';\n\n\t\t$user_id = $this->factory->user->create( [ 'role' => 'administrator' ] );\n\t\twp_set_current_user( $user_id );\n\n\t\t$_POST['papi_meta_nonce'] = wp_create_nonce( 'papi_save_data' );\n\t\t$_POST['id'] = $post_id;\n\n\t\t$this->assertSame( $post, $this->attachment_type->save_attachment( $post, null ) );\n\t\t$this->assertSame( 'Fredrik', papi_get_field( $post_id, 'name' ) );\n\n\t}\n}\n"
  },
  {
    "path": "tests/cases/types/class-papi-entry-type-test.php",
    "content": "<?php\n\n/**\n * @group types\n */\nclass Papi_Entry_Type_Test extends WP_UnitTestCase {\n\n\tpublic function setUp() {\n\t\tparent::setUp();\n\n\t\tadd_filter( 'papi/settings/directories', function () {\n\t\t\treturn [1,  PAPI_FIXTURE_DIR . '/entry-types'];\n\t\t} );\n\n\t\t$this->post_id = $this->factory->post->create();\n\n\t\tupdate_post_meta( $this->post_id, papi_get_page_type_key(), 'empty-page-type' );\n\n\t\t$this->empty_entry_type  = new Papi_Entry_Type();\n\t\t$this->info_entry_type = papi_get_entry_type( PAPI_FIXTURE_DIR . '/entry-types/info-entry-type.php' );\n\t\t$this->term_entry_type = papi_get_entry_type( PAPI_FIXTURE_DIR . '/entry-types/term-entry-type.php' );\n\t}\n\n\tpublic function tearDown() {\n\t\tparent::tearDown();\n\t\tunset(\n\t\t\t$this->post_id,\n\t\t\t$this->empty_entry_type,\n\t\t\t$this->info_entry_type,\n\t\t\t$this->term_entry_type\n\t\t);\n\t}\n\n\tpublic function test_body_classes() {\n\t\t$this->assertTrue( is_array( $this->empty_entry_type->body_classes() ) );\n\t\t$this->assertEmpty( $this->empty_entry_type->body_classes() );\n\t\t$this->assertSame( ['info-entry-type'], $this->info_entry_type->body_classes() );\n\t}\n\n\tpublic function test_broken_page_type() {\n\t\t$this->assertNull( papi_get_entry_type_by_id( 'broken-entry-type' ) );\n\t}\n\n\tpublic function test_get_body_classes() {\n\t\t$this->assertTrue( is_array( $this->empty_entry_type->get_body_classes() ) );\n\t\t$this->assertEmpty( $this->empty_entry_type->get_body_classes() );\n\t\t$this->assertSame( ['info-entry-type'], $this->info_entry_type->get_body_classes() );\n\t}\n\n\tpublic function test_get_boxes() {\n\t\t$this->assertTrue( is_array( $this->info_entry_type->get_boxes() ) );\n\t\t$this->assertSame( 1, count( $this->info_entry_type->get_boxes() ) );\n\n\t\t$boxes = $this->info_entry_type->get_boxes();\n\n\t\t$this->assertSame( 'Info', $boxes[0]->title );\n\n\t\t$this->assertEmpty( $this->empty_entry_type->get_boxes() );\n\t}\n\n\tpublic function test_get_boxes_abstract() {\n\t\t$abstract_entry_type = papi_get_entry_type( PAPI_FIXTURE_DIR . '/entry-types/abstract-entry-type.php' );\n\t\t$this->assertTrue( is_array( $abstract_entry_type->get_boxes() ) );\n\t\t$boxes = $abstract_entry_type->get_boxes();\n\t\t$this->assertSame( 'Content', $boxes[0]->title );\n\t}\n\n\tpublic function test_get_boxes_filter() {\n\t\t$this->assertTrue( is_array( $this->info_entry_type->get_boxes() ) );\n\n\t\tadd_filter( 'papi/get_boxes', function ( $boxes ) {\n\t\t\t$boxes[] = [\n\t\t\t\t'title' => 'Majs'\n\t\t\t];\n\n\t\t\t$boxes['Hello'] = [];\n\n\t\t\treturn $boxes;\n\t\t} );\n\n\t\t$boxes = $this->info_entry_type->get_boxes();\n\t\t$this->assertSame( 'Majs', $boxes[1]->title );\n\t\t$this->assertSame( 'Hello', $boxes[2]->title );\n\t}\n\n\tpublic function test_get_class_name() {\n\t\t$this->assertEmpty( $this->empty_entry_type->get_class_name() );\n\t\t$this->assertSame( 'Info_Entry_Type', $this->info_entry_type->get_class_name() );\n\t}\n\n\tpublic function test_get_file_path() {\n\t\t$this->assertEmpty( $this->empty_entry_type->get_file_path() );\n\n\t\t$this->assertSame(\n\t\t\tPAPI_FIXTURE_DIR . '/entry-types/info-entry-type.php',\n\t\t\t$this->info_entry_type->get_file_path()\n\t\t);\n\t}\n\n\tpublic function test_get_id() {\n\t\t$this->assertEmpty( $this->empty_entry_type->get_id() );\n\t\t$this->assertSame( 'info-entry-type', $this->info_entry_type->get_id() );\n\t}\n\n\tpublic function test_get_labels() {\n\t\t$entry_type = new Papi_Entry_Type;\n\t\t$this->assertEmpty( $entry_type->get_labels() );\n\t}\n\n\tpublic function test_get_property() {\n\t\t$this->assertNull( $this->empty_entry_type->get_property( 'fake' ) );\n\t\t$this->assertNull( $this->info_entry_type->get_property( 'fake' ) );\n\n\t\t$property = $this->info_entry_type->get_property( 'info' );\n\t\t$this->assertSame( 'string', $property->get_option( 'type' ) );\n\t\t$this->assertSame( 'string', $property->type );\n\t\t$this->assertSame( 'papi_info', $property->slug );\n\t\t$this->assertSame( 'papi_info', $property->get_option( 'slug' ) );\n\t\t$this->assertSame( 'papi_info', $property->get_slug() );\n\t\t$this->assertSame( 'info', $property->get_slug( true ) );\n\t\t$this->assertSame( 'Info', $property->get_option( 'title' ) );\n\t\t$this->assertSame( 'Info', $property->title );\n\t}\n\n\tpublic function test_get_properties() {\n\t\t$this->assertEmpty( $this->empty_entry_type->get_properties() );\n\t\t$this->assertNotEmpty( $this->info_entry_type->get_properties() );\n\t}\n\n\tpublic function test_get_type() {\n\t\t$this->assertSame( 'entry', $this->empty_entry_type->get_type() );\n\t\t$this->assertSame( 'entry', $this->info_entry_type->get_type() );\n\t}\n\n\tpublic function test_help_tabs() {\n\t\t$help = $this->info_entry_type->help();\n\n\t\t$this->assertArrayHasKey( 'Hello 1', $help );\n\t\t$this->assertArrayHasKey( 'Hello 2', $help );\n\n\t\tglobal $current_screen;\n\n\t\t$this->assertNull( $this->info_entry_type->add_help_tabs() );\n\t\t$this->assertNull( $current_screen );\n\n\t    $current_screen = WP_Screen::get( 'admin_init' );\n\n\t    $this->info_entry_type->add_help_tabs();\n\t    $tabs = $current_screen->get_help_tabs();\n\n\t\t$this->assertArrayHasKey( 'papi_hello_1', $tabs );\n\t\t$this->assertArrayHasKey( 'papi_hello_2', $tabs );\n\n\t\t$this->assertSame( '<p>Lorem ipsum</p>', trim( $tabs['papi_hello_1']['content'] ) );\n\t\t$this->assertSame( '<p>Lorem ipsum 2</p>', trim( $tabs['papi_hello_2']['callback']() ) );\n\n\t    $current_screen = null;\n\t}\n\n\tpublic function test_help_tabs_meta_property() {\n\t\tglobal $current_screen;\n\t    $current_screen = WP_Screen::get( 'admin_init' );\n\n\t    $this->term_entry_type->add_help_tabs();\n\t    $tabs = $current_screen->get_help_tabs();\n\n\t    $this->assertEmpty( $tabs );\n\n\t    $current_screen = null;\n\t}\n\n\tpublic function test_match_id() {\n\t\t$this->assertTrue( $this->empty_entry_type->match_id( '' ) );\n\t\t$this->assertTrue( $this->info_entry_type->match_id( 'info-entry-type' ) );\n\t}\n\n\tpublic function test_new_class() {\n\t\t$this->assertEmpty( $this->empty_entry_type->new_class() );\n\t\t$this->assertEquals( new Info_Entry_Type(), $this->info_entry_type->new_class() );\n\t}\n\n\tpublic function test_setup() {\n\t\t$this->assertTrue( apply_filters( 'screen_options_show_screen', true ) );\n\t\t$this->info_entry_type->setup();\n\t\t$this->assertFalse( apply_filters( 'screen_options_show_screen', true ) );\n\t}\n\n\tpublic function test_sort_order() {\n\t\t$this->assertSame( 1000, $this->empty_entry_type->sort_order );\n\t\t$this->assertSame( 500, $this->info_entry_type->sort_order );\n\t}\n}\n"
  },
  {
    "path": "tests/cases/types/class-papi-option-type-test.php",
    "content": "<?php\n\n/**\n * @group types\n */\nclass Papi_Option_Type_Test extends WP_UnitTestCase {\n\n\tpublic function setUp() {\n\t\tparent::setUp();\n\n\t\t$_GET = [];\n\n\t\tadd_filter( 'papi/settings/directories', function () {\n\t\t\treturn [1,  PAPI_FIXTURE_DIR . '/page-types'];\n\t\t} );\n\n\t\t$this->header_option_type = papi_get_entry_type_by_id( 'options/header-option-type' );\n\t\t$this->empty_option_type = new Papi_Option_Type();\n\t}\n\n\tpublic function tearDown() {\n\t\tparent::tearDown();\n\t\tunset(\n\t\t\t$_GET,\n\t\t\t$this->header_option_type\n\t\t);\n\t}\n\n\tpublic function test_get_boxes() {\n\t\t$this->assertEmpty( $this->empty_option_type->get_boxes() );\n\n\t\t$this->assertTrue( is_array( $this->header_option_type->get_boxes() ) );\n\n\t\t$boxes = $this->header_option_type->get_boxes();\n\n\t\t$this->assertSame( 'Options', $boxes[0]->title );\n\t}\n\n\tpublic function test_get_property() {\n\t\t$this->assertNull( $this->header_option_type->get_property( 'fake' ) );\n\n\t\t$property = $this->header_option_type->get_property( 'image' );\n\t\t$this->assertSame( 'image', $property->get_option( 'type' ) );\n\t\t$this->assertSame( 'image', $property->type );\n\t\t$this->assertSame( 'papi_image', $property->slug );\n\t\t$this->assertSame( 'papi_image', $property->get_option( 'slug' ) );\n\t\t$this->assertSame( 'Image', $property->get_option( 'title' ) );\n\t\t$this->assertSame( 'Image', $property->title );\n\n\t\t$property = $this->header_option_type->get_property( 'name' );\n\t\t$this->assertSame( 'string', $property->get_option( 'type' ) );\n\t\t$this->assertSame( 'string', $property->type );\n\t\t$this->assertSame( 'papi_name', $property->slug );\n\t\t$this->assertSame( 'papi_name', $property->get_option( 'slug' ) );\n\t\t$this->assertSame( 'Name', $property->get_option( 'title' ) );\n\t\t$this->assertSame( 'Name', $property->title );\n\n\t\t$property = $this->header_option_type->get_property( 'name_levels_2', 'child_name_2' );\n\t\t$this->assertSame( 'Child name 2', $property->get_option( 'title' ) );\n\t\t$this->assertSame( 'Child name 2', $property->title );\n\t\t$this->assertSame( 'papi_child_name_2', $property->slug );\n\t\t$this->assertSame( 'papi_child_name_2', $property->get_option( 'slug' ) );\n\t\t$this->assertSame( 'string', $property->get_option( 'type' ) );\n\t\t$this->assertSame( 'string', $property->type );\n\t}\n\n\tpublic function test_get_child_properties() {\n\t\t$property = $this->header_option_type->get_property( 'name_levels' );\n\t\t$children1 = $property->get_child_properties();\n\t\t$children2 = $children1[0]->get_child_properties();\n\t\t$this->assertTrue( papi_is_property( $children2[0] ) );\n\t\t$this->assertSame( 'Child child name', $children2[0]->get_option( 'title' ) );\n\t\t$this->assertSame( 'Child child name', $children2[0]->title );\n\t\t$this->assertSame( 'string', $children2[0]->get_option( 'type' ) );\n\t\t$this->assertSame( 'string', $children2[0]->type );\n\t}\n\n\tpublic function test_meta_info() {\n\t\t$this->assertEmpty( $this->empty_option_type->name );\n\t\t$this->assertEmpty( $this->empty_option_type->menu );\n\n\t\t$this->assertSame( 'Header', $this->header_option_type->name );\n\t\t$this->assertSame( 'options-general.php', $this->header_option_type->menu );\n\t\t$this->assertSame( 'This is your header options', $this->header_option_type->description );\n\t}\n\n\tpublic function test_render() {\n\t\t$this->header_option_type->render();\n\t\t$this->expectOutputRegex( '/.*/' );\n\t}\n}\n"
  },
  {
    "path": "tests/cases/types/class-papi-page-type-test.php",
    "content": "<?php\n\n/**\n * @group types\n */\nclass Papi_Page_Type_Test extends WP_UnitTestCase {\n\n\tpublic function setUp() {\n\t\tparent::setUp();\n\n\t\t$_GET = [];\n\n\t\tadd_filter( 'papi/settings/directories', function () {\n\t\t\treturn [1,  PAPI_FIXTURE_DIR . '/page-types'];\n\t\t} );\n\n\t\t$this->post_id = $this->factory->post->create();\n\n\t\tupdate_post_meta( $this->post_id, papi_get_page_type_key(), 'empty-page-type' );\n\n\t\t$this->empty_page_type       = new Papi_Page_Type();\n\t\t$this->big_page_type         = papi_get_entry_type_by_id( 'big-page-type' );\n\t\t$this->display_not_page_type = papi_get_entry_type_by_id( 'display-not-page-type' );\n\t\t$this->faq_page_type         = papi_get_entry_type_by_id( 'faq-page-type' );\n\t\t$this->faq_extra_page_type   = papi_get_entry_type_by_id( 'faq-extra-page-type' );\n\t\t$this->faq_extra2_page_type  = papi_get_entry_type_by_id( 'faq-extra2-page-type' );\n\t\t$this->flex_page_type        = papi_get_entry_type_by_id( 'flex2-page-type' );\n\t\t$this->simple_page_type      = papi_get_entry_type_by_id( 'simple-page-type' );\n\t\t$this->tab_page_type         = papi_get_entry_type_by_id( 'tab-page-type' );\n\t\t$this->properties_page_type  = papi_get_entry_type_by_id( 'properties-page-type' );\n\t}\n\n\tpublic function tearDown() {\n\t\tparent::tearDown();\n\t\tunset(\n\t\t\t$_GET,\n\t\t\t$this->post_id,\n\t\t\t$this->big_page_type,\n\t\t\t$this->empty_page_type,\n\t\t\t$this->display_not_page_type,\n\t\t\t$this->faq_page_type,\n\t\t\t$this->faq_extra_page_type,\n\t\t\t$this->faq_extra2_page_type,\n\t\t\t$this->simple_page_type,\n\t\t\t$this->tab_page_type\n\t\t);\n\t}\n\n\tpublic function test_display() {\n\t\t$this->assertTrue( $this->properties_page_type->display( 'post' ) );\n\t\t$this->assertFalse( $this->flex_page_type->display( 'post' ) );\n\t}\n\n\tpublic function test_get_body_classes() {\n\t\t$this->assertTrue( is_array( $this->empty_page_type->get_body_classes() ) );\n\t\t$this->assertEmpty( $this->empty_page_type->get_body_classes() );\n\t\t$this->assertSame( ['simple-page-type', 'papi-hide-edit-slug-box', 'papi-hide-pageparentdiv'], $this->simple_page_type->get_body_classes() );\n\t}\n\n\tpublic function test_get_boxes() {\n\t\t$this->assertTrue( is_array( $this->flex_page_type->get_boxes() ) );\n\t\t$this->assertTrue( is_array( $this->simple_page_type->get_boxes() ) );\n\n\t\t$boxes = $this->faq_page_type->get_boxes();\n\n\t\t$this->assertSame( 'Content', $boxes[0]->title );\n\t\t$this->assertEmpty( $this->empty_page_type->get_boxes() );\n\t}\n\n\tpublic function test_get_child_types() {\n\t\t$this->assertEmpty( $this->simple_page_type->get_child_types() );\n\t\t$child_types = $this->faq_page_type->get_child_types();\n\t\t$this->assertTrue( is_object( $child_types[0] ) );\n\t}\n\n\tpublic function test_get_labels() {\n\t\t$this->assertEmpty( $this->simple_page_type->get_labels() );\n\t\t$this->assertEmpty( $this->empty_page_type->get_labels() );\n\t\t$this->assertEquals( [\n\t\t\t'add_new_item' => 'Add New FAQ page',\n\t\t\t'edit_item'    => 'Edit FAQ page',\n\t\t\t'view_item'    => 'View FAQ page',\n\t\t\t'nan_item'     => 'Not a number item'\n\t\t], $this->faq_page_type->get_labels() );\n\t}\n\n\tpublic function test_get_thumbnail() {\n\t\t$this->assertSame( '', $this->simple_page_type->get_thumbnail() );\n\t\t$this->assertSame( '', $this->empty_page_type->get_thumbnail() );\n\t\t$this->assertSame( 'faq.png', $this->faq_page_type->get_thumbnail() );\n\t}\n\n\tpublic function test_get_property() {\n\t\t$this->assertNull( $this->empty_page_type->get_property( 'fake' ) );\n\t\t$this->assertNull( $this->simple_page_type->get_property( 'fake' ) );\n\n\t\t$property = $this->simple_page_type->get_property( 'name' );\n\t\t$this->assertSame( 'string', $property->get_option( 'type' ) );\n\t\t$this->assertSame( 'string', $property->type );\n\t\t$this->assertSame( 'papi_name', $property->slug );\n\t\t$this->assertSame( 'papi_name', $property->get_option( 'slug' ) );\n\t\t$this->assertSame( 'papi_name', $property->get_slug() );\n\t\t$this->assertSame( 'name', $property->get_slug( true ) );\n\t\t$this->assertSame( 'Name', $property->get_option( 'title' ) );\n\t\t$this->assertSame( 'Name', $property->title );\n\n\t\t$property = $this->flex_page_type->get_property( 'sections' );\n\t\t$this->assertSame( 'flexible', $property->get_option( 'type' ) );\n\t\t$this->assertSame( 'flexible', $property->type );\n\t\t$this->assertSame( 'papi_sections', $property->slug );\n\t\t$this->assertSame( 'papi_sections', $property->get_option( 'slug' ) );\n\t\t$this->assertSame( 'papi_sections', $property->get_slug() );\n\t\t$this->assertSame( 'sections', $property->get_slug( true ) );\n\t\t$this->assertSame( 'Sections', $property->get_option( 'title' ) );\n\t\t$this->assertSame( 'Sections', $property->title );\n\n\t\t$property = $this->properties_page_type->get_property( 'repeater_test', 'book_name' );\n\t\t$this->assertSame( 'Book name', $property->get_option( 'title' ) );\n\t\t$this->assertSame( 'Book name', $property->title );\n\t\t$this->assertSame( 'papi_book_name', $property->slug );\n\t\t$this->assertSame( 'papi_book_name', $property->get_option( 'slug' ) );\n\t\t$this->assertSame( 'papi_book_name', $property->get_slug() );\n\t\t$this->assertSame( 'book_name', $property->get_slug( true ) );\n\t\t$this->assertSame( 'string', $property->get_option( 'type' ) );\n\t\t$this->assertSame( 'string', $property->type );\n\n\t\t$property = $this->properties_page_type->get_property( 'flexible_test', 'twitter_name' );\n\t\t$this->assertSame( 'Twitter name', $property->get_option( 'title' ) );\n\t\t$this->assertSame( 'Twitter name', $property->title );\n\t\t$this->assertSame( 'papi_twitter_name', $property->slug );\n\t\t$this->assertSame( 'papi_twitter_name', $property->get_option( 'slug' ) );\n\t\t$this->assertSame( 'papi_twitter_name', $property->get_slug() );\n\t\t$this->assertSame( 'twitter_name', $property->get_slug( true ) );\n\t\t$this->assertSame( 'string', $property->get_option( 'type' ) );\n\t\t$this->assertSame( 'string', $property->type );\n\n\t\t$property = $this->simple_page_type->get_property( 'name_levels', 'child_name' );\n\t\t$this->assertSame( 'Child name', $property->get_option( 'title' ) );\n\t\t$this->assertSame( 'Child name', $property->title );\n\t\t$this->assertSame( 'papi_child_name', $property->slug );\n\t\t$this->assertSame( 'papi_child_name', $property->get_option( 'slug' ) );\n\t\t$this->assertSame( 'papi_child_name', $property->get_slug() );\n\t\t$this->assertSame( 'child_name', $property->get_slug( true ) );\n\t\t$this->assertSame( 'string', $property->get_option( 'type' ) );\n\t\t$this->assertSame( 'string', $property->type );\n\n\t\t$property = $this->simple_page_type->get_property( 'name_levels_2', 'child_name_2' );\n\t\t$this->assertSame( 'Child name 2', $property->get_option( 'title' ) );\n\t\t$this->assertSame( 'Child name 2', $property->title );\n\t\t$this->assertSame( 'papi_child_name_2', $property->slug );\n\t\t$this->assertSame( 'papi_child_name_2', $property->get_option( 'slug' ) );\n\t\t$this->assertSame( 'papi_child_name_2', $property->get_slug() );\n\t\t$this->assertSame( 'child_name_2', $property->get_slug( true ) );\n\t\t$this->assertSame( 'string', $property->get_option( 'type' ) );\n\t\t$this->assertSame( 'string', $property->type );\n\n\t\t$property = $this->tab_page_type->get_property( 'name_levels_2', 'child_name_2' );\n\t\t$this->assertSame( 'Child name 2', $property->get_option( 'title' ) );\n\t\t$this->assertSame( 'Child name 2', $property->title );\n\t\t$this->assertSame( 'papi_child_name_2', $property->slug );\n\t\t$this->assertSame( 'papi_child_name_2', $property->get_option( 'slug' ) );\n\t\t$this->assertSame( 'papi_child_name_2', $property->get_slug() );\n\t\t$this->assertSame( 'child_name_2', $property->get_slug( true ) );\n\t\t$this->assertSame( 'string', $property->get_option( 'type' ) );\n\t\t$this->assertSame( 'string', $property->type );\n\n\t\t$property = $this->simple_page_type->get_property( 'sections[0][title]' );\n\t\t$this->assertSame( 'string', $property->get_option( 'type' ) );\n\t\t$this->assertSame( 'string', $property->type );\n\t\t$this->assertSame( 'papi_title', $property->slug );\n\t\t$this->assertSame( 'papi_title', $property->get_option( 'slug' ) );\n\t\t$this->assertSame( 'papi_title', $property->get_slug() );\n\t\t$this->assertSame( 'title', $property->get_slug( true ) );\n\t\t$this->assertSame( 'Title', $property->get_option( 'title' ) );\n\t\t$this->assertSame( 'Title', $property->title );\n\t}\n\n\tpublic function test_get_child_properties() {\n\t\t$property = $this->simple_page_type->get_property( 'name_levels' );\n\t\t$children1 = $property->get_child_properties();\n\t\t$children2 = $children1[0]->get_child_properties();\n\t\t$this->assertTrue( papi_is_property( $children2[0] ) );\n\t\t$this->assertSame( 'Child child name', $children2[0]->get_option( 'title' ) );\n\t\t$this->assertSame( 'Child child name', $children2[0]->title );\n\t\t$this->assertSame( 'papi_child_child_name', $children2[0]->slug );\n\t\t$this->assertSame( 'papi_child_child_name', $children2[0]->get_option( 'slug' ) );\n\t\t$this->assertSame( 'papi_child_child_name', $children2[0]->get_slug() );\n\t\t$this->assertSame( 'child_child_name', $children2[0]->get_slug( true ) );\n\t\t$this->assertSame( 'string', $children2[0]->get_option( 'type' ) );\n\t\t$this->assertSame( 'string', $children2[0]->type );\n\t}\n\n\tpublic function test_has_post_type() {\n\t\t$this->assertTrue( $this->simple_page_type->has_post_type( 'page' ) );\n\t\t$this->assertTrue( $this->empty_page_type->has_post_type( 'page' ) );\n\t\t$this->assertTrue( $this->faq_page_type->has_post_type( 'faq' ) );\n\t}\n\n\tpublic function test_meta_info() {\n\t\t$this->assertSame( [], $this->empty_page_type->capabilities );\n\t\t$this->assertSame( [], $this->empty_page_type->child_types );\n\t\t$this->assertSame( '', $this->empty_page_type->description );\n\t\t$this->assertFalse( $this->empty_page_type->fill_labels );\n\t\t$this->assertSame( '', $this->empty_page_type->name );\n\t\t$this->assertSame( 'page', $this->empty_page_type->post_type[0] );\n\t\t$this->assertSame( 1000, $this->empty_page_type->sort_order );\n\t\t$this->assertFalse( $this->empty_page_type->standard_type );\n\t\t$this->assertSame( '', $this->empty_page_type->template );\n\t\t$this->assertSame( '', $this->empty_page_type->thumbnail );\n\n\t\t$this->assertSame( ['kvack'], $this->display_not_page_type->capabilities );\n\t\t$this->assertSame( [], $this->display_not_page_type->child_types );\n\t\t$this->assertSame( 'This is a display not page', $this->display_not_page_type->description );\n\t\t$this->assertFalse( $this->display_not_page_type->fill_labels );\n\t\t$this->assertSame( 'Display not page', $this->display_not_page_type->name );\n\t\t$this->assertSame( 'page', $this->display_not_page_type->post_type[0] );\n\t\t$this->assertSame( 1000, $this->display_not_page_type->sort_order );\n\t\t$this->assertFalse( $this->display_not_page_type->standard_type );\n\t\t$this->assertSame( 'pages/display-not-page.php', $this->display_not_page_type->template );\n\t\t$this->assertSame( '', $this->display_not_page_type->thumbnail );\n\n\t\t$this->assertSame( [], $this->faq_page_type->capabilities );\n\t\t$this->assertSame( ['simple-page-type', null, 'fake'], $this->faq_page_type->child_types );\n\t\t$this->assertSame( 'This is a faq page', $this->faq_page_type->description );\n\t\t$this->assertTrue( $this->faq_page_type->fill_labels );\n\t\t$this->assertSame( 'FAQ page', $this->faq_page_type->name );\n\t\t$this->assertSame( 'faq', $this->faq_page_type->post_type[0] );\n\t\t$this->assertSame( 1000, $this->faq_page_type->sort_order );\n\t\t$this->assertFalse( $this->faq_page_type->standard_type );\n\t\t$this->assertSame( 'pages/faq-page.php', $this->faq_page_type->template );\n\t\t$this->assertSame( 'faq.png', $this->faq_page_type->thumbnail );\n\n\t\t$this->assertSame( [], $this->simple_page_type->capabilities );\n\t\t$this->assertSame( [], $this->simple_page_type->child_types );\n\t\t$this->assertSame( 'This is a simple page', $this->simple_page_type->description );\n\t\t$this->assertFalse( $this->simple_page_type->fill_labels );\n\t\t$this->assertSame( 'Simple page', $this->simple_page_type->name );\n\t\t$this->assertSame( 'page', $this->simple_page_type->post_type[0] );\n\t\t$this->assertSame( 1000, $this->simple_page_type->sort_order );\n\t\t$this->assertTrue( $this->simple_page_type->standard_type );\n\t\t$this->assertSame( 'pages/simple-page.php', $this->simple_page_type->template );\n\t\t$this->assertSame( '', $this->simple_page_type->thumbnail );\n\t}\n\n\tpublic function test_parent_boxes() {\n\t\t// FAQ 1\n\t\t$property = $this->faq_page_type->get_property( 'question' );\n\t\t$this->assertSame( 'string', $property->get_option( 'type' ) );\n\t\t$this->assertSame( 'string', $property->type );\n\t\t$this->assertSame( 'papi_question', $property->slug );\n\t\t$this->assertSame( 'papi_question', $property->get_option( 'slug' ) );\n\t\t$this->assertSame( 'papi_question', $property->get_slug() );\n\t\t$this->assertSame( 'question', $property->get_slug( true ) );\n\t\t$this->assertSame( 'Question', $property->get_option( 'title' ) );\n\t\t$this->assertSame( 'Question', $property->title );\n\n\t\t// FAQ 2\n\t\t$property = $this->faq_extra_page_type->get_property( 'question' );\n\t\t$this->assertSame( 'string', $property->get_option( 'type' ) );\n\t\t$this->assertSame( 'string', $property->type );\n\t\t$this->assertSame( 'papi_question', $property->slug );\n\t\t$this->assertSame( 'papi_question', $property->get_option( 'slug' ) );\n\t\t$this->assertSame( 'papi_question', $property->get_slug() );\n\t\t$this->assertSame( 'question', $property->get_slug( true ) );\n\t\t$this->assertSame( 'Question', $property->get_option( 'title' ) );\n\t\t$this->assertSame( 'Question', $property->title );\n\n\t\t// FAQ 3\n\t\t$property = $this->faq_extra2_page_type->get_property( 'question' );\n\t\t$this->assertSame( 'string', $property->get_option( 'type' ) );\n\t\t$this->assertSame( 'string', $property->type );\n\t\t$this->assertSame( 'papi_question', $property->slug );\n\t\t$this->assertSame( 'papi_question', $property->get_option( 'slug' ) );\n\t\t$this->assertSame( 'papi_question', $property->get_slug() );\n\t\t$this->assertSame( 'question', $property->get_slug( true ) );\n\t\t$this->assertSame( 'Question', $property->get_option( 'title' ) );\n\t\t$this->assertSame( 'Question', $property->title );\n\t}\n\n\tpublic function test_publish_box() {\n\t\t$this->simple_page_type->setup();\n\t\t$this->assertFalse( has_action( 'post_submitbox_misc_actions', [$this->simple_page_type, 'publish_box'] ) );\n\n\t\t$this->faq_page_type->setup();\n\t\t$this->assertSame( 10, has_action( 'post_submitbox_misc_actions', [$this->faq_page_type, 'publish_box'] ) );\n\t}\n\n\tpublic function test_remove_post_type_supports() {\n\t\tglobal $_wp_post_type_features;\n\t\t$this->assertNull( $this->simple_page_type->remove_post_type_support() );\n\t\t$_GET['post_type'] = 'page';\n\t\t$_wp_post_type_features['page']['editor'] = true;\n\t\t$this->simple_page_type->remove_post_type_support();\n\t\t$this->assertFalse( isset( $_wp_post_type_features['page']['editor'] ) );\n\t}\n\n\tpublic function test_remove_post_type_supports_faq_level_1() {\n\t\tglobal $_wp_post_type_features;\n\t\t$this->assertNull( $this->faq_page_type->remove_post_type_support() );\n\t\t$_GET['post_type'] = 'faq';\n\t\t$_wp_post_type_features['faq']['div'] = true;\n\t\t$this->faq_page_type->remove_post_type_support();\n\t\t$this->assertFalse( isset( $_wp_post_type_features['faq']['div'] ) );\n\t}\n\n\tpublic function test_remove_post_type_supports_faq_level_2() {\n\t\tglobal $_wp_post_type_features;\n\t\t$this->assertNull( $this->faq_extra_page_type->remove_post_type_support() );\n\t\t$_GET['post_type'] = 'faq';\n\t\t$_wp_post_type_features['faq']['div'] = true;\n\t\t$_wp_post_type_features['faq']['blog'] = true;\n\t\t$this->faq_extra_page_type->remove_post_type_support();\n\t\t$this->assertFalse( isset( $_wp_post_type_features['faq']['div'] ) );\n\t\t$this->assertFalse( isset( $_wp_post_type_features['faq']['blog'] ) );\n\t}\n\n\tpublic function test_remove_post_type_supports_faq_level_3() {\n\t\tglobal $_wp_post_type_features;\n\t\t$this->assertNull( $this->faq_extra2_page_type->remove_post_type_support() );\n\t\t$_GET['post_type'] = 'faq';\n\t\t$_wp_post_type_features['faq']['div'] = true;\n\t\t$_wp_post_type_features['faq']['blog'] = true;\n\t\t$_wp_post_type_features['faq']['editor'] = true;\n\t\t$this->faq_extra2_page_type->remove_post_type_support();\n\t\t$this->assertFalse( isset( $_wp_post_type_features['faq']['div'] ) );\n\t\t$this->assertFalse( isset( $_wp_post_type_features['faq']['blog'] ) );\n\t\t$this->assertFalse( isset( $_wp_post_type_features['faq']['editor'] ) );\n\t}\n\n\tpublic function test_remove_meta_boxes() {\n\t\tglobal $wp_meta_boxes, $current_screen;\n\n\t\t$_GET['post_type'] = 'faq';\n\t\t$current_screen = WP_Screen::get( 'admin_init' );\n\t\t$wp_meta_boxes['faq']['normal']['default']['test_meta_box'] = true;\n\n\t\t$this->assertTrue( $wp_meta_boxes['faq']['normal']['default']['test_meta_box'] );\n\n\t\t$this->faq_page_type->remove_post_type_support();\n\t\tdo_action( 'add_meta_boxes' );\n\n\t\t$this->assertFalse( $wp_meta_boxes['faq']['normal']['default']['test_meta_box'] );\n\t}\n\n\tpublic function test_remove_all_meta_boxes() {\n\t\tglobal $wp_meta_boxes, $current_screen;\n\n\t\t$_GET['post_type'] = 'page';\n\t\t$current_screen = WP_Screen::get( 'admin_init' );\n\t\t$wp_meta_boxes['page']['normal']['default']['test_meta_box'] = true;\n\t\t$wp_meta_boxes['page']['normal']['default']['_papi_content'] = true;\n\t\t$this->assertTrue( $wp_meta_boxes['page']['normal']['default']['test_meta_box'] );\n\n\t\t$this->big_page_type->remove_post_type_support();\n\t\tdo_action( 'add_meta_boxes' );\n\n\t\t$this->assertFalse( $wp_meta_boxes['page']['normal']['default']['test_meta_box'] );\n\t\t$this->assertArrayHasKey( '_papi_content', $wp_meta_boxes['page']['normal']['default'] );\n\t}\n\n\tpublic function test_setup_page_templates() {\n\t\t$_GET['post_type'] = 'page';\n\n\t\t$page_templates = get_page_templates();\n\t\t$this->assertEmpty( $page_templates );\n\n\t\t$path = __DIR__ . '/../../data/page-types/name-page-type.php';\n\t\trequire_once $path;\n\n\t\tnew Name_Page_Type( $path );\n\t\t$page_templates = wp_get_theme()->get_page_templates();\n\n\t\t$this->assertNotEmpty( $page_templates );\n\t\t$this->assertTrue( isset( $page_templates['layout-a.php'] ) );\n\t}\n\n\tpublic function test_tabs_meta_boxes() {\n\t\t$boxes = $this->tab_page_type->get_boxes();\n\n\t\t// Box 1\n\t\t$this->assertInstanceOf( 'Papi_Core_Tab', $boxes[0]->properties[0] );\n\t\t$this->assertInstanceOf( 'Papi_Core_Tab', $boxes[0]->properties[1] );\n\n\t\t// Box 2.\n\t\t$this->assertFalse( $boxes[1]->properties[0] instanceof Papi_Core_Tab );\n\t\t$this->assertInstanceOf( 'Papi_Core_Property', $boxes[1]->properties[0] );\n\n\t\t// Box 3.\n\t\t$this->assertEmpty( $boxes[2]->properties );\n\n\t\t// Box 4.\n\t\t$this->assertFalse( $boxes[3]->properties[0] instanceof Papi_Core_Tab );\n\t\t$this->assertInstanceOf( 'Papi_Core_Property', $boxes[3]->properties[0] );\n\n\t\t// Box 5.\n\t\t$this->assertInstanceOf( 'Papi_Core_Tab', $boxes[4]->properties[0] );\n\t}\n\n\tpublic function test_any_post_type() {\n\t\trequire_once PAPI_FIXTURE_DIR . '/page-types2/any-page-type.php';\n\t\t$page_type = new Any_Page_Type( PAPI_FIXTURE_DIR . '/page-types2/any-page-type.php' );\n\t\t$this->assertSame( count( $page_type->post_type ), count( get_post_types( '', 'names' ) ) );\n\t}\n}\n"
  },
  {
    "path": "tests/cases/types/class-papi-taxonomy-type-test.php",
    "content": "<?php\n\n/**\n * @group types\n */\nclass Papi_Taxonomy_Type_Test extends WP_UnitTestCase {\n\n\tpublic function setUp() {\n\t\tparent::setUp();\n\n\t\tadd_filter( 'papi/settings/directories', function () {\n\t\t\treturn [1,  PAPI_FIXTURE_DIR . '/taxonomy-types'];\n\t\t} );\n\n\t\t$this->term_id = $this->factory->term->create();\n\n\t\t$this->empty_taxonomy_type      = new Papi_Taxonomy_Type();\n\t\t$this->properties_taxonomy_type = papi_get_entry_type_by_id( 'properties-taxonomy-type' );\n\t\t$this->simple_taxonomy_type     = papi_get_entry_type_by_id( 'simple-taxonomy-type' );\n\t}\n\n\tpublic function tearDown() {\n\t\tparent::tearDown();\n\t\tunset(\n\t\t\t$this->post_id,\n\t\t\t$this->empty_taxonomy_type,\n\t\t\t$this->properties_taxonomy_type,\n\t\t\t$this->simple_taxonomy_type\n\t\t);\n\t}\n\n\tpublic function test_display() {\n\t\t$this->assertTrue( $this->properties_taxonomy_type->display( 'post_tag' ) );\n\t\t$this->assertFalse( $this->simple_taxonomy_type->display( 'test_taxonomy' ) );\n\t}\n\n\tpublic function test_edit_form() {\n\t\t$_GET['taxonomy'] = 'post_tag';\n\t\t$_GET['post'] = 'post';\n\t\t$this->simple_taxonomy_type->setup();\n\t\t$this->simple_taxonomy_type->edit_form();\n\t\t$this->expectOutputRegex( '/name\\=\\\"papi\\_name\\\"/' );\n\t}\n\n\tpublic function test_get_boxes() {\n\t\t$this->assertTrue( is_array( $this->properties_taxonomy_type->get_boxes() ) );\n\t\t$this->assertTrue( is_array( $this->simple_taxonomy_type->get_boxes() ) );\n\n\t\t$boxes = $this->simple_taxonomy_type->get_boxes();\n\n\t\t$this->assertSame( 'Content', $boxes[0]->title );\n\t\t$this->assertEmpty( $this->empty_taxonomy_type->get_boxes() );\n\t}\n\n\tpublic function test_meta_data() {\n\t\t$this->assertEmpty( $this->empty_taxonomy_type->taxonomy );\n\t\t$this->assertSame( ['post_tag'], $this->properties_taxonomy_type->taxonomy );\n\t}\n\n\tpublic function test_get_labels() {\n\t\t$this->assertEmpty( $this->properties_taxonomy_type->get_labels() );\n\t\t$this->assertSame( [\n\t\t\t'name'          => 'Simple taxonomy',\n\t\t\t'singular_name' => 'Simple taxonomy',\n\t\t\t'add_new_item'  => 'Add New Simple taxonomy',\n\t\t\t'edit_item'     => 'Edit Simple taxonomy',\n\t\t\t'view_item'     => 'View Simple taxonomy'\n\t\t], $this->simple_taxonomy_type->get_labels() );\n\t}\n\n\tpublic function test_get_property() {\n\t\t$this->assertNull( $this->empty_taxonomy_type->get_property( 'fake' ) );\n\t\t$this->assertNull( $this->simple_taxonomy_type->get_property( 'fake' ) );\n\n\t\t$property = $this->properties_taxonomy_type->get_property( 'repeater_test', 'book_name' );\n\t\t$this->assertSame( 'Book name', $property->get_option( 'title' ) );\n\t\t$this->assertSame( 'Book name', $property->title );\n\t\t$this->assertSame( 'papi_book_name', $property->slug );\n\t\t$this->assertSame( 'papi_book_name', $property->get_option( 'slug' ) );\n\t\t$this->assertSame( 'papi_book_name', $property->get_slug() );\n\t\t$this->assertSame( 'book_name', $property->get_slug( true ) );\n\t\t$this->assertSame( 'string', $property->get_option( 'type' ) );\n\t\t$this->assertSame( 'string', $property->type );\n\n\t\t$property = $this->properties_taxonomy_type->get_property( 'flexible_test', 'twitter_name' );\n\t\t$this->assertSame( 'Twitter name', $property->get_option( 'title' ) );\n\t\t$this->assertSame( 'Twitter name', $property->title );\n\t\t$this->assertSame( 'papi_twitter_name', $property->slug );\n\t\t$this->assertSame( 'papi_twitter_name', $property->get_option( 'slug' ) );\n\t\t$this->assertSame( 'papi_twitter_name', $property->get_slug() );\n\t\t$this->assertSame( 'twitter_name', $property->get_slug( true ) );\n\t\t$this->assertSame( 'string', $property->get_option( 'type' ) );\n\t\t$this->assertSame( 'string', $property->type );\n\t}\n}\n"
  },
  {
    "path": "tests/data/classes/class-say.php",
    "content": "<?php\n\nclass Say {\n\tpublic function hello( $name ) {\n\t\treturn 'Hello ' . $name . '!';\n\t}\n}\n"
  },
  {
    "path": "tests/data/container/class-container-test-stub.php",
    "content": "<?php\n\nnamespace Papi\\Tests\\Fixtures\\Container;\n\nclass Container_Test_Stub {\n\tpublic function value() {\n\t\treturn 'Test class';\n\t}\n}\n"
  },
  {
    "path": "tests/data/core-types/abstract-core-type.php",
    "content": "<?php\n\nif ( ! class_exists( 'Base_Core_Type' ) ) {\n\trequire_once __DIR__ . '/base-core-type.php';\n}\n\nclass Abstract_Core_Type extends Base_Core_Type {\n\n\t/**\n\t * Define our Core Type meta data.\n\t *\n\t * @return array\n\t */\n\tpublic function meta() {\n\t\treturn [\n\t\t\t'name'        => 'Abstract core',\n\t\t\t'description' => 'This is a abstract core'\n\t\t];\n\t}\n}\n"
  },
  {
    "path": "tests/data/core-types/base-core-type.php",
    "content": "<?php\n\nabstract class Base_Core_Type extends Papi_Core_Type {\n\tabstract public function meta();\n}\n"
  },
  {
    "path": "tests/data/core-types/broken-core-type.php",
    "content": "<?php\n\nclass Book_Core_Type extends Papi_Core_Type {\n\n\t/**\n\t * Define our Core Type meta data.\n\t *\n\t * @return array\n\t */\n\tpublic function meta() {\n\t\treturn [\n\t\t\t'name' => 'Book core type'\n\t\t];\n\t}\n\n\t/**\n\t * Define our properties.\n\t */\n\tpublic function register() {\n\t\t$this->box( 'Info', papi_property( [\n\t\t\t'type'  => 'string',\n\t\t\t'title' => 'Book name'\n\t\t] ) );\n\t}\n}\n"
  },
  {
    "path": "tests/data/core-types/info-core-type.php",
    "content": "<?php\n\nclass Info_Core_Type extends Papi_Core_Type {\n\n\t/**\n\t * Define our Core Type meta data.\n\t *\n\t * @return array\n\t */\n\tpublic function meta() {\n\t\treturn [\n\t\t\t'name'        => 'Info core type',\n\t\t\t'description' => 'This is a Info core type',\n\t\t\t'sort_order'  => 500\n\t\t];\n\t}\n\n}\n"
  },
  {
    "path": "tests/data/core-types/info2-core-type.php",
    "content": "<?php\n\nif ( ! class_exists( 'Info_Core_Type' ) ) {\n\trequire_once __DIR__ . '/info-core-type.php';\n}\n\nclass Info2_Core_Type extends Info_Core_Type {\n\n\t/**\n\t * Define our Core Type meta data.\n\t *\n\t * @return array\n\t */\n\tpublic function meta() {\n\t\treturn [\n\t\t\t'name'        => 'Info2 core type',\n\t\t\t'description' => 'This is a Info2 core type'\n\t\t];\n\t}\n\n}\n"
  },
  {
    "path": "tests/data/entry-types/abstract-entry-type.php",
    "content": "<?php\n\nif ( ! class_exists( 'Base_Entry_Type' ) ) {\n\trequire_once __DIR__ . '/base-entry-type.php';\n}\n\nclass Abstract_Entry_Type extends Base_Entry_Type {\n\n\t/**\n\t * Define our Entry Type meta data.\n\t *\n\t * @return array\n\t */\n\tpublic function meta() {\n\t\treturn [\n\t\t\t'name'        => 'Abstract entry',\n\t\t\t'description' => 'This is a abstract entry'\n\t\t];\n\t}\n\n\t/**\n\t * Define our properties.\n\t */\n\tpublic function register() {\n\t\t$this->register_content_box();\n\t}\n}\n"
  },
  {
    "path": "tests/data/entry-types/base-entry-type.php",
    "content": "<?php\n\nabstract class Base_Entry_Type extends Papi_Entry_Type {\n\tabstract public function meta();\n\tabstract public function register();\n\n\tpublic function register_content_box() {\n\t\t$this->box( 'Content', [\n\t\t\tpapi_property( [\n\t\t\t\t'title' => 'Name',\n\t\t\t\t'type'  => 'string'\n\t\t\t] )\n\t\t] );\n\t}\n}\n"
  },
  {
    "path": "tests/data/entry-types/broken-entry-type.php",
    "content": "<?php\n\nclass Book_Entry_Type extends Papi_Entry_Type {\n\n\t/**\n\t * Define our entry type meta data.\n\t *\n\t * @return array\n\t */\n\tpublic function meta2() {\n\t\treturn [\n\t\t\t'name' => 'Book entry type'\n\t\t];\n\t}\n\n\t/**\n\t * Define our properties.\n\t */\n\tpublic function register() {\n\t\t$this->box( 'Info', papi_property( [\n\t\t\t'type'  => 'string',\n\t\t\t'title' => 'Book name'\n\t\t] ) );\n\t}\n}\n"
  },
  {
    "path": "tests/data/entry-types/info-entry-type.php",
    "content": "<?php\n\nclass Info_Entry_Type extends Papi_Entry_Type {\n\n\t/**\n\t * Define our entry type meta data.\n\t *\n\t * @return array\n\t */\n\tpublic function meta() {\n\t\treturn [\n\t\t\t'name'                => 'Info entry type',\n\t\t\t'description'         => 'This is a Info entry type',\n\t\t\t'sort_order'          => 500,\n\t\t\t'show_screen_options' => false\n\t\t];\n\t}\n\n\t/**\n\t * Add custom body classes.\n\t *\n\t * @return array\n\t */\n\tpublic function body_classes() {\n\t\treturn ['info-entry-type'];\n\t}\n\n\t/**\n\t * Add help tabs.\n\t *\n\t * @return array\n\t */\n\tpublic function help() {\n\t\treturn [\n\t\t\t'Hello 1' => 'Lorem ipsum',\n\t\t\t'Hello 2' => function () {\n\t\t\t\treturn 'Lorem ipsum 2';\n\t\t\t}\n\t\t];\n\t}\n\n\t/**\n\t * Define our properties.\n\t */\n\tpublic function register() {\n\t\t$this->box( 'Info', papi_property( [\n\t\t\t'type'  => 'string',\n\t\t\t'title' => 'Info'\n\t\t] ) );\n\n\t\t$this->box( ['title' => 'Site 2', 'site_id' => 2], papi_property( [\n\t\t\t'type'  => 'string',\n\t\t\t'title' => 'Name'\n\t\t] ) );\n\t}\n}\n"
  },
  {
    "path": "tests/data/entry-types/term-entry-type.php",
    "content": "<?php\n\nclass Term_Entry_Type extends Papi_Entry_Type {\n\n\t/**\n\t * Define our Entry Type meta data.\n\t *\n\t * @return array\n\t */\n\tpublic function meta() {\n\t\treturn [\n\t\t\t'name'           => 'Term test entry 1',\n\t\t\t'description'    => 'Entry type to test out the term property',\n\t\t\t'show_help_tabs' => false\n\t\t];\n\t}\n}\n"
  },
  {
    "path": "tests/data/entry-types2/term-entry-type.php",
    "content": "<?php\n\nclass Term_Entry_Type extends Papi_Entry_Type {\n\n\t/**\n\t * Define our Entry Type meta data.\n\t *\n\t * @return array\n\t */\n\tpublic function meta() {\n\t\treturn [\n\t\t\t'name'        => 'Term test entry 2',\n\t\t\t'description' => 'Entry type to test out the term property'\n\t\t];\n\t}\n}\n"
  },
  {
    "path": "tests/data/page-types/big-page-type.php",
    "content": "<?php\n\nclass Big_Page_Type extends Papi_Page_Type {\n\n\t/**\n\t * Define our Page Type meta data.\n\t *\n\t * @return array\n\t */\n\tpublic function meta() {\n\t\treturn [\n\t\t\t'name'        => 'Big page',\n\t\t\t'description' => 'This is a big page',\n\t\t\t'template'    => 'pages/big-page.php',\n\t\t\t'thumbnail'   => PAPI_PLUGIN_URL . '../tests/data/img/logo.png',\n\t\t];\n\t}\n\n\t/**\n\t * Remove post type support and `remove_meta_box`.\n\t *\n\t * @return array\n\t */\n\tpublic function remove() {\n\t\treturn ['all'];\n\t}\n\n\t/**\n\t * Define our properties.\n\t */\n\tpublic function register() {\n\t\t$this->box( [\n\t\t\t'title'      => 'Content',\n\t\t\t'properties' => [\n\t\t\t\tpapi_property( [\n\t\t\t\t\t'type'  => 'string',\n\t\t\t\t\t'title' => 'Book Name',\n\t\t\t\t\t'slug'  => 'book_name',\n\t\t\t\t] ),\n\t\t\t\tpapi_property( [\n\t\t\t\t\t'type'  => 'image',\n\t\t\t\t\t'title' => 'Book Image',\n\t\t\t\t\t'slug'  => 'book_image'\n\t\t\t\t] ),\n\t\t\t]\n\t\t] );\n\t}\n}\n"
  },
  {
    "path": "tests/data/page-types/book-page-type.php",
    "content": "<?php\n\nclass Book_Page_Type extends Papi_Page_Type {\n\n\t/**\n\t * Define our Page Type meta data.\n\t *\n\t * @return array\n\t */\n\tpublic function meta() {\n\t\treturn [\n\t\t\t'name'               => 'Book page',\n\t\t\t'description'        => 'This is a book page',\n\t\t\t'template'           => 'pages/book-page.php',\n\t\t\t'standard_type'      => false,\n\t\t\t'post_type'          => 'book'\n\t\t];\n\t}\n\n\t/**\n\t * Define our properties.\n\t */\n\tpublic function register() {\n\t\t$this->box( 'Info', papi_property( [\n\t\t\t'type'  => 'string',\n\t\t\t'title' => 'Book name'\n\t\t] ) );\n\t}\n}\n"
  },
  {
    "path": "tests/data/page-types/boxes/big.php",
    "content": "<?php\n\nreturn [\n\t'title'      => 'Content',\n\t'properties' => [\n\t\tpapi_property( [\n\t\t\t'type'  => 'string',\n\t\t\t'title' => 'Name'\n\t\t] ),\n\t],\n];\n"
  },
  {
    "path": "tests/data/page-types/boxes/properties.php",
    "content": "<?php\n\nreturn [\n\t'title' => 'Properties',\n\n\t// Bool\n\tpapi_property( [\n\t\t'type'  => 'bool',\n\t\t'title' => 'Bool test',\n\t\t'slug'  => 'bool_test'\n\t] ),\n\n\t// Checkbox\n\tpapi_property( [\n\t\t'type'     => 'checkbox',\n\t\t'title'    => 'Checkbox test',\n\t\t'slug'     => 'checkbox_test',\n\t\t'settings' => [\n\t\t\t'items' => [\n\t\t\t\t'White' => '#ffffff',\n\t\t\t\t'Black' => '#000000'\n\t\t\t]\n\t\t]\n\t] ),\n\n\t// Color\n\tpapi_property( [\n\t\t'type'  => 'color',\n\t\t'title' => 'Color test',\n\t\t'slug'  => 'color_test'\n\t] ),\n\n\t// Datetime\n\tpapi_property( [\n\t\t'type'  => 'datetime',\n\t\t'title' => 'Datetime test',\n\t\t'slug'  => 'datetime_test'\n\t] ),\n\n\t// Divider\n\tpapi_property( [\n\t\t'description' => 'Some information here',\n\t\t'type'        => 'divider',\n\t\t'title'       => 'Divider test',\n\t\t'slug'        => 'divider_test'\n\t] ),\n\n\t// Dropdown\n\tpapi_property( [\n\t\t'type'     => 'dropdown',\n\t\t'title'    => 'Dropdown test',\n\t\t'slug'     => 'dropdown_test',\n\t\t'settings' => [\n\t\t\t'items' => [\n\t\t\t\t'White' => '#ffffff',\n\t\t\t\t'Black' => '#000000'\n\t\t\t]\n\t\t]\n\t] ),\n\n\t// Dropdown 2\n\tpapi_property( [\n\t\t'type'     => 'dropdown',\n\t\t'title'    => 'Dropdown test 2',\n\t\t'slug'     => 'dropdown_test_2',\n\t\t'settings' => [\n\t\t\t'placeholder'   => 'Pick one',\n\t\t\t'items'         => [\n\t\t\t\t'White' => '#ffffff',\n\t\t\t\t'Black' => '#000000'\n\t\t\t]\n\t\t]\n\t] ),\n\n\t// Dropdown 3\n\tpapi_property( [\n\t\t'type'     => 'dropdown',\n\t\t'title'    => 'Dropdown test 3',\n\t\t'slug'     => 'dropdown_test_3',\n\t\t'settings' => [\n\t\t\t'multiple'      => true,\n\t\t\t'placeholder'   => 'Pick multiple',\n\t\t\t'items'         => [\n\t\t\t\t0,\n\t\t\t\t1,\n\t\t\t\t2,\n\t\t\t\t3,\n\t\t\t\t4,\n\t\t\t\t5\n\t\t\t]\n\t\t]\n\t] ),\n\n\t// Dropdown 4\n\tpapi_property( [\n\t\t'type'     => 'dropdown',\n\t\t'title'    => 'Dropdown test_4',\n\t\t'slug'     => 'dropdown_test_4',\n\t\t'settings' => [\n\t\t\t'items' => [\n\t\t\t\t'White' => 1,\n\t\t\t\t'Black' => 2\n\t\t\t]\n\t\t]\n\t] ),\n\n\t// Editor\n\tpapi_property( [\n\t\t'type'  => 'editor',\n\t\t'title' => 'Editor test',\n\t\t'slug'  => 'editor_test'\n\t] ),\n\n\t// Editor\n\tpapi_property( [\n\t\t'type'  => 'editor',\n\t\t'title' => 'Editor2 test',\n\t\t'slug'  => 'editor2_test',\n\t] ),\n\n\t// Email\n\tpapi_property( [\n\t\t'type'  => 'email',\n\t\t'title' => 'Email test',\n\t\t'slug'  => 'email_test'\n\t] ),\n\n\t// File\n\tpapi_property( [\n\t\t'type'  => 'file',\n\t\t'title' => 'File test',\n\t\t'slug'  => 'file_test'\n\t] ),\n\n\t// File test 2\n\tpapi_property( [\n\t\t'type'     => 'file',\n\t\t'title'    => 'File test 2',\n\t\t'slug'     => 'file_test_2',\n\t\t'settings' => [\n\t\t\t'multiple' => true\n\t\t]\n\t] ),\n\n\t// File test 3\n\tpapi_property( [\n\t\t'type'     => 'file',\n\t\t'title'    => 'File test 3',\n\t\t'slug'     => 'file_test_3',\n\t\t'settings' => [\n\t\t\t'fields'   => 'ids',\n\t\t\t'meta_key' => 'custom_id'\n\t\t]\n\t] ),\n\n\t// Flexible\n\tpapi_property( [\n\t\t'type'     => 'flexible',\n\t\t'title'    => 'Flexible test',\n\t\t'slug'     => 'flexible_test',\n\t\t'settings' => [\n\t\t\t'items' => [\n\t\t\t\t'twitter' => [\n\t\t\t\t\t'title' => 'Twitter',\n\t\t\t\t\t'items' => [\n\t\t\t\t\t\tpapi_property( [\n\t\t\t\t\t\t\t'type'  => 'string',\n\t\t\t\t\t\t\t'title' => 'Twitter name',\n\t\t\t\t\t\t\t'slug'  => 'twitter_name'\n\t\t\t\t\t\t] )\n\t\t\t\t\t]\n\t\t\t\t],\n\t\t\t\t'posts' => [\n\t\t\t\t\t'title' => 'Posts',\n\t\t\t\t\t'items' => [\n\t\t\t\t\t\tpapi_property( [\n\t\t\t\t\t\t\t'type'  => 'post',\n\t\t\t\t\t\t\t'title' => 'Post one',\n\t\t\t\t\t\t\t'slug'  => 'post_one'\n\t\t\t\t\t\t] ),\n\t\t\t\t\t\tpapi_property( [\n\t\t\t\t\t\t\t'type'  => 'post',\n\t\t\t\t\t\t\t'title' => 'Post two',\n\t\t\t\t\t\t\t'slug'  => 'post_two'\n\t\t\t\t\t\t] )\n\t\t\t\t\t]\n\t\t\t\t]\n\t\t\t]\n\t\t]\n\t] ),\n\n\t// Gallery\n\tpapi_property( [\n\t\t'type'  => 'gallery',\n\t\t'title' => 'Gallery test',\n\t\t'slug'  => 'gallery_test'\n\t] ),\n\n\t// Group\n\tpapi_property( [\n\t\t'title'    => 'Group test',\n\t\t'slug'     => 'group_test',\n\t\t'type'     => 'group',\n\t\t'settings' => [\n\t\t\t'items' => [\n\t\t\t\tpapi_property( [\n\t\t\t\t\t'title'    => 'Page',\n\t\t\t\t\t'slug'     => 'page',\n\t\t\t\t\t'type'     => 'post',\n\t\t\t\t\t'settings' => [\n\t\t\t\t\t\t'post_type' => 'page'\n\t\t\t\t\t]\n\t\t\t\t] ),\n\t\t\t\tpapi_property( [\n\t\t\t\t\t'type'     => 'string',\n\t\t\t\t\t'title'    => 'Page title',\n\t\t\t\t\t'slug'     => 'page_title'\n\t\t\t\t] )\n\t\t\t]\n\t\t]\n\t] ),\n\n\t// Hidden\n\tpapi_property( [\n\t\t'type'  => 'hidden',\n\t\t'title' => 'Hidden test',\n\t\t'slug'  => 'hidden_test'\n\t] ),\n\n\t// Html\n\tpapi_property( [\n\t\t'type'  => 'html',\n\t\t'title' => 'Html test',\n\t\t'slug'  => 'html_test',\n\t\t'settings' => [\n\t\t\t'html' => '<p>Hello, world!</p>'\n\t\t]\n\t] ),\n\n\t// Html 2\n\tpapi_property( [\n\t\t'type'  => 'html',\n\t\t'title' => 'Html test 2',\n\t\t'slug'  => 'html_test_2',\n\t\t'settings' => [\n\t\t\t'html' => 'properties_output_html'\n\t\t]\n\t] ),\n\n\t// Html save\n\tpapi_property( [\n\t\t'type'  => 'html',\n\t\t'title' => 'Html save test',\n\t\t'slug'  => 'html_save_test',\n\t\t'settings' => [\n\t\t\t'save' => true,\n\t\t\t'html' => '<p>Hello, world!</p>'\n\t\t]\n\t] ),\n\n\t// Image\n\tpapi_property( [\n\t\t'type'  => 'image',\n\t\t'title' => 'Image test',\n\t\t'slug'  => 'image_test'\n\t] ),\n\n\t// Link\n\tpapi_property( [\n\t\t'type'  => 'link',\n\t\t'title' => 'Link test',\n\t\t'slug'  => 'link_test'\n\t] ),\n\n\t// Module\n\tpapi_property( [\n\t\t'type'  => 'module',\n\t\t'title' => 'Module test',\n\t\t'slug'  => 'module_test'\n\t] ),\n\n\t// Number\n\tpapi_property( [\n\t\t'type'  => 'number',\n\t\t'title' => 'Number test',\n\t\t'slug'  => 'number_test'\n\t] ),\n\n\t// Post\n\tpapi_property( [\n\t\t'type'  => 'post',\n\t\t'title' => 'Post test',\n\t\t'slug'  => 'post_test'\n\t] ),\n\n\t// Post 2\n\tpapi_property( [\n\t\t'type'     => 'post',\n\t\t'title'    => 'Post test 2',\n\t\t'slug'     => 'post_test_2',\n\t\t'settings' => [\n\t\t\t'fields'   => 'ids',\n\t\t\t'meta_key' => 'custom_id'\n\t\t]\n\t] ),\n\n\t// Radio\n\tpapi_property( [\n\t\t'type'     => 'radio',\n\t\t'title'    => 'Radio test',\n\t\t'slug'     => 'radio_test',\n\t\t'settings' => [\n\t\t\t'items' => [\n\t\t\t\t'White' => '#ffffff',\n\t\t\t\t'Black' => '#000000'\n\t\t\t]\n\t\t]\n\t] ),\n\n\t// Reference\n\tpapi_property( [\n\t\t'type'  => 'reference',\n\t\t'title' => 'Reference test',\n\t\t'slug'  => 'reference_test'\n\t] ),\n\n\t// Relationship\n\tpapi_property( [\n\t\t'type'  => 'relationship',\n\t\t'title' => 'Relationship test',\n\t\t'slug'  => 'relationship_test'\n\t] ),\n\n\t// Relationship 2\n\tpapi_property( [\n\t\t'type'     => 'relationship',\n\t\t'title'    => 'Relationship test 2',\n\t\t'slug'     => 'relationship_test_2',\n\t\t'settings' => [\n\t\t\t'items' => [\n\t\t\t\t[\n\t\t\t\t\t'id'    => 2,\n\t\t\t\t\t'title' => 'Two'\n\t\t\t\t],\n\t\t\t\t[\n\t\t\t\t\t'id'    => 1,\n\t\t\t\t\t'title'\t=> 'One'\n\t\t\t\t]\n\t\t\t]\n\t\t]\n\t] ),\n\n\t// Repeater\n\tpapi_property( [\n\t\t'type'     => 'repeater',\n\t\t'title'    => 'Repeater test',\n\t\t'slug'     => 'repeater_test',\n\t\t'settings' => [\n\t\t\t'items' => [\n\t\t\t\tpapi_property( [\n\t\t\t\t\t'type'  => 'string',\n\t\t\t\t\t'title' => 'Book name',\n\t\t\t\t\t'slug'  => 'book_name'\n\t\t\t\t] ),\n\t\t\t\tpapi_property( [\n\t\t\t\t\t'type'  => 'bool',\n\t\t\t\t\t'title' => 'Is open?',\n\t\t\t\t\t'slug'  => 'is_open'\n\t\t\t\t] )\n\t\t\t]\n\t\t]\n\t] ),\n\n\t// Repeater with repeater inside\n\tpapi_property( [\n\t\t'type'     => 'repeater',\n\t\t'title'    => 'Repeater with child test',\n\t\t'slug'     => 'repeater_with_child_test',\n\t\t'settings' => [\n\t\t\t'items' => [\n\t\t\t\tpapi_property( [\n\t\t\t\t\t'type'     => 'repeater',\n\t\t\t\t\t'title'    => 'Repeater child test',\n\t\t\t\t\t'slug'     => 'repeater_child_test',\n\t\t\t\t\t'settings' => [\n\t\t\t\t\t\t'items' => [\n\t\t\t\t\t\t\tpapi_property( [\n\t\t\t\t\t\t\t\t'type'  => 'string',\n\t\t\t\t\t\t\t\t'title' => 'Book name',\n\t\t\t\t\t\t\t\t'slug'  => 'book_name'\n\t\t\t\t\t\t\t] ),\n\t\t\t\t\t\t\tpapi_property( [\n\t\t\t\t\t\t\t\t'type'  => 'bool',\n\t\t\t\t\t\t\t\t'title' => 'Is open?',\n\t\t\t\t\t\t\t\t'slug'  => 'is_open'\n\t\t\t\t\t\t\t] )\n\t\t\t\t\t\t]\n\t\t\t\t\t]\n\t\t\t\t] )\n\t\t\t]\n\t\t]\n\t] ),\n\n\t// Sidebar\n\tpapi_property( [\n\t\t'type'     => 'sidebar',\n\t\t'title'    => 'Sidebar test',\n\t\t'slug'     => 'sidebar_test',\n\t\t'settings' => [\n\t\t\t'placeholder' => 'Select sidebar',\n\t\t\t'render'      => false\n\t\t]\n\t] ),\n\n\t// String\n\tpapi_property( [\n\t\t'type'  => 'string',\n\t\t'title' => 'String test',\n\t\t'slug'  => 'string_test'\n\t] ),\n\n\t// String html\n\tpapi_property( [\n\t\t'type'     => 'string',\n\t\t'title'    => 'String html test',\n\t\t'slug'     => 'string_html_test',\n\t\t'settings' => [\n\t\t\t'allow_html' => true\n\t\t]\n\t] ),\n\n\t// Term\n\tpapi_property( [\n\t\t'type'     => 'term',\n\t\t'title'    => 'Term test',\n\t\t'slug'     => 'term_test',\n\t\t'settings' => [\n\t\t\t'taxonomy' => 'test_taxonomy',\n\t\t]\n\t] ),\n\n\t// Term 2\n\tpapi_property( [\n\t\t'type'     => 'term',\n\t\t'title'    => 'Term test 2',\n\t\t'slug'     => 'term_test_2',\n\t\t'settings' => [\n\t\t\t'fields'   => 'ids',\n\t\t\t'meta_key' => 'custom_id',\n\t\t\t'taxonomy' => 'test_taxonomy'\n\t\t]\n\t] ),\n\n\t// Text\n\tpapi_property( [\n\t\t'type'  => 'text',\n\t\t'title' => 'Text test',\n\t\t'slug'  => 'text_test'\n\t] ),\n\n\t// Text html\n\tpapi_property( [\n\t\t'type'     => 'text',\n\t\t'title'    => 'Text html test',\n\t\t'slug'     => 'text_html_test',\n\t\t'settings' => [\n\t\t\t'allow_html' => true\n\t\t]\n\t] ),\n\n\t// Url\n\tpapi_property( [\n\t\t'type'  => 'url',\n\t\t'title' => 'Url test',\n\t\t'slug'  => 'url_test'\n\t] ),\n\n\t// Url mediauploader\n\tpapi_property( [\n\t\t'type'     => 'url',\n\t\t'title'    => 'Url mediauploader test',\n\t\t'slug'     => 'url_mediauploader_test',\n\t\t'settings' => [\n\t\t\t'mediauploader' => true\n\t\t]\n\t] ),\n\n\t// User\n\tpapi_property( [\n\t\t'type'     => 'user',\n\t\t'title'    => 'User test',\n\t\t'slug'     => 'user_test',\n\t\t'settings' => [\n\t\t\t'placeholder' => 'Select user'\n\t\t]\n\t] )\n];\n"
  },
  {
    "path": "tests/data/page-types/boxes/simple.php",
    "content": "<?php\n\nreturn [\n\t'title' => 'Simple',\n\tpapi_property( [\n\t\t'type'  => 'string',\n\t\t'title' => 'Name'\n\t] )\n];\n"
  },
  {
    "path": "tests/data/page-types/broken-page-type.php",
    "content": "<?php\n\nclass Broken_Page_Type extends Papi_Page_Type {\n\n\t/**\n\t * Define our Page Type meta data.\n\t *\n\t * @return array\n\t */\n\tpublic function meta2() {\n\t\treturn [\n\t\t\t'id'          => 'custom-page-type-id',\n\t\t\t'name'        => 'Identifier page',\n\t\t\t'description' => 'This is a identifier page',\n\t\t\t'template'    => 'pages/identifier-page.php'\n\t\t];\n\t}\n\n\t/**\n\t * Define our properties.\n\t */\n\tpublic function register() {\n\t}\n}\n"
  },
  {
    "path": "tests/data/page-types/display-not-page-type.php",
    "content": "<?php\n\nclass Display_Not_Page_Type extends Papi_Page_Type {\n\n\t/**\n\t * Define our Page Type meta data.\n\t *\n\t * @return array\n\t */\n\tpublic function meta() {\n\t\treturn [\n\t\t\t'name'         => 'Display not page',\n\t\t\t'description'  => 'This is a display not page',\n\t\t\t'template'     => 'pages/display-not-page.php',\n\t\t\t'_not_used'    => 'Not used',\n\t\t\t'capabilities' => ['kvack']\n\t\t];\n\t}\n\n\t/**\n\t * Define our properties.\n\t */\n\tpublic function register() {\n\t}\n\n\t/**\n\t * Do not display this page type.\n\t *\n\t * @return string\n\t */\n\tpublic function display( $post_type ) {\n\t\treturn 'hello';\n\t}\n}\n"
  },
  {
    "path": "tests/data/page-types/dot-page-type.php",
    "content": "<?php\n\nclass Dot_Page_Type extends Papi_Page_Type {\n\n\t/**\n\t * Define our Page Type meta data.\n\t *\n\t * @return array\n\t */\n\tpublic function meta() {\n\t\treturn [\n\t\t\t'name'         => 'Dot page',\n\t\t\t'description'  => 'This is a dot page',\n\t\t\t'template'     => 'pages.dot'\n\t\t];\n\t}\n\n\t/**\n\t * Add custom body classes.\n\t *\n\t * @return array\n\t */\n\tpublic function body_classes() {\n\t\treturn ['custom-css-class'];\n\t}\n\n\t/**\n\t * Define our properties.\n\t */\n\tpublic function register() {\n\t}\n}\n"
  },
  {
    "path": "tests/data/page-types/dot2-page-type.php",
    "content": "<?php\n\nclass Dot2_Page_Type extends Papi_Page_Type {\n\n\t/**\n\t * Define our Page Type meta data.\n\t *\n\t * @return array\n\t */\n\tpublic function meta() {\n\t\treturn [\n\t\t\t'name'         => 'Dot2 page',\n\t\t\t'description'  => 'This is a dot2 page',\n\t\t\t'template'     => 'pages.dot2.php'\n\t\t];\n\t}\n\n\t/**\n\t * Define our properties.\n\t */\n\tpublic function register() {\n\t}\n}\n"
  },
  {
    "path": "tests/data/page-types/duck-page-type.php",
    "content": "<?php\n\nclass Duck_Page_Type extends Papi_Page_Type {\n\n\t/**\n\t * Define our Page Type meta data.\n\t *\n\t * @return array\n\t */\n\tpublic function meta() {\n\t\treturn [\n\t\t\t'name'        => 'Duck page',\n\t\t\t'description' => 'This is a duck page',\n\t\t\t'template'    => 'pages/duck-page.php',\n\t\t\t'post_type'   => 'duck'\n\t\t];\n\t}\n\n\t/**\n\t * Define our properties.\n\t */\n\tpublic function register() {\n\t}\n}\n"
  },
  {
    "path": "tests/data/page-types/editor-page-type.php",
    "content": "<?php\n\nclass Editor_Page_Type extends Papi_Page_Type {\n\n\t/**\n\t * Define our Page Type meta data.\n\t *\n\t * @return array\n\t */\n\tpublic function meta() {\n\t\treturn [\n\t\t\t'name'        => 'Editor page',\n\t\t\t'description' => 'This is a editor page',\n\t\t\t'template'    => 'pages/editor-page.php'\n\t\t];\n\t}\n\n\t/**\n\t * Define our properties.\n\t */\n\tpublic function register() {\n\t\t$this->box( 'Content', [\n\t\t\tpapi_property( [\n\t\t\t\t'type'      => 'editor',\n\t\t\t\t'title'     => 'Editor'\n\t\t\t] )\n\t\t] );\n\t}\n}\n"
  },
  {
    "path": "tests/data/page-types/empty-page-type.php",
    "content": "<?php\n\nclass Empty_Page_Type extends Papi_Page_Type {\n\n\t/**\n\t * Define our Page Type meta data.\n\t *\n\t * @return array\n\t */\n\tpublic function meta() {\n\t\treturn [\n\t\t\t'name'        => 'Empty page',\n\t\t\t'description' => 'This is a empty page',\n\t\t\t'template'    => 'pages/empty-page.php'\n\t\t];\n\t}\n\n\t/**\n\t * Define our properties.\n\t */\n\tpublic function register() {\n\t}\n}\n"
  },
  {
    "path": "tests/data/page-types/extra-page-type.php",
    "content": "<?php\n\nclass Extra_Page_Type extends Papi_Page_Type {\n\n\t/**\n\t * Define our Page Type meta data.\n\t *\n\t * @return array\n\t */\n\tpublic function meta() {\n\t\treturn [\n\t\t\t'name'        => 'Extra page',\n\t\t\t'description' => 'This is a extra page',\n\t\t\t'template'    => 'pages/extra-page.php'\n\t\t];\n\t}\n\n\t/**\n\t * Define our properties.\n\t */\n\tpublic function register() {\n\n\t\t$this->box( 'Content', [\n\n\t\t\tpapi_property( [\n\t\t\t\t'type'      => 'editor',\n\t\t\t\t'title'     => 'Editor test',\n\t\t\t\t'slug'      => 'post_content',\n\t\t\t\t'overwrite' => true\n\t\t\t] )\n\n\t\t] );\n\n\t}\n}\n"
  },
  {
    "path": "tests/data/page-types/faq-extra-page-type.php",
    "content": "<?php\n\nif ( ! class_exists( 'FAQ_Page_Type' ) ) {\n\trequire_once __DIR__ . '/faq-page-type.php';\n}\n\nclass FAQ_Extra_Page_Type extends FAQ_Page_Type {\n\n\t/**\n\t * Define our Page Type meta data.\n\t *\n\t * @return array\n\t */\n\tpublic function meta() {\n\t\treturn [\n\t\t\t'name'         => 'FAQ Extra page',\n\t\t\t'description'  => 'This is a faq extra page',\n\t\t\t'template'     => 'pages/faq-extra-page.php'\n\t\t];\n\t}\n\n\tpublic function remove() {\n\t\treturn 'blog';\n\t}\n\n\t/**\n\t * Define our properties.\n\t */\n\tpublic function register() {\n\t\t$this->box( 'Content', [\n\t\t\tpapi_property( [\n\t\t\t\t'description' => 'FAQ 2',\n\t\t\t\t'post_type'   => 'faq',\n\t\t\t\t'type'        => 'string',\n\t\t\t\t'title'       => 'Name'\n\t\t\t] )\n\t\t] );\n\t}\n}\n"
  },
  {
    "path": "tests/data/page-types/faq-extra2-page-type.php",
    "content": "<?php\n\nif ( ! class_exists( 'FAQ_Extra_Page_Type' ) ) {\n\trequire_once __DIR__ . '/faq-extra-page-type.php';\n}\n\nclass FAQ_Extra2_Page_Type extends FAQ_Extra_Page_Type {\n\n\t/**\n\t * Define our Page Type meta data.\n\t *\n\t * @return array\n\t */\n\tpublic function meta() {\n\t\treturn [\n\t\t\t'name'         => 'FAQ Extra2 page',\n\t\t\t'description'  => 'This is a faq extra2 page',\n\t\t\t'template'     => 'pages/faq-extra2-page.php'\n\t\t];\n\t}\n\n\tpublic function remove() {\n\t\treturn 'editor';\n\t}\n\n\t/**\n\t * Define our properties.\n\t */\n\tpublic function register() {\n\t\t$this->box( 'Content', [\n\t\t\tpapi_property( [\n\t\t\t\t'description' => 'FAQ 3',\n\t\t\t\t'post_type'   => 'faq',\n\t\t\t\t'type'        => 'datetime',\n\t\t\t\t'title'       => 'image'\n\t\t\t] )\n\t\t] );\n\t}\n}\n"
  },
  {
    "path": "tests/data/page-types/faq-page-type.php",
    "content": "<?php\n\nclass FAQ_Page_Type extends Papi_Page_Type {\n\n\t/**\n\t * Define our Page Type meta data.\n\t *\n\t * @return array\n\t */\n\tpublic function meta() {\n\t\treturn [\n\t\t\t'name'         => 'FAQ page',\n\t\t\t'description'  => 'This is a faq page',\n\t\t\t'template'     => 'pages/faq-page.php',\n\t\t\t'_not_used'    => 'Not used',\n\t\t\t'thumbnail'    => 'faq.png',\n\t\t\t'post_type'    => 'faq',\n\t\t\t'fill_labels'  => true,\n\t\t\t'labels'       => [\n\t\t\t\t'nan_item' => 'Not a number item'\n\t\t\t],\n\t\t\t'child_types'  => [\n\t\t\t\t'simple-page-type',\n\t\t\t\tnull,\n\t\t\t\t'fake'\n\t\t\t]\n\t\t];\n\t}\n\n\t/**\n\t * Output custom information in publish box.\n\t */\n\tpublic function publish_box() {\n\t\techo 'Hello, world';\n\t}\n\n\t/**\n\t * Remove meta boxes.\n\t *\n\t * @return array\n\t */\n\tpublic function remove() {\n\t\treturn ['div', 'test_meta_box'];\n\t}\n\n\t/**\n\t * Define our properties.\n\t */\n\tpublic function register() {\n\t\t$this->box( 'Content', [\n\t\t\tpapi_property( [\n\t\t\t\t'description' => 'FAQ 1',\n\t\t\t\t'post_type'   => 'faq',\n\t\t\t\t'type'        => 'string',\n\t\t\t\t'title'       => 'Question'\n\t\t\t] ),\n\t\t\tpapi_property( [\n\t\t\t\t'description' => 'FAQ 1',\n\t\t\t\t'post_type'   => 'faq',\n\t\t\t\t'type'        => 'text',\n\t\t\t\t'title'       => 'Answer'\n\t\t\t] ),\n\t\t\tpapi_property( [\n\t\t\t\t'description' => 'FAQ 1',\n\t\t\t\t'type'        => 'text',\n\t\t\t\t'title'       => 'Type',\n\t\t\t\t'disabled'    => true,\n\t\t\t\t'display'     => false\n\t\t\t] )\n\t\t] );\n\t}\n}\n"
  },
  {
    "path": "tests/data/page-types/fields-page-type.php",
    "content": "<?php\n\nclass Fields_Page_Type extends Papi_Page_Type {\n\n\t/**\n\t * Define our Page Type meta data.\n\t *\n\t * @return array\n\t */\n\tpublic function meta() {\n\t\treturn [\n\t\t\t'name'        => 'Fields page',\n\t\t\t'description' => 'This is a fields page',\n\t\t\t'template'    => 'pages/fields-page.php',\n\t\t];\n\t}\n\n\tpublic function fields( $fields ) {\n\t\treturn ['name' => 'fields'];\n\t}\n\n\t/**\n\t * Define our properties.\n\t */\n\tpublic function register() {\n\t\t$this->box( 'Content', [\n\t\t\tpapi_property( [\n\t\t\t\t'type'  => 'string',\n\t\t\t\t'title' => 'Name'\n\t\t\t] ),\n\t\t] );\n\t}\n}\n"
  },
  {
    "path": "tests/data/page-types/flex-page-type.php",
    "content": "<?php\n\nclass Flex_Page_Type extends Papi_Page_Type {\n\n\tpublic function meta() {\n\t\treturn [\n\t\t\t'description' => 'Vel adipisicing dapibus nostra. Lectus malesuada volutpat aliquet',\n\t\t\t'fill_labels' => true,\n\t\t\t'name'        => 'Flex page',\n\t\t\t'template'    => 'pages/flex-page.php'\n\t\t];\n\t}\n\n\tpublic function remove() {\n\t\treturn 'editor';\n\t}\n\n\tpublic function register() {\n\t\t$this->box( 'Content', [\n\t\t\tpapi_property( [\n\t\t\t\t'title'    => 'Sections',\n\t\t\t\t'type'     => 'flexible',\n\t\t\t\t'sidebar'  => false,\n\t\t\t\t'settings' => [\n\t\t\t\t\t'items' => [\n\t\t\t\t\t\t[\n\t\t\t\t\t\t\t'title' => 'Twitter',\n\t\t\t\t\t\t\t'items' => [\n\t\t\t\t\t\t\t\tpapi_property( [\n\t\t\t\t\t\t\t\t\t'title'    => 'Twitter name 1',\n\t\t\t\t\t\t\t\t\t'type'     => 'string'\n\t\t\t\t\t\t\t\t] ),\n\t\t\t\t\t\t\t\tpapi_property( [\n\t\t\t\t\t\t\t\t\t'title'    => 'Twitter name 2',\n\t\t\t\t\t\t\t\t\t'type'     => 'string'\n\t\t\t\t\t\t\t\t] ),\n\t\t\t\t\t\t\t\tpapi_property( [\n\t\t\t\t\t\t\t\t\t'title'    => 'Twitter name 3',\n\t\t\t\t\t\t\t\t\t'type'     => 'string'\n\t\t\t\t\t\t\t\t] )\n\t\t\t\t\t\t\t]\n\t\t\t\t\t\t],\n\t\t\t\t\t\t[\n\t\t\t\t\t\t\t'title' => 'Images',\n\t\t\t\t\t\t\t'items' => [\n\t\t\t\t\t\t\t\tpapi_property( [\n\t\t\t\t\t\t\t\t\t'title' => 'Image left',\n\t\t\t\t\t\t\t\t\t'type'  => 'image'\n\t\t\t\t\t\t\t\t] ),\n\t\t\t\t\t\t\t\tpapi_property( [\n\t\t\t\t\t\t\t\t\t'title' => 'Image right',\n\t\t\t\t\t\t\t\t\t'type'  => 'image'\n\t\t\t\t\t\t\t\t] )\n\t\t\t\t\t\t\t]\n\t\t\t\t\t\t],\n\t\t\t\t\t\t[\n\t\t\t\t\t\t\t'title' => 'Editor',\n\t\t\t\t\t\t\t'items' => [\n\t\t\t\t\t\t\t\tpapi_property( [\n\t\t\t\t\t\t\t\t\t'title' => 'Editor',\n\t\t\t\t\t\t\t\t\t'type'  => 'editor'\n\t\t\t\t\t\t\t\t] )\n\t\t\t\t\t\t\t]\n\t\t\t\t\t\t],\n\t\t\t\t\t\t[\n\t\t\t\t\t\t\t'title' => 'List',\n\t\t\t\t\t\t\t'items' => [\n\t\t\t\t\t\t\t\tpapi_property( [\n\t\t\t\t\t\t\t\t\t'type'     => 'repeater',\n\t\t\t\t\t\t\t\t\t'title'    => 'Repeater test',\n\t\t\t\t\t\t\t\t\t'slug'     => 'repeater_test_other',\n\t\t\t\t\t\t\t\t\t'settings' => [\n\t\t\t\t\t\t\t\t\t\t'items' => [\n\t\t\t\t\t\t\t\t\t\t\tpapi_property( [\n\t\t\t\t\t\t\t\t\t\t\t\t'type'  => 'string',\n\t\t\t\t\t\t\t\t\t\t\t\t'title' => 'Book name',\n\t\t\t\t\t\t\t\t\t\t\t\t'slug'  => 'book_name'\n\t\t\t\t\t\t\t\t\t\t\t] ),\n\t\t\t\t\t\t\t\t\t\t\tpapi_property( [\n\t\t\t\t\t\t\t\t\t\t\t\t'type'  => 'bool',\n\t\t\t\t\t\t\t\t\t\t\t\t'title' => 'Is open?',\n\t\t\t\t\t\t\t\t\t\t\t\t'slug'  => 'is_open'\n\t\t\t\t\t\t\t\t\t\t\t] )\n\t\t\t\t\t\t\t\t\t\t]\n\t\t\t\t\t\t\t\t\t]\n\t\t\t\t\t\t\t\t] )\n\t\t\t\t\t\t\t]\n\t\t\t\t\t\t]\n\t\t\t\t\t]\n\t\t\t\t]\n\t\t\t] )\n\t\t] );\n\t}\n}\n"
  },
  {
    "path": "tests/data/page-types/flex2-page-type.php",
    "content": "<?php\n\nclass Flex2_Page_Type extends Papi_Page_Type {\n\n\tpublic function meta() {\n\t\treturn [\n\t\t\t'description' => 'Vel adipisicing dapibus nostra. Lectus malesuada volutpat aliquet',\n\t\t\t'fill_labels' => true,\n\t\t\t'name'        => 'Flex page',\n\t\t\t'template'    => 'pages/flex-page.php'\n\t\t];\n\t}\n\n\tpublic function remove() {\n\t\treturn 'editor';\n\t}\n\n\tpublic function register() {\n\t\t$this->box( 'Content', [\n\t\t\tpapi_property( [\n\t\t\t\t'title'    => 'Sections',\n\t\t\t\t'type'     => 'flexible',\n\t\t\t\t'sidebar'  => false,\n\t\t\t\t'settings' => [\n\t\t\t\t\t'items' => [\n\t\t\t\t\t\t[\n\t\t\t\t\t\t\t'title' => 'Twitter',\n\t\t\t\t\t\t\t'items' => [\n\t\t\t\t\t\t\t\tpapi_property( [\n\t\t\t\t\t\t\t\t\t'title'    => 'Twitter name 1',\n\t\t\t\t\t\t\t\t\t'type'     => 'string'\n\t\t\t\t\t\t\t\t] ),\n\t\t\t\t\t\t\t\tpapi_property( [\n\t\t\t\t\t\t\t\t\t'title'    => 'Twitter name 2',\n\t\t\t\t\t\t\t\t\t'type'     => 'string'\n\t\t\t\t\t\t\t\t] ),\n\t\t\t\t\t\t\t\tpapi_property( [\n\t\t\t\t\t\t\t\t\t'title'    => 'Twitter name 3',\n\t\t\t\t\t\t\t\t\t'type'     => 'string'\n\t\t\t\t\t\t\t\t] )\n\t\t\t\t\t\t\t]\n\t\t\t\t\t\t],\n\t\t\t\t\t\t[\n\t\t\t\t\t\t\t'title' => 'Images',\n\t\t\t\t\t\t\t'items' => [\n\t\t\t\t\t\t\t\t[\n\t\t\t\t\t\t\t\t\t'title' => 'Image left',\n\t\t\t\t\t\t\t\t\t'type'  => 'image'\n\t\t\t\t\t\t\t\t],\n\t\t\t\t\t\t\t\t[\n\t\t\t\t\t\t\t\t\t'title' => 'Image right',\n\t\t\t\t\t\t\t\t\t'type'  => 'image'\n\t\t\t\t\t\t\t\t]\n\t\t\t\t\t\t\t]\n\t\t\t\t\t\t],\n\t\t\t\t\t\t[\n\t\t\t\t\t\t\t'title' => 'Editor',\n\t\t\t\t\t\t\t'items' => [\n\t\t\t\t\t\t\t\tpapi_property( [\n\t\t\t\t\t\t\t\t\t'title' => 'Editor',\n\t\t\t\t\t\t\t\t\t'type'  => 'editor'\n\t\t\t\t\t\t\t\t] )\n\t\t\t\t\t\t\t]\n\t\t\t\t\t\t]\n\t\t\t\t\t]\n\t\t\t\t]\n\t\t\t] )\n\t\t] );\n\t}\n\n\tpublic function display( $post_type ) {\n\t\treturn false;\n\t}\n}\n"
  },
  {
    "path": "tests/data/page-types/front-page-type.php",
    "content": "<?php\n\nclass Front_Page_Type extends Papi_Front_Page_Type {\n\n\t/**\n\t * Define our Page Type meta data.\n\t *\n\t * @return array\n\t */\n\tpublic function meta() {\n\t\treturn [\n\t\t\t'name'          => 'Front page',\n\t\t\t'description'   => 'This is a front page',\n\t\t\t'template'      => 'pages/front-page.php',\n\t\t\t'standard_type' => false,\n\t\t];\n\t}\n\n\t/**\n\t * Define our properties.\n\t */\n\tpublic function register() {\n\t\t$this->box( 'Info', papi_property( [\n\t\t\t'type'  => 'string',\n\t\t\t'title' => 'Book name'\n\t\t] ) );\n\t}\n}\n"
  },
  {
    "path": "tests/data/page-types/group-page-type.php",
    "content": "<?php\n\nclass Group_Page_Type extends Papi_Page_Type {\n\n\t/**\n\t * Define our Page Type meta data.\n\t *\n\t * @return array\n\t */\n\tpublic function meta() {\n\t\treturn [\n\t\t\t'name'        => 'Group page',\n\t\t\t'description' => 'This is a group page',\n\t\t\t'template'    => 'pages/group-page.php',\n\t\t];\n\t}\n\n\t/**\n\t * Remove post type support and `remove_meta_box`.\n\t *\n\t * @return array\n\t */\n\tpublic function remove() {\n\t\treturn ['all'];\n\t}\n\n\t/**\n\t * Define our properties.\n\t */\n\tpublic function register() {\n\t\t$this->box( [\n\t\t\t'title' => 'Content',\n\t\t\t'props' => [\n\t\t\t\tpapi_property( [\n\t\t\t\t\t'title'    => 'Group',\n\t\t\t\t\t'type'     => 'group',\n\t\t\t\t\t'settings' => [\n\t\t\t\t\t\t'items' => [\n\t\t\t\t\t\t\tpapi_property( [\n\t\t\t\t\t\t\t\t'type'  => 'string',\n\t\t\t\t\t\t\t\t'title' => 'Title',\n\t\t\t\t\t\t\t\t'slug'  => 'title',\n\t\t\t\t\t\t\t] ),\n\t\t\t\t\t\t\tpapi_property( [\n\t\t\t\t\t\t\t\t'title' => 'Preamble',\n\t\t\t\t\t\t\t\t'slug'  => 'preamble',\n\t\t\t\t\t\t\t\t'desc'  => 'Max length: 175 characters',\n\t\t\t\t\t\t\t\t'type'  => 'text',\n\t\t\t\t\t\t\t] ),\n\t\t\t\t\t\t]\n\t\t\t\t\t]\n\t\t\t\t] )\n\t\t\t]\n\t\t] );\n\t}\n}\n"
  },
  {
    "path": "tests/data/page-types/hidden-page-type.php",
    "content": "<?php\n\nclass Hidden_Page_Type extends Papi_Page_Type {\n\n\t/**\n\t * Define our Page Type meta data.\n\t *\n\t * @return array\n\t */\n\tpublic function meta() {\n\t\treturn [\n\t\t\t'name'          => 'Hidden page',\n\t\t\t'description'   => 'This is a hidden page',\n\t\t\t'template'      => 'pages/hidden-page.php',\n\t\t\t'post_type'     => 'hidden',\n\t\t\t'standard_type' => true\n\t\t];\n\t}\n\n\t/**\n\t * Define our properties.\n\t */\n\tpublic function register() {\n\t}\n}\n"
  },
  {
    "path": "tests/data/page-types/hidden2-page-type.php",
    "content": "<?php\n\nclass Hidden2_Page_Type extends Papi_Page_Type {\n\n\t/**\n\t * Define our Page Type meta data.\n\t *\n\t * @return array\n\t */\n\tpublic function meta() {\n\t\treturn [\n\t\t\t'name'          => 'Hidden2 page',\n\t\t\t'description'   => 'This is a hidden2 page',\n\t\t\t'template'      => 'pages/hidden2-page.php',\n\t\t\t'post_type'     => 'hidden',\n\t\t\t'standard_type' => false\n\t\t];\n\t}\n\n\t/**\n\t * Define our properties.\n\t */\n\tpublic function register() {\n\t}\n}\n"
  },
  {
    "path": "tests/data/page-types/identifier-page-type.php",
    "content": "<?php\n\nclass Identifier_Page_Type extends Papi_Page_Type {\n\n\t/**\n\t * Define our Page Type meta data.\n\t *\n\t * @return array\n\t */\n\tpublic function meta() {\n\t\treturn [\n\t\t\t'id'          => 'custom-page-type-id',\n\t\t\t'name'        => 'Identifier page',\n\t\t\t'description' => 'This is a identifier page',\n\t\t\t'template'    => 'pages/identifier-page.php'\n\t\t];\n\t}\n\n\t/**\n\t * Define our properties.\n\t */\n\tpublic function register() {\n\t}\n}\n"
  },
  {
    "path": "tests/data/page-types/module-page-type.php",
    "content": "<?php\n\nclass Module_Page_Type extends Papi_Page_Type {\n\n\t/**\n\t * Define our Page Type meta data.\n\t *\n\t * @return array\n\t */\n\tpublic function meta() {\n\t\treturn [\n\t\t\t'name'        => 'Module page',\n\t\t\t'description' => 'This is a module page',\n\t\t\t'template'    => 'pages/module-page.php'\n\t\t];\n\t}\n\n\t/**\n\t * Define our properties.\n\t */\n\tpublic function register() {\n\t\t$this->box( 'Content', [\n\t\t\tpapi_property( [\n\t\t\t\t'type'  => 'module',\n\t\t\t\t'title' => 'Module',\n\t\t\t] )\n\t\t] );\n\t}\n}\n"
  },
  {
    "path": "tests/data/page-types/modules/image-module-type.php",
    "content": "<?php\n\nclass Image_Module_Type extends Papi_Module_Type {\n\n\t/**\n\t * Define our Page Type meta data.\n\t *\n\t * @return array\n\t */\n\tpublic function meta() {\n\t\treturn [\n\t\t\t'name'        => 'Image module',\n\t\t\t'description' => 'This is a image module',\n\t\t\t'template'    => [\n\t\t\t\t[\n\t\t\t\t\t'template' => 'layout-a.php',\n\t\t\t\t\t'label'    => 'Layout A'\n\t\t\t\t],\n\t\t\t\t[\n\t\t\t\t\t'template' => 'layout-b.php',\n\t\t\t\t\t'label'    => 'Layout B'\n\t\t\t\t]\n\t\t\t]\n\t\t];\n\t}\n\n\t/**\n\t * Define our properties.\n\t */\n\tpublic function register() {\n\t}\n}\n"
  },
  {
    "path": "tests/data/page-types/modules/string-module-type.php",
    "content": "<?php\n\nclass String_Module_Type extends Papi_Module_Type {\n\n\t/**\n\t * Define our Page Type meta data.\n\t *\n\t * @return array\n\t */\n\tpublic function meta() {\n\t\treturn [\n\t\t\t'name'        => 'String module',\n\t\t\t'description' => 'This is a string module',\n\t\t\t'template'    => [\n\t\t\t\t'string-a.php',\n\t\t\t\t'string-b.php'\n\t\t\t]\n\t\t];\n\t}\n\n\t/**\n\t * Define our properties.\n\t */\n\tpublic function register() {\n\t}\n}\n"
  },
  {
    "path": "tests/data/page-types/modules/top-module-type.php",
    "content": "<?php\n\nclass Top_Module_Type extends Papi_Page_Type {\n\n\t/**\n\t * Define our Page Type meta data.\n\t *\n\t * @return array\n\t */\n\tpublic function meta() {\n\t\treturn [\n\t\t\t'name'        => 'Top module',\n\t\t\t'post_type'   => 'module',\n\t\t\t'description' => 'This is a top module'\n\t\t];\n\t}\n\n\t/**\n\t * Define our properties.\n\t */\n\tpublic function register() {\n\t}\n}\n"
  },
  {
    "path": "tests/data/page-types/modules/video-module-type.php",
    "content": "<?php\n\nclass Video_Module_Type extends Papi_Module_Type {\n\n\t/**\n\t * Define our Page Type meta data.\n\t *\n\t * @return array\n\t */\n\tpublic function meta() {\n\t\treturn [\n\t\t\t'name'        => 'Video module',\n\t\t\t'description' => 'This is a video module',\n\t\t\t'template'    => [\n\t\t\t\t[\n\t\t\t\t\t'template' => 'video-b.php',\n\t\t\t\t\t'label'    => 'Video B',\n\t\t\t\t],\n\t\t\t\t[\n\t\t\t\t\t'template' => 'video-a.php',\n\t\t\t\t\t'label'    => 'Video A',\n\t\t\t\t\t'default'  => true\n\t\t\t\t]\n\t\t\t]\n\t\t];\n\t}\n\n\t/**\n\t * Define our properties.\n\t */\n\tpublic function register() {\n\t}\n}\n"
  },
  {
    "path": "tests/data/page-types/name-page-type.php",
    "content": "<?php\n\nclass Name_Page_Type extends Papi_Page_Type {\n\n\t/**\n\t * Define our Page Type meta data.\n\t *\n\t * @return array\n\t */\n\tpublic function meta() {\n\t\treturn [\n\t\t\t'name'         => 'Name page',\n\t\t\t'description'  => 'This is a name page',\n\t\t\t'template'     => [\n\t\t\t\t[\n\t\t\t\t\t'template' => 'layout-a.php',\n\t\t\t\t\t'label'    => 'Layout A'\n\t\t\t\t],\n\t\t\t\t[\n\t\t\t\t\t'template' => 'layout-b.php',\n\t\t\t\t\t'label'    => 'Layout B'\n\t\t\t\t]\n\t\t\t]\n\t\t];\n\t}\n\n\t/**\n\t * Define our properties.\n\t */\n\tpublic function register() {\n\t\t  $this->box( 'Your Name', [\n            papi_property( 'properties/name.php', [\n                'slug'  => 'my_name_is'\n            ] )\n        ] );\n\t}\n}\n"
  },
  {
    "path": "tests/data/page-types/namespace-page-type.php",
    "content": "<?php\n\nnamespace Foo\\Bar;\n\nclass Namespace_Page_Type extends \\Papi_Page_Type {\n\n\t/**\n\t * Define our Page Type meta data.\n\t *\n\t * @return array\n\t */\n\tpublic function meta() {\n\t\treturn [\n\t\t\t'name'        => 'Namespace page',\n\t\t\t'description' => 'This is a namespace page',\n\t\t\t'template'    => 'pages/namespace-page.php'\n\t\t];\n\t}\n\n\t/**\n\t * Define our properties.\n\t */\n\tpublic function register() {\n\t}\n}\n"
  },
  {
    "path": "tests/data/page-types/no-page-type.php",
    "content": "<?php\n\nclass No_Page_Type extends Papi_Entry_Type {\n\n\t/**\n\t * Define our Page Type meta data.\n\t *\n\t * @return array\n\t */\n\tpublic function meta() {\n\t\treturn [\n\t\t\t'name'        => 'No page',\n\t\t\t'description' => 'This is a no page',\n\t\t\t'template'    => 'pages/no-page.php'\n\t\t];\n\t}\n\n\t/**\n\t * Define our properties.\n\t */\n\tpublic function register() {\n\t}\n}\n"
  },
  {
    "path": "tests/data/page-types/options/class-settings-option-type.php",
    "content": "<?php\n\nclass Settings_Option_Type extends Papi_Option_Type {\n\n\tpublic function meta() {\n\t\treturn [\n\t\t\t'name' => 'Settings',\n\t\t\t'menu' => 'options-general.php'\n\t\t];\n\t}\n\n\tpublic function register() {\n\t\t$this->box( 'Settings', [\n\t\t\tpapi_property( [\n\t\t\t\t'title' => 'Link',\n\t\t\t\t'type' => 'link'\n\t\t\t] )\n\t\t] );\n\t}\n}\n"
  },
  {
    "path": "tests/data/page-types/options/header-option-type.php",
    "content": "<?php\n\nclass Header_Option_Type extends Papi_Option_Type {\n\n\tpublic function meta() {\n\t\treturn [\n\t\t\t'name'        => 'Header',\n\t\t\t'menu'        => 'options-general.php',\n\t\t\t'description' => 'This is your header options'\n\t\t];\n\t}\n\n\tpublic function register() {\n\t\t$this->box( 'Options', [\n\n\t\t\tpapi_property( [\n\t\t\t\t'title' => 'Image',\n\t\t\t\t'type' => 'image'\n\t\t\t] ),\n\n\t\t\tpapi_property( [\n\t\t\t\t'title' => 'Name',\n\t\t\t\t'type' => 'string'\n\t\t\t] ),\n\n\t\t\tpapi_property( [\n\t\t\t\t'title' => 'Link',\n\t\t\t\t'type' => 'link'\n\t\t\t] )\n\n\t\t] );\n\n\t\t$this->box( 'Children', [\n\t\t\tpapi_property( [\n\t\t\t\t'type'     => 'string',\n\t\t\t\t'title'    => 'Name',\n\t\t\t\t'slug'     => 'name_levels',\n\t\t\t\t'settings' => [\n\t\t\t\t\t'items' => [\n\t\t\t\t\t\tpapi_property( [\n\t\t\t\t\t\t\t'type'     => 'string',\n\t\t\t\t\t\t\t'title'\t   => 'Child name',\n\t\t\t\t\t\t\t'slug'     => 'child_name',\n\t\t\t\t\t\t\t'settings' => [\n\t\t\t\t\t\t\t\t'items' => [\n\t\t\t\t\t\t\t\t\tpapi_property( [\n\t\t\t\t\t\t\t\t\t\t'type'  => 'string',\n\t\t\t\t\t\t\t\t\t\t'title' => 'Child child name',\n\t\t\t\t\t\t\t\t\t\t'slug'  => 'child_child_name'\n\t\t\t\t\t\t\t\t\t] ),\n\t\t\t\t\t\t\t\t\tnull\n\t\t\t\t\t\t\t\t]\n\t\t\t\t\t\t\t]\n\t\t\t\t\t\t] )\n\t\t\t\t\t]\n\t\t\t\t]\n\t\t\t] )\n\t\t] );\n\n\t\t$this->box( 'Children 2', [\n\t\t\tpapi_property( [\n\t\t\t\t'type'     => 'string',\n\t\t\t\t'title'    => 'Name',\n\t\t\t\t'slug'     => 'name_levels_2',\n\t\t\t\t'settings' => [\n\t\t\t\t\t'items' => [\n\t\t\t\t\t\tpapi_property( [\n\t\t\t\t\t\t\t'type'  => 'string',\n\t\t\t\t\t\t\t'title'\t=> 'Child name 2',\n\t\t\t\t\t\t\t'slug'  => 'child_name_2'\n\t\t\t\t\t\t] )\n\t\t\t\t\t]\n\t\t\t\t]\n\t\t\t] )\n\t\t] );\n\n\t}\n\n\tpublic function display( $post_type ) {\n\t\treturn false;\n\t}\n}\n"
  },
  {
    "path": "tests/data/page-types/options/properties-option-type.php",
    "content": "<?php\n\nclass Properties_Option_Type extends Papi_Option_Type {\n\n\tpublic function meta() {\n\t\treturn [\n\t\t\t'name' => 'Properties',\n\t\t\t'menu' => 'options-general.php'\n\t\t];\n\t}\n\n\tpublic function register() {\n\t\t$this->box( 'boxes/properties.php' );\n\t}\n\n}\n"
  },
  {
    "path": "tests/data/page-types/others/attachment-type.php",
    "content": "<?php\n\nclass Attachment_Type extends Papi_Attachment_Type {\n\n\tpublic function meta() {\n\t\treturn [\n\t\t\t'name' => 'Attachment'\n\t\t];\n\t}\n\n\tpublic function register() {\n\t\t$this->box( 'Content', [\n\t\t\tpapi_property( [\n\t\t\t\t'title' => 'Name',\n\t\t\t\t'slug'  => 'name',\n\t\t\t\t'type'  => 'string'\n\t\t\t] ),\n\t\t\tpapi_property( [\n\t\t\t\t'title' => 'Post',\n\t\t\t\t'slug'  => 'post',\n\t\t\t\t'type'  => 'post'\n\t\t\t] ),\n\t\t\tpapi_property( [\n\t\t\t\t'title' => 'Text',\n\t\t\t\t'slug'  => 'text',\n\t\t\t\t'type'  => 'text'\n\t\t\t] )\n\t\t] );\n\t}\n}\n"
  },
  {
    "path": "tests/data/page-types/others/media-attachment-type.php",
    "content": "<?php\n\nclass Media_Attachment_Type extends Papi_Attachment_Type {\n\n\tpublic function meta() {\n\t\treturn [\n\t\t\t'name' => 'Media'\n\t\t];\n\t}\n\n\tpublic function register() {\n\t\t$this->box( 'Content', [\n\t\t\tpapi_property( [\n\t\t\t\t'title' => 'Media name',\n\t\t\t\t'slug'  => 'media_name',\n\t\t\t\t'type'  => 'string'\n\t\t\t] ),\n\t\t\tpapi_property( [\n\t\t\t\t'title' => 'Media post',\n\t\t\t\t'slug'  => 'media_post',\n\t\t\t\t'type'  => 'post'\n\t\t\t] ),\n\t\t\tpapi_property( [\n\t\t\t\t'title' => 'Media text',\n\t\t\t\t'slug'  => 'media_text',\n\t\t\t\t'type'  => 'text'\n\t\t\t] )\n\t\t] );\n\t}\n}\n"
  },
  {
    "path": "tests/data/page-types/post-page-type.php",
    "content": "<?php\n\nclass Post_Page_Type extends Papi_Page_Type {\n\n\t/**\n\t * Define our Page Type meta data.\n\t *\n\t * @return array\n\t */\n\tpublic function meta() {\n\t\treturn [\n\t\t\t'name'        => 'Post page',\n\t\t\t'description' => 'This is a post page',\n\t\t\t'template'    => 'pages/post-page.php',\n\t\t\t'post_type'   => ['post', 'item', 'attachment']\n\t\t];\n\t}\n\n\t/**\n\t * Define our properties.\n\t */\n\tpublic function register() {\n\t\t$this->box( 'Content', [\n\t\t\tpapi_property( [\n\t\t\t\t'type'     => 'post',\n\t\t\t\t'title'    => 'Post',\n\t\t\t\t'settings' => [\n\t\t\t\t\t'placeholder' => 'Select post'\n\t\t\t\t]\n\t\t\t] )\n\t\t] );\n\t}\n}\n"
  },
  {
    "path": "tests/data/page-types/price-page-type.php",
    "content": "<?php\n\nclass Price_Page_Type extends Papi_Page_Type {\n\n  /**\n   * The type meta options.\n   *\n   * @return array\n   */\n  public function meta() {\n    return [\n      'name'        => 'Price page',\n      'description' => '',\n      'template'    => 'pages/price-page.php'\n    ];\n  }\n\n  /**\n   * Remove default WordPress fields.\n   *\n   * @return array\n   */\n  public function remove() {\n    return ['commentsdiv', 'commentstatusdiv'];\n  }\n\n  /**\n   * Register content meta box.\n   */\n  public function register() {\n    $this->box( 'Content', [\n        papi_property( [\n            'title'    => 'Prices',\n            'type'     => 'repeater',\n            'slug'     => 'list',\n            'settings' => [\n                'layout' => 'row',\n                'items'  => [\n                    papi_property( [\n                        'title' => 'Title',\n                        'type'  => 'string',\n                        'slug'  => 'title',\n                    ] ),\n                    papi_property( [\n                        'title' => 'Description',\n                        'type'  => 'string',\n                        'slug'  => 'Description',\n                    ] ),\n                    papi_property( [\n                        'title'    => 'Prices',\n                        'type'     => 'repeater',\n                        'slug'     => 'prices',\n                        'settings' => [\n                            'layout' => 'row',\n                            'items'  => [\n                                papi_property( [\n                                    'title' => 'Title',\n                                    'type'  => 'string',\n                                    'slug'  => 'title',\n                                ] ),\n                                papi_property( [\n                                    'title' => 'Price',\n                                    'type'  => 'string',\n                                    'slug'  => 'price',\n                                ] ),\n                                papi_property( [\n                                    'title' => 'Description',\n                                    'type'  => 'string',\n                                    'slug'  => 'Description',\n                                ] ),\n                            ]\n                        ]\n                    ] )\n                ]\n            ]\n        ] )\n    ] );\n  }\n}\n"
  },
  {
    "path": "tests/data/page-types/properties/name.php",
    "content": "<?php\n\nreturn papi_property( [\n    'description' => 'This is an example of template - missing data prop',\n    'type'  => 'string',\n    'slug'  => 'slug_to_overwrite',\n] );\n"
  },
  {
    "path": "tests/data/page-types/properties-page-type.php",
    "content": "<?php\n\nclass Properties_Page_Type extends Papi_Page_Type {\n\n\t/**\n\t * Define our Page Type meta data.\n\t *\n\t * @return array\n\t */\n\tpublic function meta() {\n\t\treturn [\n\t\t\t'name'        => 'Properties page type',\n\t\t\t'description' => 'This is a properties page',\n\t\t\t'template'    => 'pages/properties-page.php'\n\t\t];\n\t}\n\n\t/**\n\t * Remove meta boxes.\n\t *\n\t * @return array\n\t */\n\tpublic function remove() {\n\t\treturn ['commentsdiv', 'commentstatusdiv', 'authordiv', 'slugdiv'];\n\t}\n\n\t/**\n\t * Define our properties.\n\t */\n\tpublic function register() {\n\t\t$this->box( 'boxes/properties.php' );\n\n\t\t$this->box( 'Other', [\n\t\t\t// Test items converting.\n\t\t\tpapi_property( [\n\t\t\t\t'type'     => 'flexible',\n\t\t\t\t'title'    => 'Flexible test',\n\t\t\t\t'slug'     => 'flexible_test_other',\n\t\t\t\t'settings' => [\n\t\t\t\t\t'items' => [\n\t\t\t\t\t\t'twitter' => [\n\t\t\t\t\t\t\t'title' => 'Twitter',\n\t\t\t\t\t\t\t'items' => [\n\t\t\t\t\t\t\t\tpapi_property( [\n\t\t\t\t\t\t\t\t\t'type'  => 'string',\n\t\t\t\t\t\t\t\t\t'title' => 'Twitter name',\n\t\t\t\t\t\t\t\t\t'slug'  => 'twitter_name'\n\t\t\t\t\t\t\t\t] )\n\t\t\t\t\t\t\t]\n\t\t\t\t\t\t],\n\t\t\t\t\t\t'posts' => [\n\t\t\t\t\t\t\t'title' => 'Posts',\n\t\t\t\t\t\t\t'items' => [\n\t\t\t\t\t\t\t\tpapi_property( [\n\t\t\t\t\t\t\t\t\t'type'  => 'post',\n\t\t\t\t\t\t\t\t\t'title' => 'Post one',\n\t\t\t\t\t\t\t\t\t'slug'  => 'post_one'\n\t\t\t\t\t\t\t\t] ),\n\t\t\t\t\t\t\t\tpapi_property( [\n\t\t\t\t\t\t\t\t\t'type'  => 'post',\n\t\t\t\t\t\t\t\t\t'title' => 'Post two',\n\t\t\t\t\t\t\t\t\t'slug'  => 'post_two'\n\t\t\t\t\t\t\t\t] )\n\t\t\t\t\t\t\t]\n\t\t\t\t\t\t],\n\t\t\t\t\t\t'list' => [\n\t\t\t\t\t\t\t'title' => 'List',\n\t\t\t\t\t\t\t'items' => [\n\t\t\t\t\t\t\t\tpapi_property( [\n\t\t\t\t\t\t\t\t\t'type'     => 'repeater',\n\t\t\t\t\t\t\t\t\t'title'    => 'Repeater test',\n\t\t\t\t\t\t\t\t\t'slug'     => 'repeater_test_other',\n\t\t\t\t\t\t\t\t\t'settings' => [\n\t\t\t\t\t\t\t\t\t\t'items' => [\n\t\t\t\t\t\t\t\t\t\t\tpapi_property( [\n\t\t\t\t\t\t\t\t\t\t\t\t'type'  => 'string',\n\t\t\t\t\t\t\t\t\t\t\t\t'title' => 'Book name',\n\t\t\t\t\t\t\t\t\t\t\t\t'slug'  => 'book_name'\n\t\t\t\t\t\t\t\t\t\t\t] ),\n\t\t\t\t\t\t\t\t\t\t\tpapi_property( [\n\t\t\t\t\t\t\t\t\t\t\t\t'type'  => 'bool',\n\t\t\t\t\t\t\t\t\t\t\t\t'title' => 'Is open?',\n\t\t\t\t\t\t\t\t\t\t\t\t'slug'  => 'is_open'\n\t\t\t\t\t\t\t\t\t\t\t] )\n\t\t\t\t\t\t\t\t\t\t]\n\t\t\t\t\t\t\t\t\t]\n\t\t\t\t\t\t\t\t] )\n\t\t\t\t\t\t\t]\n\t\t\t\t\t\t],\n\t\t\t\t\t\t'list2' => [\n\t\t\t\t\t\t\t'title' => 'List 2',\n\t\t\t\t\t\t\t'items' => [\n\t\t\t\t\t\t\t\tpapi_property( [\n\t\t\t\t\t\t\t\t\t'type'     => 'repeater',\n\t\t\t\t\t\t\t\t\t'title'    => 'Repeater test 2',\n\t\t\t\t\t\t\t\t\t'slug'     => 'repeater_test_other_2',\n\t\t\t\t\t\t\t\t\t'settings' => [\n\t\t\t\t\t\t\t\t\t\t'items' => [\n\t\t\t\t\t\t\t\t\t\t\tpapi_property( [\n\t\t\t\t\t\t\t\t\t\t\t\t'type'  => 'string',\n\t\t\t\t\t\t\t\t\t\t\t\t'title' => 'Book name',\n\t\t\t\t\t\t\t\t\t\t\t\t'slug'  => 'book_name'\n\t\t\t\t\t\t\t\t\t\t\t] ),\n\t\t\t\t\t\t\t\t\t\t\tpapi_property( [\n\t\t\t\t\t\t\t\t\t\t\t\t'type'  => 'bool',\n\t\t\t\t\t\t\t\t\t\t\t\t'title' => 'Is open?',\n\t\t\t\t\t\t\t\t\t\t\t\t'slug'  => 'is_open'\n\t\t\t\t\t\t\t\t\t\t\t] )\n\t\t\t\t\t\t\t\t\t\t]\n\t\t\t\t\t\t\t\t\t]\n\t\t\t\t\t\t\t\t] )\n\t\t\t\t\t\t\t]\n\t\t\t\t\t\t]\n\t\t\t\t\t]\n\t\t\t\t]\n\t\t\t] )\n\t\t] );\n\n\t}\n}\n"
  },
  {
    "path": "tests/data/page-types/rule-page-type.php",
    "content": "<?php\n\nclass Rule_Page_Type extends Papi_Page_Type {\n\n\t/**\n\t * Define our Page Type meta data.\n\t *\n\t * @return array\n\t */\n\tpublic function meta() {\n\t\treturn [\n\t\t\t'name'        => 'Rule page',\n\t\t\t'description' => 'This is a rule page',\n\t\t\t'template'    => 'pages/rule-page.php',\n\t\t\t'post_type'   => []\n\t\t];\n\t}\n\n\t/**\n\t * Define our properties.\n\t */\n\tpublic function register() {\n\t\t// Test box property.\n\t\t$this->box( 'Hello', papi_property( [\n\t\t\t'type'  => 'string',\n\t\t\t'title' => 'Name'\n\t\t] ) );\n\n\t\t$this->box( 'Content', [\n\t\t\t'type'  => 'string',\n\t\t\t'title' => 'Name',\n\t\t\t'slug' => 'name2'\n\t\t] );\n\n\t\t$this->box( papi_property( [\n\t\t\t'type'  => 'number',\n\t\t\t'title' => 'Siffran',\n\t\t\t'slug'  => 'siffran'\n\t\t] ) );\n\n\t\t$this->box( 'Number', papi_property( [\n\t\t\t'type'  => 'number',\n\t\t\t'title' => 'Number',\n\t\t\t'slug'  => 'number'\n\t\t] ) );\n\n\t\t$this->box( 'Rules', [\n\t\t\tpapi_property( [\n\t\t\t\t'type'  => 'number',\n\t\t\t\t'title' => 'Rules 1',\n\t\t\t\t'slug'  => 'rules_1',\n\t\t\t\t'rules' => [\n\t\t\t\t\t[\n\t\t\t\t\t\t'operator' => '=',\n\t\t\t\t\t\t'slug'     => 'rules1',\n\t\t\t\t\t\t'value'\t   => 123\n\t\t\t\t\t]\n\t\t\t\t]\n\t\t\t] ),\n\t\t\tpapi_property( [\n\t\t\t\t'type'  => 'number',\n\t\t\t\t'title' => 'Rules 2',\n\t\t\t\t'slug'  => 'rules_2',\n\t\t\t\t'rules' => [\n\t\t\t\t\t[\n\t\t\t\t\t\t'operator' => 'NOT EXISTS',\n\t\t\t\t\t\t'slug'     => 'rules2'\n\t\t\t\t\t]\n\t\t\t\t]\n\t\t\t] ),\n\t\t\tpapi_property( [\n\t\t\t\t'type'  => 'string',\n\t\t\t\t'title' => 'Rules 3',\n\t\t\t\t'slug'  => 'rules_3',\n\t\t\t\t'rules' => [\n\t\t\t\t\t[\n\t\t\t\t\t\t'operator' => '=',\n\t\t\t\t\t\t'slug'     => 'rules_3',\n\t\t\t\t\t\t'value'    => 'hello'\n\t\t\t\t\t]\n\t\t\t\t]\n\t\t\t] )\n\t\t] );\n\n\t\t// Support rules with group:\n\t\t// slug => group.0.media\n\t\t$this->box( [\n\t\t\t'title'      => 'Content',\n\t\t\t'properties' => [\n\t\t\t\tpapi_property( [\n\t\t\t\t\t'type'     => 'repeater',\n\t\t\t\t\t'title'    => 'List',\n\t\t\t\t\t'slug'     => 'list',\n\t\t\t\t\t'settings' => [\n\t\t\t\t\t\t'layout'  => 'row',\n\t\t\t\t\t\t'items'  => [\n\t\t\t\t\t\t\tpapi_property( [\n\t\t\t\t\t\t\t\t'slug'     => 'group',\n\t\t\t\t\t\t\t\t'title'    => 'Group',\n\t\t\t\t\t\t\t\t'type'     => 'group',\n\t\t\t\t\t\t\t\t'settings' => [\n\t\t\t\t\t\t\t\t\t'items' => [\n\t\t\t\t\t\t\t\t\t\tpapi_property( [\n\t\t\t\t\t\t\t\t\t\t\t'type'     => 'radio',\n\t\t\t\t\t\t\t\t\t\t\t'title'    => 'Media',\n\t\t\t\t\t\t\t\t\t\t\t'slug'     => 'media',\n\t\t\t\t\t\t\t\t\t\t\t'settings' => [\n\t\t\t\t\t\t\t\t\t\t\t\t'items' => [\n\t\t\t\t\t\t\t\t\t\t\t\t\t'Image' => 'image',\n\t\t\t\t\t\t\t\t\t\t\t\t\t'Video' => 'video',\n\t\t\t\t\t\t\t\t\t\t\t\t],\n\t\t\t\t\t\t\t\t\t\t\t],\n\t\t\t\t\t\t\t\t\t\t] ),\n\t\t\t\t\t\t\t\t\t],\n\t\t\t\t\t\t\t\t],\n\t\t\t\t\t\t\t] ),\n\t\t\t\t\t\t\tpapi_property( [\n\t\t\t\t\t\t\t\t'type'  => 'string',\n\t\t\t\t\t\t\t\t'title' => 'Video link',\n\t\t\t\t\t\t\t\t'slug'  => 'video_link',\n\t\t\t\t\t\t\t\t'rules' => [\n\t\t\t\t\t\t\t\t\t[\n\t\t\t\t\t\t\t\t\t\t'compare' => '=',\n\t\t\t\t\t\t\t\t\t\t'value'   => 'video',\n\t\t\t\t\t\t\t\t\t\t'slug'    => 'group.media',\n\t\t\t\t\t\t\t\t\t]\n\t\t\t\t\t\t\t\t],\n\t\t\t\t\t\t\t] ),\n\t\t\t\t\t\t],\n\t\t\t\t\t],\n\t\t\t\t] ),\n\t\t\t],\n\t\t] );\n\n\t\t$this->box( 'Group', [\n\t\t\tpapi_property( [\n\t\t\t\t'title'    => 'CTA Button',\n\t\t\t\t'slug'     => 'cta_link',\n\t\t\t\t'type'     => 'group',\n\t\t\t\t'settings' => [\n\t\t\t\t\t'items' => [\n\t\t\t\t\t\tpapi_property( [\n\t\t\t\t\t\t\t'title'    => 'Select link type',\n\t\t\t\t\t\t\t'slug'     => 'selected_link_type',\n\t\t\t\t\t\t\t'type'     => 'radio',\n\t\t\t\t\t\t\t'sidebar'  => false,\n\t\t\t\t\t\t\t'layout'   => 'row',\n\t\t\t\t\t\t\t'settings' => [\n\t\t\t\t\t\t\t\t'selected' => 'none',\n\t\t\t\t\t\t\t\t'items'    => [\n\t\t\t\t\t\t\t\t\t'None'              => 'none',\n\t\t\t\t\t\t\t\t\t'Link to page'      => 'link_to_page',\n\t\t\t\t\t\t\t\t\t'Scroll to content' => 'scroll_to_content',\n\t\t\t\t\t\t\t\t],\n\t\t\t\t\t\t\t],\n\t\t\t\t\t\t] ),\n\t\t\t\t\t\tpapi_property( [\n\t\t\t\t\t\t\t'slug'    => 'page_link',\n\t\t\t\t\t\t\t'type'    => 'link',\n\t\t\t\t\t\t\t'sidebar' => false,\n\t\t\t\t\t\t\t'rules'   => [\n\t\t\t\t\t\t\t\t[\n\t\t\t\t\t\t\t\t\t'operator' => '=',\n\t\t\t\t\t\t\t\t\t'value'    => 'link_to_page',\n\t\t\t\t\t\t\t\t\t'slug'     => 'selected_link_type',\n\t\t\t\t\t\t\t\t],\n\t\t\t\t\t\t\t],\n\t\t\t\t\t\t] ),\n\t\t\t\t\t\tpapi_property( [\n\t\t\t\t\t\t\t'slug'        => 'scroll_link',\n\t\t\t\t\t\t\t'description' => 'Button text',\n\t\t\t\t\t\t\t'type'        => 'string',\n\t\t\t\t\t\t\t'rules'       => [\n\t\t\t\t\t\t\t\t[\n\t\t\t\t\t\t\t\t\t'operator' => '=',\n\t\t\t\t\t\t\t\t\t'value'    => 'scroll_to_content',\n\t\t\t\t\t\t\t\t\t'slug'     => 'selected_link_type',\n\t\t\t\t\t\t\t\t],\n\t\t\t\t\t\t\t],\n\t\t\t\t\t\t] ),\n\t\t\t\t\t],\n\t\t\t\t],\n\t\t\t] ),\n\t\t] );\n\t}\n}\n"
  },
  {
    "path": "tests/data/page-types/simple-page-type.php",
    "content": "<?php\n\nclass Simple_Page_Type extends Papi_Page_Type {\n\n\t/**\n\t * Define our Page Type meta data.\n\t *\n\t * @return array\n\t */\n\tpublic function meta() {\n\t\treturn [\n\t\t\t'name'                 => 'Simple page',\n\t\t\t'description'          => 'This is a simple page',\n\t\t\t'template'             => 'pages/simple-page.php',\n\t\t\t'post_type'            => [],\n\t\t\t'standard_type'        => true,\n\t\t\t'show_page_attributes' => false,\n\t\t\t'show_permalink'       => false\n\t\t];\n\t}\n\n\t/**\n\t * Add custom body classes.\n\t *\n\t * @return array\n\t */\n\tpublic function body_classes() {\n\t\treturn ['simple-page-type'];\n\t}\n\n\t/**\n\t * Remove post type support and `remove_meta_box`.\n\t *\n\t * @return array\n\t */\n\tpublic function remove() {\n\t\treturn ['editor', 'commentdiv'];\n\t}\n\n\t/**\n\t * Define our properties.\n\t */\n\tpublic function register() {\n\t\t// Test box property.\n\t\t$this->box( 'Hello', papi_property( [\n\t\t\t'type'  => 'string',\n\t\t\t'title' => 'Name'\n\t\t] ) );\n\n\t\t$this->box( 'Hello 2', papi_property( [\n\t\t\t'type'  => 'string',\n\t\t\t'title' => 'Namn'\n\t\t] ) );\n\n\t\t$this->box( 'Content', [\n\t\t\t'type'  => 'string',\n\t\t\t'title' => 'Name',\n\t\t\t'slug' => 'name2'\n\t\t] );\n\n\t\t$this->box( 'Siffror', [\n\t\t\tpapi_property( [\n\t\t\t\t'type'         => 'number',\n\t\t\t\t'title'        => 'Siffran',\n\t\t\t\t'slug'         => 'siffran',\n\t\t\t\t'before_html'  => 'En siffra',\n\t\t\t\t'before_class' => 'en-siffra',\n\t\t\t\t'after_html'   => 'Två siffra',\n\t\t\t\t'after_class'  => 'tva-siffra'\n\t\t\t] )\n\t\t] );\n\n\t\t$this->box( 'Number', papi_property( [\n\t\t\t'type'  => 'number',\n\t\t\t'title' => 'Number',\n\t\t\t'slug'  => 'number'\n\t\t] ) );\n\n\t\t// Will not work.\n\t\t$this->box( 1 );\n\n\t\t// Box without a empty title.\n\t\t$this->box( ['title' => ''], [\n\t\t\tpapi_property( [\n\t\t\t\t'type'  => 'string',\n\t\t\t\t'title' => 'Country'\n\t\t\t] )\n\t\t] );\n\n\t\t// Test properties from another method.\n\t\t$this->box( [\n\t\t\t'title'      => 'Content',\n\t\t\t'sort_order' => 100\n\t\t], [ $this, 'content_box' ] );\n\n\t\t// Load box from a template file.\n\t\t$this->box(\n\t\t\t$this->template(\n\t\t\t\tdirname( __DIR__ ) . '/page-types/boxes/simple.php'\n\t\t\t)\n\t\t);\n\n\t\t$this->box( 'Children', [\n\t\t\tpapi_property( [\n\t\t\t\t'type'     => 'string',\n\t\t\t\t'title'    => 'Name',\n\t\t\t\t'slug'     => 'name_levels',\n\t\t\t\t'settings' => [\n\t\t\t\t\t'items' => [\n\t\t\t\t\t\tpapi_property( [\n\t\t\t\t\t\t\t'type'     => 'string',\n\t\t\t\t\t\t\t'title'\t   => 'Child name',\n\t\t\t\t\t\t\t'slug'     => 'child_name',\n\t\t\t\t\t\t\t'settings' => [\n\t\t\t\t\t\t\t\t'items' => [\n\t\t\t\t\t\t\t\t\tpapi_property( [\n\t\t\t\t\t\t\t\t\t\t'type'  => 'string',\n\t\t\t\t\t\t\t\t\t\t'title' => 'Child child name',\n\t\t\t\t\t\t\t\t\t\t'slug'  => 'child_child_name'\n\t\t\t\t\t\t\t\t\t] ),\n\t\t\t\t\t\t\t\t\tnull\n\t\t\t\t\t\t\t\t]\n\t\t\t\t\t\t\t]\n\t\t\t\t\t\t] )\n\t\t\t\t\t]\n\t\t\t\t]\n\t\t\t] )\n\t\t] );\n\n\t\t$this->box( 'Children 2', [\n\t\t\tpapi_property( [\n\t\t\t\t'type'     => 'string',\n\t\t\t\t'title'    => 'Name',\n\t\t\t\t'slug'     => 'name_levels_2',\n\t\t\t\t'settings' => [\n\t\t\t\t\t'items' => [\n\t\t\t\t\t\tpapi_property( [\n\t\t\t\t\t\t\t'type'  => 'string',\n\t\t\t\t\t\t\t'title'\t=> 'Child name 2',\n\t\t\t\t\t\t\t'slug'  => 'child_name_2'\n\t\t\t\t\t\t] )\n\t\t\t\t\t]\n\t\t\t\t]\n\t\t\t] )\n\t\t] );\n\n\t\t$this->box( 'Content box broken', [ [ $this, 'content_box_broken' ] ] );\n\n\t\t// Test default value.\n\t\t$this->box( 'Hello', papi_property( [\n\t\t\t'slug'  => 'name_default',\n\t\t\t'type'  => 'string',\n\t\t\t'title' => 'Name',\n\t\t\t'value' => 'Fredrik'\n\t\t] ) );\n\n\t\t$this->box( 'Sections', [\n\t\t\tpapi_property( [\n\t\t\t\t'type'     => 'repeater',\n\t\t\t\t'title'    => 'Sections',\n\t\t\t\t'slug'     => 'sections',\n\t\t\t\t'sidebar'  => false,\n\t\t\t\t'settings' => [\n\t\t\t\t\t'items' => papi_property( [\n\t\t\t\t\t\t'type'  => 'string',\n\t\t\t\t\t\t'title' => 'Title',\n\t\t\t\t\t\t'slug'  => 'title'\n\t\t\t\t\t] )\n\t\t\t\t]\n\t\t\t] )\n\t\t] );\n\n\t\t$this->box( 'String test', [\n\t\t\tpapi_property( [\n\t\t\t\t'type'  => 'string',\n\t\t\t\t'title' => 'String test',\n\t\t\t\t'slug'  => 'string_test'\n\t\t\t] )\n\t\t] );\n\n\t\t$this->box( 'UPPERCASE TEST', [\n\t\t\tpapi_property( [\n\t\t\t\t'type'  => 'string',\n\t\t\t\t'title' => 'UPPERCASE',\n\t\t\t\t'slug'  => 'UPPERCASE'\n\t\t\t] )\n\t\t] );\n\n\t\t$this->box( 'Format callback property', [\n\t\t\tpapi_property( [\n\t\t\t\t'type'      => 'string',\n\t\t\t\t'title'     => 'format_cb',\n\t\t\t\t'slug'      => 'format_cb',\n\t\t\t\t'format_cb' => function ( $value ) {\n\t\t\t\t\treturn 'Hello ' . $value;\n\t\t\t\t}\n\t\t\t] )\n\t\t] );\n\t}\n\n\tpublic function content_box() {\n\t\treturn [ $this->property( [\n\t\t\t'type'  => 'string',\n\t\t\t'title' => 'Name'\n\t\t] ) ];\n\t}\n\n\tpublic function content_box_broken() {\n\t\treturn;\n\t}\n}\n"
  },
  {
    "path": "tests/data/page-types/tab-page-type.php",
    "content": "<?php\n\nclass Tab_Page_Type extends Papi_Page_Type {\n\n\t/**\n\t * Define our Page Type meta data.\n\t *\n\t * @return array\n\t */\n\tpublic function meta() {\n\t\treturn [\n\t\t\t'name'        => 'Tab page',\n\t\t\t'description' => 'This is a tab page',\n\t\t\t'template'    => 'pages/tab-page.php'\n\t\t];\n\t}\n\n\t/**\n\t * Define our properties.\n\t */\n\tpublic function register() {\n\t\t// Add tabs to a box.\n\t\t$this->box( 'Tabs', [\n\t\t\t$this->tab( 'Content', [\n\t\t\t\t$this->property( [\n\t\t\t\t\t'type'  => 'string',\n\t\t\t\t\t'title' => 'Name'\n\t\t\t\t] )\n\t\t\t] ),\n\n\t\t\t$this->tab(\n\t\t\t\tpapi_template( __DIR__ . '/tabs/content.php' )\n\t\t\t)\n\t\t] );\n\n\t\t$this->box( [\n\t\t\t'type'  => 'string',\n\t\t\t'title' => 'Title'\n\t\t] );\n\n\t\t$this->box( 'Tabs not working', [\n\t\t\t$this->tab( 1 )\n\t\t] );\n\n\t\t$this->box( 'Tabs not working with children', [\n\t\t\tpapi_property( [\n\t\t\t\t'type'     => 'string',\n\t\t\t\t'title'    => 'Name',\n\t\t\t\t'slug'     => 'name_levels_2',\n\t\t\t\t'settings' => [\n\t\t\t\t\t'items' => [\n\t\t\t\t\t\tpapi_property( [\n\t\t\t\t\t\t\t'type'  => 'string',\n\t\t\t\t\t\t\t'title'\t=> 'Child name 2',\n\t\t\t\t\t\t\t'slug'  => 'child_name_2'\n\t\t\t\t\t\t] )\n\t\t\t\t\t]\n\t\t\t\t]\n\t\t\t] )\n\t\t] );\n\n\t\t$this->box( 'Services Page', [\n\t\t\t$this->tab( 'tabs/video.php', [\n\t\t\t\t'title' => 'Background'\n\t\t\t] )\n\t\t] );\n\t}\n}\n"
  },
  {
    "path": "tests/data/page-types/tab2-page-type.php",
    "content": "<?php\n\nclass Tab2_Page_Type extends Papi_Page_Type {\n\n\t/**\n\t * Define our Page Type meta data.\n\t *\n\t * @return array\n\t */\n\tpublic function meta() {\n\t\treturn [\n\t\t\t'name'        => 'Tab2 page',\n\t\t\t'description' => 'This is a tab2 page',\n\t\t\t'template'    => 'pages/tab2-page.php'\n\t\t];\n\t}\n\n\t/**\n\t * Remove meta boxes.\n\t *\n\t * @return array\n\t */\n\tpublic function remove() {\n\t\treturn ['editor', 'commentsdiv', 'commentstatusdiv', 'authordiv', 'slugdiv'];\n\t}\n\n\t/**\n\t * Define our properties.\n\t */\n\tpublic function register() {\n\t\t$this->box( 'Edit page', [\n\t\t\tpapi_tab( 'Content', [\n\t\t\t\tpapi_property( [\n\t\t\t\t\t'slug'      => 'post_content',\n\t\t\t\t\t'type'      => 'editor',\n\t\t\t\t\t'sidebar'   => false,\n\t\t\t\t\t'overwrite' => true\n\t\t\t\t] )\n\t\t\t] ),\n\t\t\tpapi_tab( 'Images', [\n\t\t\t\tpapi_property( [\n\t\t\t\t\t'title'       => 'Image',\n\t\t\t\t\t'slug'        => 'image',\n\t\t\t\t\t'type'        => 'image',\n\t\t\t\t\t'overwrite'   => true,\n\t\t\t\t\t'description' => 'Pretium non irure. Nascetur sit velit quisque venenatis fermentum'\n\t\t\t\t] ),\n\t\t\t\tpapi_property( [\n\t\t\t\t\t'title' => 'Description',\n\t\t\t\t\t'slug'  => 'image_description',\n\t\t\t\t\t'type'  => 'string'\n\t\t\t\t] )\n\t\t\t] ),\n\t\t\tpapi_tab( [\n\t\t\t\t'title'      => 'Author',\n\t\t\t\t'background' => 'white'\n\t\t\t], [\n\t\t\t\tpapi_property( [\n\t\t\t\t\t'title' => 'Name',\n\t\t\t\t\t'slug'  => 'name',\n\t\t\t\t\t'type'  => 'string'\n\t\t\t\t] )\n\t\t\t] )\n\t\t] );\n\t}\n}\n"
  },
  {
    "path": "tests/data/page-types/tabs/content.php",
    "content": "<?php\n\nreturn papi_tab( [\n\t'title' => 'Content 2',\n\tpapi_property( [\n\t\t'type'  => 'string',\n\t\t'title' => 'Name 2'\n\t] )\n] );\n"
  },
  {
    "path": "tests/data/page-types/tabs/video.php",
    "content": "<?php\n\nreturn papi_tab( [\n  'title' => 'Images',\n  'slug'  => 'custom_image_slug'\n], [\n  papi_property( [\n    'type'  => 'string',\n    'title' => 'Name',\n    'slug'  => 'name'\n  ] )\n] );\n"
  },
  {
    "path": "tests/data/page-types/test-page-type.php",
    "content": "<?php\n\nclass Test_Page_Type extends Papi_Page_Type {\n\n\t/**\n\t * Define our Page Type meta data.\n\t *\n\t * @return array\n\t */\n\tpublic function meta() {\n\t\treturn [\n\t\t\t'fill_labels' => true,\n\t\t\t'name'        => 'Test page',\n\t\t\t'template'    => 'pages/test-page.php'\n\t\t];\n\t}\n\n\t/**\n\t * Add help tabs.\n\t *\n\t * @return array\n\t */\n\tpublic function help() {\n\t\treturn [\n\t\t\t'Page type'    => 'Class cursus vehicula dolor. Tellus inceptos semper pede reprehenderit habitant.',\n\t\t\t'More details' => 'Convallis morbi fames vivamus cum. Ac metus vivamus sollicitudin.'\n\t\t];\n\t}\n\n\t/**\n\t * Remove meta boxes.\n\t *\n\t * @return array\n\t */\n\tpublic function remove() {\n\t\treturn ['editor', 'commentsdiv', 'commentstatusdiv', 'authordiv', 'slugdiv'];\n\t}\n\n\t/**\n\t * Register properties.\n\t */\n\tpublic function register() {\n\t\t$this->box( 'boxes/properties.php' );\n\t}\n}\n"
  },
  {
    "path": "tests/data/page-types/twenty-page-type.php",
    "content": "<?php\n\nclass Twenty_Page_Type extends Papi_Page_Type {\n\n\t/**\n\t * Define our Page Type meta data.\n\t *\n\t * @return array\n\t */\n\tpublic function meta() {\n\t\treturn [\n\t\t\t'name'        => 'Twenty page',\n\t\t\t'description' => 'This is a twenty page',\n\t\t\t'template'    => 'functions.php'\n\t\t];\n\t}\n\n\t/**\n\t * Define our properties.\n\t */\n\tpublic function register() {\n\t}\n}\n"
  },
  {
    "path": "tests/data/page-types2/any-page-type.php",
    "content": "<?php\n\nclass Any_Page_Type extends Papi_Page_Type {\n\n\t/**\n\t * Define our Page Type meta data.\n\t *\n\t * @return array\n\t */\n\tpublic function meta() {\n\t\treturn [\n\t\t\t'name'        => 'Any page',\n\t\t\t'description' => 'This is a any page',\n\t\t\t'template'    => 'pages/any-page.php',\n\t\t\t'post_type'   => 'any'\n\t\t];\n\t}\n\n\t/**\n\t * Define our properties.\n\t */\n\tpublic function register() {\n\t\t$this->box( 'Content', papi_property( [\n\t\t\t'type'  => 'string',\n\t\t\t'title' => 'Name'\n\t\t] ) );\n\t}\n}\n"
  },
  {
    "path": "tests/data/page-types2/look-page-type.php",
    "content": "<?php\n\nclass Look_Module_Type extends Papi_Page_Type {\n\n\t/**\n\t * Define our Page Type meta data.\n\t *\n\t * @return array\n\t */\n\tpublic function meta() {\n\t\treturn [\n\t\t\t'name'        => 'Look page',\n\t\t\t'description' => 'This is a look page',\n\t\t\t'post_type'   => []\n\t\t];\n\t}\n\n\t/**\n\t * Define our properties.\n\t */\n\tpublic function register() {\n\t}\n}\n"
  },
  {
    "path": "tests/data/properties/array.php",
    "content": "<?php\n\nreturn [\n\t'title' => 'Name',\n\t'type'  => 'string'\n];\n"
  },
  {
    "path": "tests/data/properties/class-papi-property-fake.php",
    "content": "<?php\n\nclass Papi_Property_Fake {}\n"
  },
  {
    "path": "tests/data/properties/class-papi-property-kvack.php",
    "content": "<?php\n\nclass Papi_Property_Kvack extends Papi_Property {\n\n\tpublic function html() {}\n}\n"
  },
  {
    "path": "tests/data/properties/simple.php",
    "content": "<?php\n\nreturn papi_property( [\n\t'type'  => 'string',\n\t'title' => 'Name'\n] );\n"
  },
  {
    "path": "tests/data/taxonomy-types/broken-taxonomy-type.php",
    "content": "<?php\n\nclass Broken_Taxonomy_Type extends Papi_Taxonomy_Type {\n\n\t/**\n\t * Define our Taxonomy Type meta data.\n\t *\n\t * @return array\n\t */\n\tpublic function meta2() {\n\t\treturn [\n\t\t\t'id'          => 'custom-taxonomy-type-id',\n\t\t\t'name'        => 'Broken taxonomy',\n\t\t\t'description' => 'This is a broken taxonomy',\n\t\t\t'template'    => 'pages/broken-taxonomy.php'\n\t\t];\n\t}\n\n\t/**\n\t * Define our properties.\n\t */\n\tpublic function register() {\n\t}\n}\n"
  },
  {
    "path": "tests/data/taxonomy-types/empty-taxonomy-type.php",
    "content": "<?php\n\nclass Empty_Taxonomy_Type extends Papi_Taxonomy_Type {\n\n\t/**\n\t * Define our Taxonomy Type meta data.\n\t *\n\t * @return array\n\t */\n\tpublic function meta() {\n\t\treturn [\n\t\t\t'name'        => 'Empty taxonomy',\n\t\t\t'description' => 'This is a empty taxonomy',\n\t\t\t'template'    => 'pages/empty-taxonomy.php'\n\t\t];\n\t}\n\n\t/**\n\t * Define our properties.\n\t */\n\tpublic function register() {\n\t}\n}\n"
  },
  {
    "path": "tests/data/taxonomy-types/faq-extra-taxonomy-type.php",
    "content": "<?php\n\nif ( ! class_exists( 'FAQ_Taxonomy_Type' ) ) {\n\trequire_once __DIR__ . '/faq-taxonomy-type.php';\n}\n\nclass FAQ_Extra_Taxonomy_Type extends FAQ_Taxonomy_Type {\n\n\t/**\n\t * Define our Taxonomy Type meta data.\n\t *\n\t * @return array\n\t */\n\tpublic function meta() {\n\t\treturn [\n\t\t\t'name'         => 'FAQ Extra taxonomy',\n\t\t\t'description'  => 'This is a faq extra taxonomy',\n\t\t\t'template'     => 'pages/faq-extra-taxonomy.php'\n\t\t];\n\t}\n\n\t/**\n\t * Define our properties.\n\t */\n\tpublic function register() {\n\t\t$this->box( 'Content', [\n\t\t\tpapi_property( [\n\t\t\t\t'description' => 'FAQ 2',\n\t\t\t\t'post_type'   => 'faq',\n\t\t\t\t'type'        => 'string',\n\t\t\t\t'title'       => 'Name'\n\t\t\t] )\n\t\t] );\n\t}\n}\n"
  },
  {
    "path": "tests/data/taxonomy-types/faq-extra2-taxonomy-type.php",
    "content": "<?php\n\nif ( ! class_exists( 'FAQ_Extra_Taxonomy_Type' ) ) {\n\trequire_once __DIR__ . '/faq-extra-taxonomy-type.php';\n}\n\nclass FAQ_Extra2_Taxonomy_Type extends FAQ_Extra_Taxonomy_Type {\n\n\t/**\n\t * Define our Taxonomy Type meta data.\n\t *\n\t * @return array\n\t */\n\tpublic function meta() {\n\t\treturn [\n\t\t\t'name'         => 'FAQ Extra2 taxonomy',\n\t\t\t'description'  => 'This is a faq extra2 taxonomy',\n\t\t\t'template'     => 'pages/faq-extra2-taxonomy.php'\n\t\t];\n\t}\n\n\t/**\n\t * Define our properties.\n\t */\n\tpublic function register() {\n\t\t$this->box( 'Content', [\n\t\t\tpapi_property( [\n\t\t\t\t'description' => 'FAQ 3',\n\t\t\t\t'post_type'   => 'faq',\n\t\t\t\t'type'        => 'image',\n\t\t\t\t'title'       => 'image'\n\t\t\t] )\n\t\t] );\n\t}\n}\n"
  },
  {
    "path": "tests/data/taxonomy-types/faq-taxonomy-type.php",
    "content": "<?php\n\nclass FAQ_Taxonomy_Type extends Papi_Taxonomy_Type {\n\n\t/**\n\t * Define our Taxonomy Type meta data.\n\t *\n\t * @return array\n\t */\n\tpublic function meta() {\n\t\treturn [\n\t\t\t'name'         => 'FAQ taxonomy',\n\t\t\t'description'  => 'This is a faq taxonomy',\n\t\t\t'template'     => 'pages/faq-taxonomy.php',\n\t\t\t'taxonomy'     => 'faq',\n\t\t\t'fill_labels'  => true\n\t\t];\n\t}\n\n\t/**\n\t * Define our properties.\n\t */\n\tpublic function register() {\n\t\t$this->box( 'Content', [\n\t\t\tpapi_property( [\n\t\t\t\t'description' => 'FAQ 1',\n\t\t\t\t'post_type'   => 'faq',\n\t\t\t\t'type'        => 'string',\n\t\t\t\t'title'       => 'Question'\n\t\t\t] ),\n\t\t\tpapi_property( [\n\t\t\t\t'description' => 'FAQ 1',\n\t\t\t\t'post_type'   => 'faq',\n\t\t\t\t'type'        => 'text',\n\t\t\t\t'title'       => 'Answer'\n\t\t\t] ),\n\t\t\tpapi_property( [\n\t\t\t\t'description' => 'FAQ 1',\n\t\t\t\t'type'        => 'text',\n\t\t\t\t'title'       => 'Type',\n\t\t\t\t'disabled'    => true,\n\t\t\t\t'display'     => false\n\t\t\t] )\n\t\t] );\n\t}\n}\n"
  },
  {
    "path": "tests/data/taxonomy-types/properties-taxonomy-type.php",
    "content": "<?php\n\nclass Properties_Taxonomy_Type extends Papi_Taxonomy_Type {\n\n\t/**\n\t * Define our Page Type meta data.\n\t *\n\t * @return array\n\t */\n\tpublic function meta() {\n\t\treturn [\n\t\t\t'name'        => 'Properties taxonomy type',\n\t\t\t'description' => 'This is a properties taxonomy type',\n\t\t\t'template'    => 'pages/properties-taxonomy.php',\n\t\t\t'taxonomy'    => 'post_tag'\n\t\t];\n\t}\n\n\t/**\n\t * Define our properties.\n\t */\n\tpublic function register() {\n\t\t$this->box( papi_template( __DIR__ . '/../page-types/boxes/properties.php' ) );\n\t}\n}\n"
  },
  {
    "path": "tests/data/taxonomy-types/simple-taxonomy-type.php",
    "content": "<?php\n\nclass Simple_Taxonomy_Type extends Papi_Taxonomy_Type {\n\n\t/**\n\t * Define our Taxonomy Type meta data.\n\t *\n\t * @return array\n\t */\n\tpublic function meta() {\n\t\treturn [\n\t\t\t'name'        => 'Simple taxonomy',\n\t\t\t'description' => 'This is a simple taxonomy',\n\t\t\t'template'    => 'pages/simple-taxonomy.php',\n\t\t\t'taxonomy'    => ['post_tag', 'category', 'test_taxonomy', 'faq'],\n\t\t\t'fill_labels' => true,\n\t\t\t'labels'      => [\n\t\t\t\t'name'          => 'Simple taxonomy',\n\t\t\t\t'singular_name' => 'Simple taxonomy'\n\t\t\t]\n\t\t];\n\t}\n\n\t/**\n\t * Hide taxonomy type for taxonomy.\n\t *\n\t * @return bool\n\t */\n\tpublic function display( $taxonomy ) {\n\t\treturn $taxonomy !== 'test_taxonomy';\n\t}\n\n\t/**\n\t * Define our properties.\n\t */\n\tpublic function register() {\n\t\t$this->box( 'Content', [\n\t\t\tpapi_property( [\n\t\t\t\t'type'  => 'string',\n\t\t\t\t'title' => 'Name',\n\t\t\t] ),\n\t\t\tpapi_property( [\n\t\t\t\t'type'  => 'editor',\n\t\t\t\t'title' => 'Text'\n\t\t\t] )\n\t\t] );\n\t}\n}\n"
  },
  {
    "path": "tests/framework/class-papi-property-test-case.php",
    "content": "<?php\n\nabstract class Papi_Property_Test_Case extends WP_UnitTestCase {\n\n\t/**\n\t * Setup test and add global properties, post id, slugs and so.\n\t */\n\tpublic function setUp() {\n\t\tparent::setUp();\n\n\t\tadd_filter( 'papi/settings/directories', function () {\n\t\t\treturn [1,  PAPI_FIXTURE_DIR . '/page-types', PAPI_FIXTURE_DIR . '/taxonomy-types'];\n\t\t} );\n\n\t\t$this->meta_type = 'post';\n\t\t$this->post_id   = $this->factory->post->create();\n\t\t$this->term_id   = $this->factory->term->create();\n\n\t\t$_GET = [];\n\t\t$_GET['post']    = $this->post_id;\n\t\t$_GET['term_id'] = $this->term_id;\n\n\t\tupdate_post_meta( $this->post_id, papi_get_page_type_key(), 'properties-page-type' );\n\t\tupdate_term_meta( $this->term_id, papi_get_page_type_key(), 'properties-taxonomy-type' );\n\n\t\t$this->entry_type = papi_get_entry_type_by_id( 'properties-page-type' );\n\t\t$this->entry_type = papi_get_entry_type_by_id( 'properties-taxonomy-type' );\n\n\t\tif ( isset( $this->slug ) && is_string( $this->slug ) ) {\n\t\t\t$this->slugs      = [$this->slug];\n\t\t\t$this->property   = $this->entry_type->get_property( $this->slug );\n\t\t\t$this->properties = [$this->property];\n\t\t} else {\n\t\t\t$slugs = $this->slugs;\n\t\t\t$this->properties = [];\n\t\t\tforeach ( $slugs as $slug ) {\n\t\t\t\t$this->properties[] = $this->entry_type->get_property( $slug );\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * Tear down all properties that has been used.\n\t */\n\tpublic function tearDown() {\n\t\tparent::tearDown();\n\t\tunset( $_GET, $_POST, $this->post_id, $this->term_id, $this->entry_type, $this->property );\n\t}\n\n\t/**\n\t * Get property value.\n\t *\n\t * @param  string $name\n\t *\n\t * @return mixed\n\t */\n\tpublic function __get( $name ) {\n\t\tif ( $name === 'property' && count( $this->properties ) > 0 ) {\n\t\t\treturn $this->properties[0];\n\t\t}\n\n\t\tif ( $name === 'slug' && count( $this->slugs ) > 0 ) {\n\t\t\treturn $this->slugs[0];\n\t\t}\n\n\t\treturn parent::__get( $name );\n\t}\n\n\t/**\n\t * Assert values will assert values so it's the same by default.\n\t */\n\tpublic function assert_values( $expected, $actual, $slug ) {\n\t\t$this->assertSame( $expected, $actual );\n\t}\n\n\t/**\n\t * Get actual value for the given slug.\n\t */\n\tabstract public function get_value();\n\n\t/**\n\t * Get expected value for the given slug.\n\t */\n\tabstract public function get_expected();\n\n\t/**\n\t * Save properties value for different types.\n\t *\n\t * @param  Papi_Property $property\n\t * @param  mixed  $value\n\t * @param  string $type\n\t */\n\tpublic function save_properties( $property, $value = null, $type = 'post' ) {\n\t\tglobal $wp_current_filter;\n\n\t\tif ( is_null( $value ) ) {\n\t\t\t$value = $this->get_value( $property->get_slug( true ) );\n\t\t}\n\n\t\t$values = papi_test_create_property_post_data( [\n\t\t\t'slug'  => $property->slug,\n\t\t\t'type'  => $property,\n\t\t\t'value' => $value\n\t\t] );\n\n\t\tforeach ( $values as $key => $value ) {\n\t\t\tif ( ! isset( $_POST[$key] ) ) {\n\t\t\t\t$_POST[$key] = $value;\n\t\t\t}\n\t\t}\n\n\t\t$_POST['papi_meta_nonce'] = wp_create_nonce( 'papi_save_data' );\n\n\t\tswitch ( papi_get_meta_type( $type ) ) {\n\t\t\tcase 'post':\n\t\t\t\t$id = $this->post_id;\n\t\t\t\t$handler = new Papi_Admin_Meta_Handler();\n\t\t\t\t$wp_current_filter = ['save_post'];\n\t\t\t\tbreak;\n\t\t\tcase 'term':\n\t\t\t\t$id = $this->term_id;\n\t\t\t\t$handler = new Papi_Admin_Meta_Handler();\n\t\t\t\t$wp_current_filter = ['create_term'];\n\t\t\t\tbreak;\n\t\t\tcase 'option':\n\t\t\t\t$_SERVER['REQUEST_METHOD'] = 'POST';\n\t\t\t\t$id = 0;\n\t\t\t\t$handler = new Papi_Admin_Option_Handler();\n\t\t\t\t$wp_current_filter = [];\n\t\t\t\tbreak;\n\t\t\tdefault:\n\t\t\t\t$id = 0;\n\t\t\t\tbreak;\n\t\t}\n\n\t\t$handler->save_properties( $id );\n\t\t$_SERVER['REQUEST_METHOD'] = '';\n\t\t$wp_current_filter = [];\n\t}\n\n\t/**\n\t * Save properties value for the different meta types.\n\t *\n\t * @param  Papi_Property $property\n\t * @param  string $type\n\t */\n\tpublic function save_properties_value( $property = null, $type = 'post' ) {\n\t\t$_GET['meta_type'] = $type;\n\n\t\t$this->meta_type = $type;\n\n\t\t// Set right entry type for meta type.\n\t\tswitch ( $type ) {\n\t\t\tcase 'option':\n\t\t\t\tglobal $current_screen;\n\n\t\t\t\t$current_screen = WP_Screen::get( 'admin_init' );\n\n\t\t\t\t$_GET['page'] = 'papi/option/options/properties-option-type';\n\t\t\t\t$_SERVER['REQUEST_URI'] = 'http://site.com/?page=papi/option/options/properties-option-type';\n\n\t\t\t\tbreak;\n\t\t\tcase 'term':\n\t\t\t\t$_GET['entry_type'] = 'properties-taxonomy-type';\n\t\t\t\tbreak;\n\t\t\tcase 'post':\n\t\t\t\t$_GET['entry_type'] = 'properties-page-type';\n\t\t\t\tbreak;\n\t\t\tdefault:\n\t\t\t\tbreak;\n\t\t}\n\n\t\t$value = $this->get_value( $property->get_slug( true ) );\n\n\t\tif ( is_null( $value ) ) {\n\t\t\tswitch ( $type ) {\n\t\t\t\tcase 'option':\n\t\t\t\t\t$actual = papi_get_option( $property->slug );\n\t\t\t\t\tbreak;\n\t\t\t\tcase 'post':\n\t\t\t\t\t$actual = papi_get_field( $this->post_id, $property->slug );\n\t\t\t\t\tbreak;\n\t\t\t\tcase 'term':\n\t\t\t\t\t$actual = papi_get_term_field( $this->term_id, $property->slug );\n\t\t\t\t\tbreak;\n\t\t\t\tdefault:\n\t\t\t\t\tbreak;\n\t\t\t}\n\t\t\t$this->assertNull( $actual );\n\t\t\treturn;\n\t\t}\n\n\t\t$this->save_properties( $property, $value, $type );\n\n\t\tswitch ( $type ) {\n\t\t\tcase 'option':\n\t\t\t\t// Leave admin screen.\n\t\t\t\t$current_screen = null;\n\t\t\t\t$actual = papi_get_option( $property->slug );\n\t\t\t\tunset( $_GET['page'] );\n\t\t\t\tbreak;\n\t\t\tcase 'post':\n\t\t\t\t$actual = papi_get_field( $this->post_id, $property->slug );\n\t\t\t\tbreak;\n\t\t\tcase 'term':\n\t\t\t\t$actual = papi_get_term_field( $this->term_id, $property->slug );\n\t\t\t\tbreak;\n\t\t\tdefault:\n\t\t\t\tbreak;\n\t\t}\n\n\t\t$expected = $this->get_expected( $property->get_slug( true ) );\n\n\t\t$this->assert_values( $expected, $actual, $property->get_slug( true ) );\n\t}\n\n\t/**\n\t * Test property convert type, by default it is `string` if isn't set to anything else.\n\t */\n\tpublic function test_property_convert_type() {\n\t\tforeach ( $this->properties as $property ) {\n\t\t\t$this->assertSame( 'string', $property->convert_type );\n\t\t}\n\t}\n\n\t/**\n\t * Test property default value, by default is `null` if it isn't set to anything else.\n\t */\n\tpublic function test_property_default_value() {\n\t\tforeach ( $this->properties as $property ) {\n\t\t\t$this->assertNull( $property->default_value );\n\t\t}\n\t}\n\n\t/**\n\t * Test property `format_value`, it will check so the\n\t * expected value is return by default.\n\t */\n\tpublic function test_property_format_value() {\n\t\t$this->assertSame( $this->get_expected(), $this->property->format_value( $this->get_value(), '', 0 ) );\n\t}\n\n\t/**\n\t * Test properties `get_default_settings` so it is a array.\n\t */\n\tpublic function test_property_get_default_settings() {\n\t\tforeach ( $this->properties as $property ) {\n\t\t\t$this->assertTrue( is_array( $property->get_default_settings() ) );\n\t\t}\n\t}\n\n\t/**\n\t * Abstract method that should test property options, if any.\n\t */\n\tabstract public function test_property_options();\n\n\t/**\n\t * Test property output will test so the html contains the property.\n\t */\n\tpublic function test_property_output() {\n\t\tforeach ( $this->properties as $property ) {\n\t\t\tpapi_render_property( $property );\n\t\t\t$this->expectOutputRegex( '/name=\\\"' . papi_get_property_type_key( $property->get_option( 'slug' ) ) . '\\\"' );\n\t\t\t$this->expectOutputRegex( '/data\\-property=\\\"' . $property->get_option( 'type' ) . '\\\"/' );\n\t\t}\n\t}\n\n\t/**\n\t * Test save properties will test to save properties as\n\t * all existing types.\n\t */\n\tpublic function test_save_properties_value() {\n\t\tforeach ( $this->properties as $prop ) {\n\t\t\t$this->save_properties_value( $prop, 'option' );\n\t\t\t$this->save_properties_value( $prop, 'post' );\n\t\t\t$this->save_properties_value( $prop, 'term' );\n\t\t}\n\n\t\t$_SERVER['REQUEST_URI'] = '';\n\t}\n\n\t/**\n\t * Get update value will return the value that will\n\t * be used as value when updating the property.\n\t *\n\t * @return mixed\n\t */\n\tpublic function get_update_value() {\n\t\treturn;\n\t}\n\n\t/**\n\t * Update type value will test to update the property value.\n\t *\n\t * @param  int    $post_id\n\t * @param  int    $index\n\t * @param  string $slug\n\t * @param  string $type\n\t */\n\tpublic function update_type_value( $post_id = 0, $index = 0, $slug = null, $type = 'post' ) {\n\t\t$property = $this->properties[$index];\n\t\t$value    = $this->get_update_value( $property->get_slug( true ) );\n\n\t\tif ( is_null( $value ) ) {\n\t\t\t$this->assertNull( papi_get_field( $this->post_id, $property->slug, null, $type ) );\n\t\t} else if ( papi_update_field( $this->post_id, $slug, $value, $type ) ) {\n\t\t\t$actual = papi_get_field( $this->post_id, $property->slug, null, $type );\n\t\t\t$this->assert_values( $value, $actual, $property->slug );\n\t\t}\n\t}\n\n\t/**\n\t * Test so values is updated right.\n\t *\n\t * @depends test_save_properties_value\n\t */\n\tpublic function test_property_update_type_value() {\n\t\tforeach ( $this->slugs as $index => $slug ) {\n\t\t\t$this->update_type_value( $this->post_id, $index, $slug );\n\t\t\t$this->update_type_value( 0, $index, $slug, 'option' );\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "tests/framework/functions.php",
    "content": "<?php\n\n/**\n * Output html for properties tests.\n */\nfunction properties_output_html() {\n\t?>\n\t<p>Hello, callable!</p>\n\t<?php\n}\n\n/**\n * Say Hello!\n */\nfunction say_hello_stub() {\n\techo 'Hello';\n}\n\n/**\n * Say Hello!\n *\n * @param string $name\n */\nfunction say_hello_name_stub( $name ) {\n\techo \"Hello $name\";\n}\n\n/**\n * Register a book post type.\n */\nfunction papi_test_register_book_post_type() {\n\tglobal $wp_post_types;\n\n\t$labels = [\n\t\t'name'               => _x( 'Books', 'post type general name', 'papi-tests' ),\n\t\t'singular_name'      => _x( 'Book', 'post type singular name', 'papi-tests' ),\n\t\t'menu_name'          => _x( 'Books', 'admin menu', 'papi-tests' ),\n\t\t'name_admin_bar'     => _x( 'Book', 'add new on admin bar', 'papi-tests' ),\n\t\t'add_new'            => _x( 'Add New', 'book', 'papi-tests' ),\n\t\t'add_new_item'       => __( 'Add New Book', 'papi-tests' ),\n\t\t'new_item'           => __( 'New Book', 'papi-tests' ),\n\t\t'edit_item'          => __( 'Edit Book', 'papi-tests' ),\n\t\t'view_item'          => __( 'View Book', 'papi-tests' ),\n\t\t'all_items'          => __( 'All Books', 'papi-tests' ),\n\t\t'search_items'       => __( 'Search Books', 'papi-tests' ),\n\t\t'parent_item_colon'  => __( 'Parent Books:', 'papi-tests' ),\n\t\t'not_found'          => __( 'No books found.', 'papi-tests' ),\n\t\t'not_found_in_trash' => __( 'No books found in Trash.', 'papi-tests' )\n\t];\n\n\t$args = [\n\t\t'labels'             => $labels,\n\t\t'public'             => true,\n\t\t'publicly_queryable' => true,\n\t\t'show_ui'            => true,\n\t\t'show_in_menu'       => true,\n\t\t'query_var'          => true,\n\t\t'rewrite'            => ['slug' => 'book'],\n\t\t'capability_type'    => 'post',\n\t\t'has_archive'        => true,\n\t\t'hierarchical'       => false,\n\t\t'menu_position'      => null,\n\t\t'supports'           => ['title', 'editor', 'author', 'thumbnail', 'excerpt', 'comments']\n\t];\n\n\t$out = register_post_type( 'book', $args );\n\n\tif ( ! isset( $wp_post_types['book'] ) ) {\n\t\t$wp_post_types['book'] = $out;\n\t}\n}\n\n/**\n * Add custom taxonomy `FAQ taxonomy`.\n */\nfunction papi_test_register_faq_taxonomy() {\n\t$labels = [\n\t\t'name'              => 'FAQ taxonomy',\n\t\t'singular_name'     => 'FAQ',\n\t\t'search_items'      => 'Search FAQs',\n\t\t'all_items'         => 'All FAQs',\n\t\t'parent_item'       => 'Parent FAQ',\n\t\t'parent_item_colon' => 'Parent FAQ:',\n\t\t'edit_item'         => 'Edit FAQ',\n\t\t'update_item'       => 'Update FAQ',\n\t\t'add_new_item'      => 'Add New FAQ',\n\t\t'new_item_name'     => 'New FAQ Name',\n\t\t'menu_name'         => 'FAQ taxonomy',\n\t\t'view_item'         => 'View FAQ'\n\t];\n\t$args = [\n\t\t'hierarchical'      => true,\n\t\t'labels'            => $labels,\n\t\t'show_ui'           => true,\n\t\t'show_admin_column' => true,\n\t\t'query_var'         => true,\n\t];\n\n\tregister_taxonomy( 'faq', ['post'], $args );\n}\n\nadd_action( 'init', 'papi_test_register_faq_taxonomy' );\n\n/**\n * Register a hidden post type.\n */\nfunction papi_test_register_hidden_post_type() {\n\tglobal $wp_post_types;\n\n\t$labels = [\n\t\t'name'               => _x( 'Hiddens', 'post type general name', 'papi-tests' ),\n\t\t'singular_name'      => _x( 'Hidden', 'post type singular name', 'papi-tests' ),\n\t\t'menu_name'          => _x( 'Hidden', 'admin menu', 'papi-tests' ),\n\t\t'name_admin_bar'     => _x( 'Hidden', 'add new on admin bar', 'papi-tests' ),\n\t\t'add_new'            => _x( 'Add New', 'hidden', 'papi-tests' ),\n\t\t'add_new_item'       => __( 'Add New Hidden', 'papi-tests' ),\n\t\t'new_item'           => __( 'New Hidden', 'papi-tests' ),\n\t\t'edit_item'          => __( 'Edit Hidden', 'papi-tests' ),\n\t\t'view_item'          => __( 'View Hidden', 'papi-tests' ),\n\t\t'all_items'          => __( 'All Hiddens', 'papi-tests' ),\n\t\t'search_items'       => __( 'Search Hidden', 'papi-tests' ),\n\t\t'parent_item_colon'  => __( 'Parent Hiddens:', 'papi-tests' ),\n\t\t'not_found'          => __( 'No hiddens found.', 'papi-tests' ),\n\t\t'not_found_in_trash' => __( 'No hiddens found in Trash.', 'papi-tests' )\n\t];\n\n\t$args = [\n\t\t'labels'             => $labels,\n\t\t'public'             => true,\n\t\t'publicly_queryable' => true,\n\t\t'show_ui'            => true,\n\t\t'show_in_menu'       => false,\n\t\t'query_var'          => true,\n\t\t'rewrite'            => ['slug' => 'hidden'],\n\t\t'capability_type'    => 'post',\n\t\t'has_archive'        => true,\n\t\t'hierarchical'       => false,\n\t\t'menu_position'      => null,\n\t\t'supports'           => ['title', 'editor', 'author', 'thumbnail', 'excerpt', 'comments']\n\t];\n\n\t$out = register_post_type( 'hidden', $args );\n\n\tif ( ! isset( $wp_post_types['hidden'] ) ) {\n\t\t$wp_post_types['hidden'] = $out;\n\t}\n}\n"
  },
  {
    "path": "tests/framework/helpers.php",
    "content": "<?php\n\n/**\n * Create property post data.\n *\n * @param array $values\n * @param mixed $post\n *\n * @return array\n */\nfunction papi_test_create_property_post_data( $values, $post = null ) {\n\t$property_type_slug = papi_html_name( papi_get_property_type_key( $values['slug'] ) );\n\n\t$data = [];\n\t$data[$values['slug']] = $values['value'];\n\n\tif ( is_object( $values['type'] ) ) {\n\t\t$property_type_options = $values['type']->get_options();\n\t} else {\n\t\t$property_type_options = $values['type'];\n\t}\n\n\t$data[$property_type_slug] = base64_encode( papi_maybe_json_encode( $property_type_options ) );\n\n\tif ( ! is_null( $post ) ) {\n\t\treturn array_merge( $post, $data );\n\t}\n\n\treturn $data;\n}\n"
  },
  {
    "path": "webpack.config.js",
    "content": "const path = require('path');\nconst webpack = require('webpack');\n\nmodule.exports = {\n  mode: process.env.NODE === 'production' ? 'production' : 'development',\n  context: path.join(__dirname, 'src/assets/js'),\n  devtool: 'source-map',\n  entry: './main.js',\n  externals: {\n    jquery: 'jQuery'\n  },\n  output: {\n    filename: './main.min.js',\n    path: path.resolve(__dirname, 'dist/js/')\n  },\n  resolve: {\n    extensions: ['.js'],\n    modules: [\n      path.join(__dirname, 'node_modules'),\n      path.join(__dirname, 'src/assets/js')\n    ]\n  },\n  module: {\n    rules: [\n      {\n        test: /\\.js$/,\n        exclude: /node_modules/,\n        loader: 'babel-loader'\n      }\n    ]\n  },\n  plugins: [new webpack.IgnorePlugin(/^\\.\\/locale$/, /moment$/)]\n};\n"
  }
]