All Projects → inpsyde → more-menu-fields

inpsyde / more-menu-fields

Licence: MIT license
Package to add more fields to WordPress menu edit screen.

Programming Languages

PHP
23972 projects - #3 most used programming language

Projects that are alternatives of or similar to more-menu-fields

Nav3D
3D Pathfinding and cover system plugin for UE4, using Sparse Voxel Octrees.
Stars: ✭ 58 (+93.33%)
Mutual labels:  navigation
itree
Interactive tree command for file system navigation
Stars: ✭ 18 (-40%)
Mutual labels:  navigation
routing-py
🌎 Python library to access all public routing, isochrones and matrix APIs in a consistent manner.
Stars: ✭ 106 (+253.33%)
Mutual labels:  navigation
Parrot
Web router specially designed for building SPAs using Meteor
Stars: ✭ 75 (+150%)
Mutual labels:  navigation
OneHtmlNav
单文件html的网络导航页面,简约并不简单
Stars: ✭ 78 (+160%)
Mutual labels:  navigation
unity-splines
Tool for making navigation bezier splines with points, events and bindings to colliders
Stars: ✭ 13 (-56.67%)
Mutual labels:  navigation
Brick
🧱 Brick - Multiplatform navigation library for Compose.
Stars: ✭ 33 (+10%)
Mutual labels:  navigation
hfos-legacy
Hackerfleet Operating System
Stars: ✭ 28 (-6.67%)
Mutual labels:  navigation
awesome-maps-ukraine
A curated list of maps of Ukraine, ukrainian mappers and tools that they use or develop for creating and publish maps
Stars: ✭ 35 (+16.67%)
Mutual labels:  navigation
micvision
Micvision package provide exploration and location for robot using navigation and cartographer packages
Stars: ✭ 21 (-30%)
Mutual labels:  navigation
uesvon
3D navmesh generation and pathfinding plugin for UnrealEngine
Stars: ✭ 165 (+450%)
Mutual labels:  navigation
Deep Visual Inertial Odometry
Deep Learning for Visual-Inertial Odometry
Stars: ✭ 31 (+3.33%)
Mutual labels:  navigation
MVVMJetpack
JectpackDemo
Stars: ✭ 37 (+23.33%)
Mutual labels:  navigation
qlevar router
Manage you project Routes. Create nested routes. Simply navigation without context to your pages. Change only one sub widget in your page when navigating to new route.
Stars: ✭ 51 (+70%)
Mutual labels:  navigation
surfacer
AI and pathfinding for 2D-platformers in Godot.
Stars: ✭ 56 (+86.67%)
Mutual labels:  navigation
react-native-mapbox-navigation
A navigation UI ready to drop into your React Native application
Stars: ✭ 86 (+186.67%)
Mutual labels:  navigation
Turtlebot Navigation
This project was completed on May 15, 2015. The goal of the project was to implement software system for frontier based exploration and navigation for turtlebot-like robots.
Stars: ✭ 28 (-6.67%)
Mutual labels:  navigation
Dual-color-Polyline-Animation
This library will help to show the polyline in dual color similar as Uber.
Stars: ✭ 73 (+143.33%)
Mutual labels:  navigation
navbar.js
Modern Navigation Component
Stars: ✭ 47 (+56.67%)
Mutual labels:  navigation
AutonomousPrecisionLanding
Precision landing on a visual target using OpenCV and dronekit-python
Stars: ✭ 31 (+3.33%)
Mutual labels:  navigation

More Menu Fields Latest Stable Version Project Status Build Status License

Package to add more fields to WordPress menu edit screen.


What / Why

WordPress provides a nice UI for editing navigation menus.

However, it is quite opinionated about the available settings for each menu item. They are:

  • "Navigation Label"
  • "Title Attribute"
  • "Open link in a new tab"
  • "CSS Classes"
  • "Link Relationship (XFN)"
  • "Description"

For our clients we needed additional fields, e.g. "data" attributes or "rel" attributes ("noopener", "nofollow"...).

Issue is WordPress <5.4.0 provides no filter to edit the default fields and there's also no action hook to allow echoing custom form fields HTML, like happens in many other parts of WP backend.

Since WordPress 5.4.0 there is a new Hook wp_nav_menu_item_custom_fields implemented which allows you to filter the current item and add custom fields.

This package exists, because we needed in WordPress <5.4.0 a way to add more fields that could work if used from more plugins. You can still use this library with newer WordPress version to work in an object oriented way on custom navigation items attributes.


Usage

First Step

Because the use target of "More Menu Fields" is to be used from plugins, it is not a plugin itself, but a package that can be required by plugins via Composer.

When the package is required via Composer and Composer autoload has been loaded, it is needed to bootstrap the package.

It can be done inside a plugin by just calling a function:

Inpsyde\MoreMenuFields\bootstrap();

There's no need to wrap the call in any hook and if called more than once (by different plugins) nothing bad will happen.

The Field Interfaces

To add more fields it is necessary to create a PHP class for each of them. The class has to implement the interface Inpsyde\MoreMenuFields\EditField which looks like this:

interface EditField {

	public function name(): string;

	public function field_markup(): string;
}

The first method, name(), has to return the field name, it can be any string, but must be unique. This will also be used later on to retrieve the value that is entered in the input field.

The second and last method, field_markup(), has to return the HTML markup for the field, as it will appear on the UI.

In the HTML markup it will very likely be necessary to use the input name, its id and its current stored value, if any. Those information can be obtained via an object of type Inpsyde\MoreMenuFields\EditFieldValue. More on this soon.

Very often (if not always) the value users enter in the generated input field needs to be sanitized before being saved. This is why the package ships another interface Inpsyde\MoreMenuFields\SanitizedEditField which looks like this:

interface SanitizedEditField extends EditField {

	public function sanitize_callback(): callable;
}

The interface extends EditField and its only method, sanitize_callback(), can be used to return a callback used to sanitize the users input. It is recommended to implement this interface to create fields and only use EditField for form input fields that don't actually take input, like buttons.

Field Class Example

Nothing is better than an example to see how things work.

Below there's a real-world example of a class that will render a checkbox to add a "nofollow" attribute on a menu item link.

namespace My\Plugin;

class NofollowField implements Inpsyde\MoreMenuFields\SanitizedEditField
{
	private $value;

	public function __construct( Inpsyde\MoreMenuFields\EditFieldValue $value )
	{
		$this->value = $value;
	}

	public function name(): string
	{
		return 'nofollow';
	}

	public function field_markup(): string
	{
		if ( ! $this->value->is_valid() ) {
			return '';
		}
		ob_start();
		?>
		<p class="<?= $this->value->form_field_class() ?>">
			<label>
				<?= esc_html__( 'Enable nofollow?', 'my-plugin' ) ?>
				<input
					type="checkbox"
					name="<?= $this->value->form_field_name() ?>"
					id="<?= $this->value->form_field_id() ?>"
					value="1"<?php checked( 1, $this->value->value() ) ?>/>
			</label>
		</p>
		<?php

		return ob_get_clean();
	}

	public function sanitize_callback(): callable {
		return 'intval';
	}
}

Quite easy. Even because many of the "hard work" used in the generation of field HTML is done by the instance of EditFieldValue that the field class receives in the constructor. But where does it come from?

Adding a Field

Just having the field class above will do nothing if the package does not know about it, and to make the package aware of the class we need to add an instance of it to the array passed by filter hook stored in the constant Inpsyde\MoreMenuFields\FILTER_FIELDS.

That filter passes to hooking callbacks the array of currently added filters, and as second argument an instance of EditFieldValueFactory: an object that can be used to obtain instances of EditFieldValue to be used in field classes.

Let's see an usage example:

add_filter(
	Inpsyde\MoreMenuFields\FILTER_FIELDS,
	function ( array $items, EditMenuFieldValueFactory $value_factory )
	{
		$fields[] = new My\Plugin\NofollowField( $value_factory->create( 'nofollow' ) );

		return $fields;
	},
	10,
	2
);

When hooking Inpsyde\MoreMenuFields\FILTER_FIELDS the passed EditMenuFieldValueFactory is used to obtain an instance of EditFieldValue that is injected in the field object (nothing more than what is shown above).

To obtain the EditFieldValue instance the create() method is called on the factory, passing to it the name of the field, that must be the exact same name returned by field object name() method.

That's it. The filter right above, plus the class in previous section is really all it takes to print the field and also save it.

The benefit of this can be seen when there are many fields added. Moreover, the Inpsyde\MoreMenuFields\FILTER_FIELDS filter can be used by many plugins that know nothing about each other and all will work just fine.

Retrieving Saved Values

At some point there will be the need to use the value stored by the added fields.

The package stores them as post meta of the related menu item post. So to retrieve them it is possible to just use get_post_meta().

The only thing needed for it is to know the meta key, which is generated by the package prepending to the return value of field object name() method a fixed prefix stored in the EditFieldValue::KEY_PREFIX class constant:

$no_follow = get_post_meta( $menu_item_id, Inpsyde\MoreMenuFields\EditFieldValue::KEY_PREFIX . 'nofollow', TRUE );

Considering this is quite verbose, the package provides a shorter way to do the exact same thing:

$no_follow = Inpsyde\MoreMenuFields\field_value( $menu_item, 'nofollow' );

For example, to make actual use of the "nofollow" field from above it is possible to do:

add_filter( 'nav_menu_link_attributes', function ( array $attributes, $item )
{
	$current_rel = $attributes[ 'rel' ] ?? '';

	if ( Inpsyde\MoreMenuFields\field_value( $item, 'nofollow' ) ) {
		$attributes[ 'rel' ] = trim( $current_rel .= ' nofollow' );
	}
	
    return $attributes;
}

Where 'nav_menu_link_attributes' filter is used to add the rel="nofollow" attribute to a menu item if the checkbox we previously added on backend is checked.

On Escaping Retrieved Value

When retrieving a value via Inpsyde\MoreMenuFields\field_value() or via (get_post_meta()) the value is not sanitized with the callback returned via field object sanitize_callback() method, which is only called to sanitize the value before saving in DB.

So the value needs to be escaped before being used. In the usage example above the obtained value is only used for a boolean check, so there's no need to escape, but in case the value is printed to page it needs to be escaped via esc_attr, esc_html or anything fits better.


About Customizer

WordPress provides a menu editing UI in the customizer. This package is not integrated there.

The reason is that many times the fields we add via this package are nothing "visual" (take as example the "nofollow" example in this readme), so having a live preview to edit them is not really helpful, so we (and our clients) never felt the need to have these fields in the customizer UI.

To whoever is interested in such a feature, we can say "maybe later", but PRs are always open ;)


Requirements

  • PHP 7+
  • Composer to install

Installation

Via Composer, package name is inpsyde/more-menu-fields.


License and Copyright

Copyright (c) 2017 Inpsyde GmbH.

"More Menu Fields" code is licensed under MIT license.

The team at Inpsyde is engineering the Web since 2006.

Note that the project description data, including the texts, logos, images, and/or trademarks, for each open source project belongs to its rightful owner. If you wish to add or remove any projects, please contact us at [email protected].