├── .travis.yml ├── LICENSE ├── composer.json ├── inc └── bootstrap.php └── src ├── EditField.php ├── EditFieldValue.php ├── EditFieldValueFactory.php ├── EditFields.php ├── EditWalker.php └── SanitizedEditField.php /.travis.yml: -------------------------------------------------------------------------------- 1 | language: php 2 | 3 | php: 4 | - 7.0 5 | - 7.1 6 | - nightly 7 | 8 | sudo: false 9 | 10 | matrix: 11 | allow_failures: 12 | - php: nightly 13 | 14 | before_install: 15 | - composer self-update 16 | 17 | install: 18 | - composer install 19 | 20 | script: 21 | - vendor/bin/phpunit -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2017 Inpsyde GmbH 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is furnished 8 | to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in all 11 | copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | THE SOFTWARE. 20 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "inpsyde/more-menu-fields", 3 | "description": "Package to add more fields to WordPress menu edit screen.", 4 | "type": "library", 5 | "license": "MIT", 6 | "authors": [ 7 | { 8 | "name": "Inpsyde GmbH", 9 | "email": "hello@inpsyde.com", 10 | "homepage": "http://inpsyde.com", 11 | "role": "Company" 12 | }, 13 | { 14 | "name": "Giuseppe Mazzapica", 15 | "email": "g.mazzapica@inpsyde.com", 16 | "role": "Developer" 17 | }, 18 | { 19 | "name": "Christian Brückner", 20 | "email": "c.brueckner@inpsyde.com", 21 | "role": "Developer" 22 | } 23 | ], 24 | "minimum-stability": "stable", 25 | "require": { 26 | "php": ">=7" 27 | }, 28 | "require-dev": { 29 | "phpunit/phpunit": "6.3.*", 30 | "doctrine/instantiator": "~1.0.5", 31 | "brain/monkey": "^2.1" 32 | }, 33 | "autoload": { 34 | "psr-4": { 35 | "Inpsyde\\MoreMenuFields\\": "src/" 36 | }, 37 | "files": [ 38 | "inc/bootstrap.php" 39 | ] 40 | }, 41 | "autoload-dev": { 42 | "psr-4": { 43 | "Inpsyde\\MoreMenuFields\\Tests\\": "tests/src/" 44 | } 45 | }, 46 | "config": { 47 | "optimize-autoloader": true 48 | }, 49 | "extra": { 50 | "branch-alias": { 51 | "dev-master": "0.x-dev" 52 | } 53 | } 54 | } -------------------------------------------------------------------------------- /inc/bootstrap.php: -------------------------------------------------------------------------------- 1 | all_fields( (int) $db_id ); 42 | $factory = new EditFieldValueFactory( (int) $db_id ); 43 | foreach ( $fields as $field ) { 44 | $sanitize = $field instanceof SanitizedEditField ? $field->sanitize_callback() : null; 45 | $value = $factory->create( $field->name(), $sanitize ); 46 | $value->is_valid() and $value->save(); 47 | } 48 | }, 10, 2 ); 49 | } 50 | 51 | /** 52 | * @param int|\WP_Post $menu_item 53 | * @param string $key 54 | * 55 | * @return mixed|null 56 | */ 57 | function field_value( $menu_item, string $key ) { 58 | 59 | $post = get_post( $menu_item ); 60 | 61 | if ( ! $post instanceof \WP_Post || ! is_nav_menu_item( $post ) ) { 62 | return null; 63 | } 64 | 65 | return get_post_meta( $post->ID, EditFieldValue::KEY_PREFIX . $key, TRUE ); 66 | } -------------------------------------------------------------------------------- /src/EditField.php: -------------------------------------------------------------------------------- 1 | meta_key = self::KEY_PREFIX . $key; 44 | $this->item_id = $item_id && is_nav_menu_item( $item_id ) ? $item_id : 0; 45 | $this->sanitize_callback = $sanitize_callback; 46 | } 47 | 48 | /** 49 | * @return bool 50 | */ 51 | public function is_valid(): bool { 52 | 53 | return $this->item_id > 0; 54 | } 55 | 56 | /** 57 | * @return string 58 | */ 59 | public function form_field_name(): string { 60 | 61 | return "{$this->meta_key}[{$this->item_id}]"; 62 | } 63 | 64 | /** 65 | * @return string 66 | */ 67 | public function form_field_id(): string { 68 | 69 | return "{$this->meta_key}-{$this->item_id}"; 70 | } 71 | 72 | /** 73 | * @return string 74 | */ 75 | public function form_field_class(): string { 76 | 77 | $class = sanitize_html_class( substr( $this->meta_key, strlen( self::KEY_PREFIX ) ) ); 78 | 79 | return "field-{$class} description description-wide"; 80 | } 81 | 82 | /** 83 | * @return int 84 | */ 85 | public function item_id(): int { 86 | 87 | return $this->item_id; 88 | } 89 | 90 | /** 91 | * @return mixed 92 | */ 93 | public function value() { 94 | 95 | $value = get_post_meta( $this->item_id, $this->meta_key, TRUE ); 96 | $this->sanitize_callback and $value = ( $this->sanitize_callback )( $value ); 97 | 98 | return $value; 99 | } 100 | 101 | /** 102 | * @return bool 103 | */ 104 | public function save(): bool { 105 | 106 | if ( ! doing_action( 'wp_update_nav_menu_item' ) ) { 107 | return FALSE; 108 | } 109 | 110 | $values = filter_input( INPUT_POST, $this->meta_key, FILTER_UNSAFE_RAW, FILTER_FORCE_ARRAY ); 111 | if ( ! is_array( $values ) ) { 112 | $values = filter_input( INPUT_GET, $this->meta_key, FILTER_UNSAFE_RAW, FILTER_FORCE_ARRAY ); 113 | } 114 | 115 | $valid = is_array( $values ) && array_key_exists( $this->item_id, $values ); 116 | $value = $valid ? $values[ $this->item_id ] : null; 117 | $now = $this->value(); 118 | 119 | $this->sanitize_callback and $value = ( $this->sanitize_callback )( $value ); 120 | 121 | if ( $value === $now ) { 122 | return TRUE; 123 | } 124 | 125 | if ( $value ) { 126 | return (bool) update_post_meta( $this->item_id, $this->meta_key, $value ); 127 | } elseif ( $now ) { 128 | return (bool) delete_post_meta( $this->item_id, $this->meta_key ); 129 | } 130 | 131 | return TRUE; 132 | 133 | } 134 | } -------------------------------------------------------------------------------- /src/EditFieldValueFactory.php: -------------------------------------------------------------------------------- 1 | item_id = $item_id; 30 | } 31 | 32 | /** 33 | * @param string $key 34 | * @param callable|null $sanitize_callback 35 | * 36 | * @return EditFieldValue 37 | */ 38 | public function create( string $key, callable $sanitize_callback = null ): EditFieldValue { 39 | 40 | return new EditFieldValue( $key, $this->item_id, $sanitize_callback ); 41 | 42 | } 43 | } -------------------------------------------------------------------------------- /src/EditFields.php: -------------------------------------------------------------------------------- 1 | ID ) ) { 29 | parent::start_el( $output, $item, $depth, $args, $id ); 30 | 31 | return; 32 | } 33 | 34 | $fields = ( new EditFields() )->all_fields( (int) $item->ID ); 35 | 36 | if ( ! $fields ) { 37 | parent::start_el( $output, $item, $depth, $args, $id ); 38 | 39 | return; 40 | } 41 | 42 | $el = ''; 43 | parent::start_el( $el, $item, $depth, $args, $id ); 44 | 45 | $dom = new \DOMDocument; 46 | $dom->loadXML( "{$el}" ); // append , or invalid HTML will cause issues, will be removed later 47 | 48 | $item_id = (int) $item->ID; 49 | $nodes = $dom->getElementsByTagName( 'div' ); 50 | 51 | if ( ! $nodes->length ) { 52 | $output .= $el; 53 | 54 | return; 55 | } 56 | 57 | $fieldset = $target_node = null; 58 | 59 | /** @var \DOMElement $node */ 60 | foreach ( $nodes as $node ) { 61 | if ( $node->getAttribute( 'id' ) === "menu-item-settings-{$item_id}" ) { 62 | $fieldsets = $node->getElementsByTagName( 'fieldset' ); 63 | $fieldsets->length and $fieldset = $fieldsets->item( 0 ); 64 | $target_node = $node; 65 | break; 66 | } 67 | } 68 | 69 | if ( ! $fieldset ) { 70 | $output .= $el; 71 | 72 | return; 73 | } 74 | 75 | $fragment = $dom->createDocumentFragment(); 76 | foreach ( $fields as $field ) { 77 | $fragment->appendXML( $field->field_markup() ); 78 | } 79 | 80 | $target_node->insertBefore( $fragment, $fieldset ); 81 | 82 | $output .= substr( trim( $dom->saveHTML() ), 0, - 5 ); // remove at the end 83 | } 84 | } -------------------------------------------------------------------------------- /src/SanitizedEditField.php: -------------------------------------------------------------------------------- 1 |