Nova_Restaurant::menu_item_loop_close_element()   A
last analyzed

Complexity

Conditions 1
Paths 1

Size

Total Lines 22

Duplication

Lines 22
Ratio 100 %

Importance

Changes 0
Metric Value
cc 1
nc 1
nop 1
dl 22
loc 22
rs 9.568
c 0
b 0
f 0
1
<?php
2
3
use Automattic\Jetpack\Assets;
4
5
/*
6
 * Put the following code in your theme's Food Menu Page Template to customize the markup of the menu.
7
8
if ( class_exists( 'Nova_Restaurant' ) ) {
9
	Nova_Restaurant::init( array(
10
		'menu_tag'               => 'section',
11
		'menu_class'             => 'menu-items',
12
		'menu_header_tag'        => 'header',
13
		'menu_header_class'      => 'menu-group-header',
14
		'menu_title_tag'         => 'h1',
15
		'menu_title_class'       => 'menu-group-title',
16
		'menu_description_tag'   => 'div',
17
		'menu_description_class' => 'menu-group-description',
18
	) );
19
}
20
21
*/
22
23
/* @todo
24
25
Bulk/Quick edit response of Menu Item rows is broken.
26
27
Drag and Drop reordering.
28
*/
29
30
class Nova_Restaurant {
31
	const MENU_ITEM_POST_TYPE = 'nova_menu_item';
32
	const MENU_ITEM_LABEL_TAX = 'nova_menu_item_label';
33
	const MENU_TAX = 'nova_menu';
34
35
	/**
36
	 * Version number used when enqueuing all resources (css and js).
37
	 *
38
	 * @var string
39
	 */
40
	public $version = '20210303';
41
42
	protected $default_menu_item_loop_markup = array(
43
		'menu_tag'               => 'section',
44
		'menu_class'             => 'menu-items',
45
		'menu_header_tag'        => 'header',
46
		'menu_header_class'      => 'menu-group-header',
47
		'menu_title_tag'         => 'h1',
48
		'menu_title_class'       => 'menu-group-title',
49
		'menu_description_tag'   => 'div',
50
		'menu_description_class' => 'menu-group-description',
51
	);
52
53
	protected $menu_item_loop_markup = array();
54
	protected $menu_item_loop_last_term_id = false;
55
	protected $menu_item_loop_current_term = false;
56
57
	static function init( $menu_item_loop_markup = array() ) {
58
		static $instance = false;
59
60
		if ( !$instance ) {
61
			$instance = new Nova_Restaurant();
62
		}
63
64
		if ( $menu_item_loop_markup ) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $menu_item_loop_markup of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
65
			$instance->menu_item_loop_markup = wp_parse_args( $menu_item_loop_markup, $instance->default_menu_item_loop_markup );
0 ignored issues
show
Documentation Bug introduced by
It seems like wp_parse_args($menu_item..._menu_item_loop_markup) can be null. However, the property $menu_item_loop_markup is declared as array. Maybe change the type of the property to array|null or add a type check?

Our type inference engine has found an assignment of a scalar value (like a string, an integer or null) to a property which is an array.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property.

To type hint that a parameter can be either an array or null, you can set a type hint of array and a default value of null. The PHP interpreter will then accept both an array or null for that parameter.

function aContainsB(array $needle = null, array  $haystack) {
    if (!$needle) {
        return false;
    }

    return array_intersect($haystack, $needle) == $haystack;
}

The function can be called with either null or an array for the parameter $needle but will only accept an array as $haystack.

Loading history...
Documentation introduced by
$instance->default_menu_item_loop_markup is of type array, but the function expects a string.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
66
		}
67
68
		return $instance;
69
	}
70
71
	function __construct() {
72
		if ( ! $this->site_supports_nova() )
73
			return;
74
75
		$this->register_taxonomies();
76
		$this->register_post_types();
77
		add_action( 'admin_menu',            array( $this, 'add_admin_menus'      ) );
78
		add_action( 'admin_enqueue_scripts', array( $this, 'enqueue_nova_styles'  ) );
79
		add_action( 'admin_head',            array( $this, 'set_custom_font_icon' ) );
80
81
		// Always sort menu items correctly
82
		add_action( 'parse_query',   array( $this, 'sort_menu_item_queries_by_menu_order'    ) );
83
		add_filter( 'posts_results', array( $this, 'sort_menu_item_queries_by_menu_taxonomy' ), 10, 2 );
84
85
		add_action( 'wp_insert_post', array( $this, 'add_post_meta' ) );
86
87
		$this->menu_item_loop_markup = $this->default_menu_item_loop_markup;
88
89
		// Only output our Menu Item Loop Markup on a real blog view.  Not feeds, XML-RPC, admin, etc.
90
		add_filter( 'template_include', array( $this, 'setup_menu_item_loop_markup__in_filter' ) );
91
92
		add_filter( 'enter_title_here',       array( $this, 'change_default_title' ) );
93
		add_filter( 'post_updated_messages',  array( $this, 'updated_messages'     ) );
94
		add_filter( 'dashboard_glance_items', array( $this, 'add_to_dashboard'     ) );
95
	}
96
97
	/**
98
	* Should this Custom Post Type be made available?
99
	*/
100
	function site_supports_nova() {
101
		// If we're on WordPress.com, and it has the menu site vertical.
102
		if ( function_exists( 'site_vertical' ) && 'nova_menu' == site_vertical() )
103
			return true;
104
105
		// Else, if the current theme requests it.
106
		if ( current_theme_supports( self::MENU_ITEM_POST_TYPE ) )
107
			return true;
108
109
		// Otherwise, say no unless something wants to filter us to say yes.
110
		/**
111
		 * Allow something else to hook in and enable this CPT.
112
		 *
113
		 * @module custom-content-types
114
		 *
115
		 * @since 2.6.0
116
		 *
117
		 * @param bool false Whether or not to enable this CPT.
118
		 * @param string $var The slug for this CPT.
119
		 */
120
		return (bool) apply_filters( 'jetpack_enable_cpt', false, self::MENU_ITEM_POST_TYPE );
0 ignored issues
show
Unused Code introduced by
The call to apply_filters() has too many arguments starting with self::MENU_ITEM_POST_TYPE.

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress.

In this case you can add the @ignore PhpDoc annotation to the duplicate definition and it will be ignored.

Loading history...
121
	}
122
123
/* Setup */
124
125
	/**
126
	 * Register Taxonomies and Post Type
127
	 */
128
	function register_taxonomies() {
129
		if ( ! taxonomy_exists( self::MENU_ITEM_LABEL_TAX ) ) {
130
			register_taxonomy( self::MENU_ITEM_LABEL_TAX, self::MENU_ITEM_POST_TYPE, array(
131
				'labels' => array(
132
					/* translators: this is about a food menu */
133
					'name'                       => __( 'Menu Item Labels', 'jetpack' ),
134
					/* translators: this is about a food menu */
135
					'singular_name'              => __( 'Menu Item Label', 'jetpack' ),
136
					/* translators: this is about a food menu */
137
					'search_items'               => __( 'Search Menu Item Labels', 'jetpack' ),
138
					'popular_items'              => __( 'Popular Labels', 'jetpack' ),
139
					/* translators: this is about a food menu */
140
					'all_items'                  => __( 'All Menu Item Labels', 'jetpack' ),
141
					/* translators: this is about a food menu */
142
					'edit_item'                  => __( 'Edit Menu Item Label', 'jetpack' ),
143
					/* translators: this is about a food menu */
144
					'view_item'                  => __( 'View Menu Item Label', 'jetpack' ),
145
					/* translators: this is about a food menu */
146
					'update_item'                => __( 'Update Menu Item Label', 'jetpack' ),
147
					/* translators: this is about a food menu */
148
					'add_new_item'               => __( 'Add New Menu Item Label', 'jetpack' ),
149
					/* translators: this is about a food menu */
150
					'new_item_name'              => __( 'New Menu Item Label Name', 'jetpack' ),
151
					'separate_items_with_commas' => __( 'For example, spicy, favorite, etc. <br /> Separate Labels with commas', 'jetpack' ),
152
					'add_or_remove_items'        => __( 'Add or remove Labels', 'jetpack' ),
153
					'choose_from_most_used'      => __( 'Choose from the most used Labels', 'jetpack' ),
154
					'items_list_navigation'      => __( 'Menu item label list navigation',   'jetpack' ),
155
					'items_list'                 => __( 'Menu item labels list',              'jetpack' ),
156
				),
157
				'no_tagcloud' => __( 'No Labels found', 'jetpack' ),
158
				'hierarchical'  => false,
159
			) );
160
		}
161
162
		if ( ! taxonomy_exists( self::MENU_TAX ) ) {
163
			register_taxonomy( self::MENU_TAX, self::MENU_ITEM_POST_TYPE, array(
164
				'labels' => array(
165
					/* translators: this is about a food menu */
166
					'name'               => __( 'Menu Sections', 'jetpack' ),
167
					/* translators: this is about a food menu */
168
					'singular_name'      => __( 'Menu Section', 'jetpack' ),
169
					/* translators: this is about a food menu */
170
					'search_items'       => __( 'Search Menu Sections', 'jetpack' ),
171
					/* translators: this is about a food menu */
172
					'all_items'          => __( 'All Menu Sections', 'jetpack' ),
173
					/* translators: this is about a food menu */
174
					'parent_item'        => __( 'Parent Menu Section', 'jetpack' ),
175
					/* translators: this is about a food menu */
176
					'parent_item_colon'  => __( 'Parent Menu Section:', 'jetpack' ),
177
					/* translators: this is about a food menu */
178
					'edit_item'          => __( 'Edit Menu Section', 'jetpack' ),
179
					/* translators: this is about a food menu */
180
					'view_item'          => __( 'View Menu Section', 'jetpack' ),
181
					/* translators: this is about a food menu */
182
					'update_item'        => __( 'Update Menu Section', 'jetpack' ),
183
					/* translators: this is about a food menu */
184
					'add_new_item'       => __( 'Add New Menu Section', 'jetpack' ),
185
					/* translators: this is about a food menu */
186
					'new_item_name'      => __( 'New Menu Sections Name', 'jetpack' ),
187
					'items_list_navigation' => __( 'Menu section list navigation',  'jetpack' ),
188
					'items_list'            => __( 'Menu section list',             'jetpack' ),
189
				),
190
				'rewrite' => array(
191
					'slug'         => 'menu',
192
					'with_front'   => false,
193
					'hierarchical' => true,
194
				),
195
				'hierarchical'  => true,
196
				'show_tagcloud' => false,
197
				'query_var'     => 'menu',
198
			) );
199
		}
200
	}
201
202
	function register_post_types() {
203
		if ( post_type_exists( self::MENU_ITEM_POST_TYPE ) ) {
204
			return;
205
		}
206
207
		register_post_type( self::MENU_ITEM_POST_TYPE, array(
208
			'description' => __( "Items on your restaurant's menu", 'jetpack' ),
209
210
			'labels' => array(
211
				/* translators: this is about a food menu */
212
				'name'               => __( 'Menu Items', 'jetpack' ),
213
				/* translators: this is about a food menu */
214
				'singular_name'      => __( 'Menu Item', 'jetpack' ),
215
				/* translators: this is about a food menu */
216
				'menu_name'          => __( 'Food Menus', 'jetpack' ),
217
				/* translators: this is about a food menu */
218
				'all_items'          => __( 'Menu Items', 'jetpack' ),
219
				/* translators: this is about a food menu */
220
				'add_new'            => __( 'Add One Item', 'jetpack' ),
221
				/* translators: this is about a food menu */
222
				'add_new_item'       => __( 'Add Menu Item', 'jetpack' ),
223
				/* translators: this is about a food menu */
224
				'edit_item'          => __( 'Edit Menu Item', 'jetpack' ),
225
				/* translators: this is about a food menu */
226
				'new_item'           => __( 'New Menu Item', 'jetpack' ),
227
				/* translators: this is about a food menu */
228
				'view_item'          => __( 'View Menu Item', 'jetpack' ),
229
				/* translators: this is about a food menu */
230
				'search_items'       => __( 'Search Menu Items', 'jetpack' ),
231
				/* translators: this is about a food menu */
232
				'not_found'          => __( 'No Menu Items found', 'jetpack' ),
233
				/* translators: this is about a food menu */
234
				'not_found_in_trash' => __( 'No Menu Items found in Trash', 'jetpack' ),
235
				'filter_items_list'     => __( 'Filter menu items list',       'jetpack' ),
236
				'items_list_navigation' => __( 'Menu item list navigation',    'jetpack' ),
237
				'items_list'            => __( 'Menu items list',              'jetpack' ),
238
			),
239
			'supports' => array(
240
				'title',
241
				'editor',
242
				'thumbnail',
243
				'excerpt',
244
			),
245
			'rewrite' => array(
246
				'slug'       => 'item',
247
				'with_front' => false,
248
				'feeds'      => false,
249
				'pages'      => false,
250
			),
251
			'register_meta_box_cb' => array( $this, 'register_menu_item_meta_boxes' ),
252
253
			'public'          => true,
254
			'show_ui'         => true, // set to false to replace with custom UI
255
			'menu_position'   => 20, // below Pages
256
			'capability_type' => 'page',
257
			'map_meta_cap'    => true,
258
			'has_archive'     => false,
259
			'query_var'       => 'item',
260
		) );
261
	}
262
263
264
	/**
265
	 * Update messages for the Menu Item admin.
266
	 */
267 View Code Duplication
	function updated_messages( $messages ) {
268
		global $post;
269
270
		$messages[self::MENU_ITEM_POST_TYPE] = array(
271
			0  => '', // Unused. Messages start at index 1.
272
				/* translators: this is about a food menu */
273
			1  => sprintf( __( 'Menu item updated. <a href="%s">View item</a>', 'jetpack' ), esc_url( get_permalink( $post->ID ) ) ),
274
			2  => esc_html__( 'Custom field updated.', 'jetpack' ),
275
			3  => esc_html__( 'Custom field deleted.', 'jetpack' ),
276
			/* translators: this is about a food menu */
277
			4  => esc_html__( 'Menu item updated.', 'jetpack' ),
278
			/* translators: %s: date and time of the revision */
279
			5  => isset( $_GET['revision'] ) ? sprintf( esc_html__( 'Menu item restored to revision from %s', 'jetpack' ), wp_post_revision_title( (int) $_GET['revision'], false ) ) : false,
280
			/* translators: this is about a food menu */
281
			6  => sprintf( __( 'Menu item published. <a href="%s">View item</a>', 'jetpack' ), esc_url( get_permalink( $post->ID ) ) ),
282
			/* translators: this is about a food menu */
283
			7  => esc_html__( 'Menu item saved.', 'jetpack' ),
284
			/* translators: this is about a food menu */
285
			8  => sprintf( __( 'Menu item submitted. <a target="_blank" href="%s">Preview item</a>', 'jetpack' ), esc_url( add_query_arg( 'preview', 'true', get_permalink( $post->ID ) ) ) ),
286
			/* translators: this is about a food menu */
287
			9  => sprintf( __( 'Menu item scheduled for: <strong>%1$s</strong>. <a target="_blank" href="%2$s">Preview item</a>', 'jetpack' ),
288
			// translators: Publish box date format, see https://php.net/date
289
			date_i18n( __( 'M j, Y @ G:i', 'jetpack' ), strtotime( $post->post_date ) ), esc_url( get_permalink($post->ID) ) ),
290
			/* translators: this is about a food menu */
291
			10 => sprintf( __( 'Menu item draft updated. <a target="_blank" href="%s">Preview item</a>', 'jetpack' ), esc_url( add_query_arg( 'preview', 'true', get_permalink( $post->ID ) ) ) ),
292
		);
293
294
		return $messages;
295
	}
296
297
298
	/**
299
	 * Nova Styles and Scripts
300
	 */
301
	function enqueue_nova_styles( $hook ) {
302
		global $post_type;
303
		$pages = array( 'edit.php', 'post.php', 'post-new.php' );
304
305
		if ( in_array( $hook, $pages ) && $post_type == self::MENU_ITEM_POST_TYPE ) {
306
			wp_enqueue_style( 'nova-style', plugins_url( 'css/nova.css', __FILE__ ),      array(), $this->version );
307
		}
308
309
		wp_enqueue_style( 'nova-font',  plugins_url( 'css/nova-font.css', __FILE__ ), array(), $this->version );
310
	}
311
312
313
	/**
314
	 * Change ‘Enter Title Here’ text for the Menu Item.
315
	 */
316
	function change_default_title( $title ) {
317
		if ( self::MENU_ITEM_POST_TYPE == get_post_type() ) {
318
			/* translators: this is about a food menu */
319
			$title = esc_html__( "Enter the menu item's name here", 'jetpack' );
320
		}
321
322
		return $title;
323
	}
324
325
326
	/**
327
	 * Add to Dashboard At A Glance
328
	 */
329
	function add_to_dashboard() {
330
		$number_menu_items = wp_count_posts( self::MENU_ITEM_POST_TYPE );
331
332
		if ( current_user_can( 'administrator' ) ) {
333
			$number_menu_items_published = sprintf(
334
				'<a href="%1$s">%2$s</a>',
335
				esc_url(
336
					get_admin_url(
337
						get_current_blog_id(),
338
						'edit.php?post_type=' . self::MENU_ITEM_POST_TYPE
339
					)
340
				),
341
				sprintf(
342
					/* translators: Placehoder is a number of items. */
343
					_n(
344
						'%1$d Food Menu Item',
345
						'%1$d Food Menu Items',
346
						(int) $number_menu_items->publish,
347
						'jetpack'
348
					),
349
					number_format_i18n( $number_menu_items->publish )
350
				)
351
			);
352
		} else {
353
			$number_menu_items_published = sprintf(
354
				'<span>%1$s</span>',
355
				sprintf(
356
					/* translators: Placehoder is a number of items. */
357
					_n(
358
						'%1$d Food Menu Item',
359
						'%1$d Food Menu Items',
360
						(int) $number_menu_items->publish,
361
						'jetpack'
362
					),
363
					number_format_i18n( $number_menu_items->publish )
364
				)
365
			);
366
		}
367
368
		echo '<li class="nova-menu-count">' . $number_menu_items_published . '</li>';
369
	}
370
371
372
	/**
373
	 * Query
374
	 */
375
	function is_menu_item_query( $query ) {
376
		if (
377
			( isset( $query->query_vars['taxonomy'] ) && self::MENU_TAX == $query->query_vars['taxonomy'] )
378
		||
379
			( isset( $query->query_vars['post_type'] ) && self::MENU_ITEM_POST_TYPE == $query->query_vars['post_type'] )
380
		) {
381
			return true;
382
		}
383
384
		return false;
385
	}
386
387
	function sort_menu_item_queries_by_menu_order( $query ) {
388
		if ( ! $this->is_menu_item_query( $query ) ) {
389
			return;
390
		}
391
392
		$query->query_vars['orderby'] = 'menu_order';
393
		$query->query_vars['order'] = 'ASC';
394
395
		// For now, just turn off paging so we can sort by taxonmy later
396
		// If we want paging in the future, we'll need to add the taxonomy sort here (or at least before the DB query is made)
397
		$query->query_vars['posts_per_page'] = -1;
398
	}
399
400
	function sort_menu_item_queries_by_menu_taxonomy( $posts, $query ) {
401
		if ( !$posts ) {
402
			return $posts;
403
		}
404
405
		if ( !$this->is_menu_item_query( $query ) ) {
406
			return $posts;
407
		}
408
409
		$grouped_by_term = array();
410
411
		foreach ( $posts as $post ) {
412
			$term = $this->get_menu_item_menu_leaf( $post->ID );
413
			if ( !$term || is_wp_error( $term ) ) {
414
				$term_id = 0;
415
			} else {
416
				$term_id = $term->term_id;
417
			}
418
419
			if ( !isset( $grouped_by_term["$term_id"] ) ) {
420
				$grouped_by_term["$term_id"] = array();
421
			}
422
423
			$grouped_by_term["$term_id"][] = $post;
424
		}
425
426
		$term_order = get_option( 'nova_menu_order', array() );
427
428
		$return = array();
429 View Code Duplication
		foreach ( $term_order as $term_id ) {
430
			if ( isset( $grouped_by_term["$term_id"] ) ) {
431
				$return = array_merge( $return, $grouped_by_term["$term_id"] );
432
				unset( $grouped_by_term["$term_id"] );
433
			}
434
		}
435
436
		foreach ( $grouped_by_term as $term_id => $posts ) {
437
			$return = array_merge( $return, $posts );
438
		}
439
440
		return $return;
441
	}
442
443
444
	/**
445
	 * Add Many Items
446
	 */
447
	function add_admin_menus() {
448
		$hook = add_submenu_page(
449
			'edit.php?post_type=' . self::MENU_ITEM_POST_TYPE,
450
			__( 'Add Many Items', 'jetpack' ),
451
			__( 'Add Many Items', 'jetpack' ),
452
			'edit_pages',
453
			'add_many_nova_items',
454
			array( $this, 'add_many_new_items_page' )
455
		);
456
457
		add_action( "load-$hook",     array( $this, 'add_many_new_items_page_load' ) );
458
459
		add_action( 'current_screen', array( $this, 'current_screen_load' ) );
460
461
		//Adjust 'Add Many Items' submenu position
462
		if ( isset( $GLOBALS['submenu']['edit.php?post_type=' . self::MENU_ITEM_POST_TYPE] ) ) {
463
			$submenu_item = array_pop( $GLOBALS['submenu']['edit.php?post_type=' . self::MENU_ITEM_POST_TYPE] );
464
			$GLOBALS['submenu']['edit.php?post_type=' . self::MENU_ITEM_POST_TYPE][11] = $submenu_item;
465
			ksort( $GLOBALS['submenu']['edit.php?post_type=' . self::MENU_ITEM_POST_TYPE] );
466
		}
467
468
469
		$this->setup_menu_item_columns();
470
471
		wp_register_script(
472
			'nova-menu-checkboxes',
473
			Assets::get_file_url_for_environment(
474
				'_inc/build/custom-post-types/js/menu-checkboxes.min.js',
475
				'modules/custom-post-types/js/menu-checkboxes.js'
476
			),
477
			array( 'jquery' ),
478
			$this->version,
479
			true
480
		);
481
	}
482
483
484
	/**
485
	 * Custom Nova Icon CSS
486
	 */
487
	function set_custom_font_icon() {
488
	?>
489
	<style type="text/css">
490
	#menu-posts-nova_menu_item .wp-menu-image:before {
491
		font-family: 'nova-font' !important;
492
		content: '\e603' !important;
493
	}
494
	</style>
495
	<?php
496
	}
497
498
	function current_screen_load() {
499
		$screen = get_current_screen();
500
		if ( 'edit-nova_menu_item' !== $screen->id ) {
501
			return;
502
		}
503
504
		$this->edit_menu_items_page_load();
505
		add_filter( 'admin_notices', array( $this, 'admin_notices' ) );
506
	}
507
508
/* Edit Items List */
509
510
	function admin_notices() {
511
		if ( isset( $_GET['nova_reordered'] ) )
512
			/* translators: this is about a food menu */
513
			printf( '<div class="updated"><p>%s</p></div>', __( 'Menu Items re-ordered.', 'jetpack' ) );
514
	}
515
516
	function no_title_sorting( $columns ) {
517
		if ( isset( $columns['title'] ) )
518
			unset( $columns['title'] );
519
		return $columns;
520
	}
521
522
	function setup_menu_item_columns() {
523
		add_filter( sprintf( 'manage_edit-%s_sortable_columns', self::MENU_ITEM_POST_TYPE ), array( $this, 'no_title_sorting' ) );
524
		add_filter( sprintf( 'manage_%s_posts_columns', self::MENU_ITEM_POST_TYPE ), array( $this, 'menu_item_columns' ) );
525
526
		add_action( sprintf( 'manage_%s_posts_custom_column', self::MENU_ITEM_POST_TYPE ), array( $this, 'menu_item_column_callback' ), 10, 2 );
527
	}
528
529
	function menu_item_columns( $columns ) {
530
		unset( $columns['date'], $columns['likes'] );
531
532
		$columns['thumbnail'] = __( 'Thumbnail', 'jetpack' );
533
		$columns['labels']    = __( 'Labels',    'jetpack' );
534
		$columns['price']     = __( 'Price',     'jetpack' );
535
		$columns['order']     = __( 'Order',     'jetpack' );
536
537
		return $columns;
538
	}
539
540
	function menu_item_column_callback( $column, $post_id ) {
541
		$screen = get_current_screen();
542
543
		switch ( $column ) {
544
			case 'thumbnail':
545
				echo get_the_post_thumbnail( $post_id, array( 50, 50 ) );
546
				break;
547
			case 'labels' :
548
				$this->list_admin_labels( $post_id );
549
				break;
550
			case 'price' :
551
				$this->display_price( $post_id );
552
				break;
553
			case 'order' :
554
				$url = admin_url( $screen->parent_file );
555
556
				$up_url = add_query_arg( array(
557
					'action' => 'move-item-up',
558
					'post_id' => (int) $post_id,
559
				), wp_nonce_url( $url, 'nova_move_item_up_' . $post_id ) );
560
561
				$down_url = add_query_arg( array(
562
					'action' => 'move-item-down',
563
					'post_id' => (int) $post_id,
564
				), wp_nonce_url( $url, 'nova_move_item_down_' . $post_id ) );
565
				$menu_item = get_post($post_id);
566
				$this->get_menu_by_post_id( $post_id );
567
				if ( $term_id = $this->get_menu_by_post_id( $post_id ) ) {
568
					$term_id = $term_id->term_id;
569
				}
570
	?>
571
				<input type="hidden" class="menu-order-value" name="nova_order[<?php echo (int) $post_id ?>]" value="<?php echo esc_attr( $menu_item->menu_order ) ?>" />
572
				<input type="hidden" class='nova-menu-term' name="nova_menu_term[<?php echo (int) $post_id ?>]" value="<?php echo esc_attr( $term_id ); ?>">
573
574
				<span class="hide-if-js">
575
				&nbsp; &nbsp; &mdash; <a class="nova-move-item-up" data-post-id="<?php echo (int) $post_id; ?>" href="<?php echo esc_url( $up_url ); ?>">up</a>
576
				<br />
577
				&nbsp; &nbsp; &mdash; <a class="nova-move-item-down" data-post-id="<?php echo (int) $post_id; ?>" href="<?php echo esc_url( $down_url ); ?>">down</a>
578
				</span>
579
	<?php
580
				break;
581
		}
582
	}
583
584
	function get_menu_by_post_id( $post_id = null ) {
585
		if ( ! $post_id )
586
			return false;
587
588
		$terms = get_the_terms( $post_id, self::MENU_TAX );
589
590
		if ( ! is_array( $terms ) )
591
			return false;
592
593
		return array_pop( $terms );
594
	}
595
596
	/**
597
	 * Fires on a menu edit page. We might have drag-n-drop reordered
598
	 */
599
	function maybe_reorder_menu_items() {
600
		// make sure we clicked our button
601
		if ( ! ( isset( $_REQUEST['menu_reorder_submit'] ) && $_REQUEST['menu_reorder_submit'] === __( 'Save New Order', 'jetpack' ) ) )
602
			return;
603
		;
604
605
		// make sure we have the nonce
606
		if ( ! ( isset( $_REQUEST['drag-drop-reorder'] ) && wp_verify_nonce( $_REQUEST['drag-drop-reorder'], 'drag-drop-reorder' ) ) )
607
			return;
608
609
		$term_pairs = array_map( 'absint', $_REQUEST['nova_menu_term'] );
610
		$order_pairs = array_map( 'absint', $_REQUEST['nova_order'] );
611
612
		foreach( $order_pairs as $ID => $menu_order ) {
613
			$ID = absint( $ID );
614
			unset( $order_pairs[$ID] );
615
			if ( $ID < 0 )
616
				continue;
617
618
			$post = get_post( $ID );
619
			if ( ! $post )
620
				continue;
621
622
			// save a write if the order hasn't changed
623
			if ( $menu_order != $post->menu_order )
624
				wp_update_post( compact( 'ID', 'menu_order' ) );
625
626
			// save a write if the term hasn't changed
627
			if ( $term_pairs[$ID] != $this->get_menu_by_post_id( $ID )->term_id )
628
				wp_set_object_terms( $ID, $term_pairs[$ID], self::MENU_TAX );
629
630
		}
631
632
		$redirect = add_query_arg( array(
633
			'post_type' => self::MENU_ITEM_POST_TYPE,
634
			'nova_reordered' => '1'
635
		), admin_url( 'edit.php' ) );
636
		wp_safe_redirect( $redirect );
637
		exit;
638
639
	}
640
641
	function edit_menu_items_page_load() {
642
		if ( isset( $_GET['action'] ) ) {
643
			$this->handle_menu_item_actions();
644
		}
645
646
		$this->maybe_reorder_menu_items();
647
648
		wp_enqueue_script(
649
			'nova-drag-drop',
650
			Assets::get_file_url_for_environment(
651
				'_inc/build/custom-post-types/js/nova-drag-drop.min.js',
652
				'modules/custom-post-types/js/nova-drag-drop.js'
653
			),
654
			array( 'jquery-ui-sortable' ),
655
			$this->version,
656
			true
657
		);
658
659
		wp_localize_script( 'nova-drag-drop', '_novaDragDrop', array(
660
			'nonce'       => wp_create_nonce( 'drag-drop-reorder' ),
661
			'nonceName'   => 'drag-drop-reorder',
662
			'reorder'     => __( 'Save New Order', 'jetpack' ),
663
			'reorderName' => 'menu_reorder_submit'
664
		) );
665
		add_action( 'the_post', array( $this, 'show_menu_titles_in_menu_item_list' ) );
666
	}
667
668
	function handle_menu_item_actions() {
669
		$action = (string) $_GET['action'];
670
671
		switch ( $action ) {
672
		case 'move-item-up' :
673
		case 'move-item-down' :
674
			$reorder = false;
675
676
			$post_id = (int) $_GET['post_id'];
677
678
			$term = $this->get_menu_item_menu_leaf( $post_id );
679
680
			// Get all posts in that term
681
			$query = new WP_Query( array(
682
				'taxonomy' => self::MENU_TAX,
683
				'term'     => $term->slug,
684
			) );
685
686
			$order = array();
687
			foreach ( $query->posts as $post ) {
688
				$order[] = $post->ID;
689
			}
690
691
			if ( 'move-item-up' == $action ) {
692
				check_admin_referer( 'nova_move_item_up_' . $post_id );
693
694
				$first_post_id = $order[0];
695
				if ( $post_id == $first_post_id ) {
696
					break;
697
				}
698
699
				foreach ( $order as $menu_order => $order_post_id ) {
700
					if ( $post_id != $order_post_id ) {
701
						continue;
702
					}
703
704
					$swap_post_id = $order[$menu_order - 1];
705
					$order[$menu_order - 1] = $post_id;
706
					$order[$menu_order] = $swap_post_id;
707
708
					$reorder = true;
709
					break;
710
				}
711 View Code Duplication
			} else {
712
				check_admin_referer( 'nova_move_item_down_' . $post_id );
713
714
				$last_post_id = end( $order );
715
				if ( $post_id == $last_post_id ) {
716
					break;
717
				}
718
719
				foreach ( $order as $menu_order => $order_post_id ) {
720
					if ( $post_id != $order_post_id ) {
721
						continue;
722
					}
723
724
					$swap_post_id = $order[$menu_order + 1];
725
					$order[$menu_order + 1] = $post_id;
726
					$order[$menu_order] = $swap_post_id;
727
728
					$reorder = true;
729
				}
730
			}
731
732
			if ( $reorder ) {
733
				foreach ( $order as $menu_order => $ID ) {
734
					wp_update_post( compact( 'ID', 'menu_order' ) );
735
				}
736
			}
737
738
			break;
739
		case 'move-menu-up' :
740
		case 'move-menu-down' :
741
			$reorder = false;
742
743
			$term_id = (int) $_GET['term_id'];
744
745
			$terms = $this->get_menus();
746
747
			$order = array();
748
			foreach ( $terms as $term ) {
749
				$order[] = $term->term_id;
750
			}
751
752
			if ( 'move-menu-up' == $action ) {
753
				check_admin_referer( 'nova_move_menu_up_' . $term_id );
754
755
				$first_term_id = $order[0];
756
				if ( $term_id == $first_term_id ) {
757
					break;
758
				}
759
760
				foreach ( $order as $menu_order => $order_term_id ) {
761
					if ( $term_id != $order_term_id ) {
762
						continue;
763
					}
764
765
					$swap_term_id = $order[$menu_order - 1];
766
					$order[$menu_order - 1] = $term_id;
767
					$order[$menu_order] = $swap_term_id;
768
769
					$reorder = true;
770
					break;
771
				}
772 View Code Duplication
			} else {
773
				check_admin_referer( 'nova_move_menu_down_' . $term_id );
774
775
				$last_term_id = end( $order );
776
				if ( $term_id == $last_term_id ) {
777
					break;
778
				}
779
780
				foreach ( $order as $menu_order => $order_term_id ) {
781
					if ( $term_id != $order_term_id ) {
782
						continue;
783
					}
784
785
					$swap_term_id = $order[$menu_order + 1];
786
					$order[$menu_order + 1] = $term_id;
787
					$order[$menu_order] = $swap_term_id;
788
789
					$reorder = true;
790
				}
791
			}
792
793
			if ( $reorder ) {
794
				update_option( 'nova_menu_order', $order );
795
			}
796
797
			break;
798
		default :
799
			return;
800
		}
801
802
		$redirect = add_query_arg( array(
803
			'post_type' => self::MENU_ITEM_POST_TYPE,
804
			'nova_reordered' => '1'
805
		), admin_url( 'edit.php' ) );
806
		wp_safe_redirect( $redirect );
807
		exit;
808
	}
809
810
	/*
811
	 * Add menu title rows to the list table
812
	 */
813
	function show_menu_titles_in_menu_item_list( $post ) {
814
		global $wp_list_table;
815
816
		static $last_term_id = false;
817
818
		$term = $this->get_menu_item_menu_leaf( $post->ID );
819
820
		$term_id = $term instanceof WP_Term ? $term->term_id : null;
0 ignored issues
show
Bug introduced by
The class WP_Term does not exist. Did you forget a USE statement, or did you not list all dependencies?

This error could be the result of:

1. Missing dependencies

PHP Analyzer uses your composer.json file (if available) to determine the dependencies of your project and to determine all the available classes and functions. It expects the composer.json to be in the root folder of your repository.

Are you sure this class is defined by one of your dependencies, or did you maybe not list a dependency in either the require or require-dev section?

2. Missing use statement

PHP does not complain about undefined classes in ìnstanceof checks. For example, the following PHP code will work perfectly fine:

if ($x instanceof DoesNotExist) {
    // Do something.
}

If you have not tested against this specific condition, such errors might go unnoticed.

Loading history...
821
822
		if ( false !== $last_term_id && $last_term_id === $term_id ) {
823
			return;
824
		}
825
826
		if ( is_null( $term_id ) ) {
827
			$last_term_id = null;
828
			$term_name = '';
829
			$parent_count = 0;
830
		} else {
831
			$last_term_id = $term->term_id;
832
			$term_name = $term->name;
833
			$parent_count = 0;
834
			$current_term = $term;
835
			while ( $current_term->parent ) {
836
				$parent_count++;
837
				$current_term = get_term( $current_term->parent, self::MENU_TAX );
838
			}
839
		}
840
841
		$non_order_column_count = $wp_list_table->get_column_count() - 1;
842
843
		$screen = get_current_screen();
844
845
		$url = admin_url( $screen->parent_file );
846
847
		$up_url = add_query_arg( array(
848
			'action'  => 'move-menu-up',
849
			'term_id' => (int) $term_id,
850
		), wp_nonce_url( $url, 'nova_move_menu_up_' . $term_id ) );
851
852
		$down_url = add_query_arg( array(
853
			'action'  => 'move-menu-down',
854
			'term_id' => (int) $term_id,
855
		), wp_nonce_url( $url, 'nova_move_menu_down_' . $term_id ) );
856
857
?>
858
		<tr class="no-items menu-label-row" data-term_id="<?php echo esc_attr( $term_id ) ?>">
859
			<td class="colspanchange" colspan="<?php echo (int) $non_order_column_count; ?>">
860
				<h3><?php
861
					echo str_repeat( ' &mdash; ', (int) $parent_count );
862
863
					if ( $term instanceof WP_Term ) {
0 ignored issues
show
Bug introduced by
The class WP_Term does not exist. Did you forget a USE statement, or did you not list all dependencies?

This error could be the result of:

1. Missing dependencies

PHP Analyzer uses your composer.json file (if available) to determine the dependencies of your project and to determine all the available classes and functions. It expects the composer.json to be in the root folder of your repository.

Are you sure this class is defined by one of your dependencies, or did you maybe not list a dependency in either the require or require-dev section?

2. Missing use statement

PHP does not complain about undefined classes in ìnstanceof checks. For example, the following PHP code will work perfectly fine:

if ($x instanceof DoesNotExist) {
    // Do something.
}

If you have not tested against this specific condition, such errors might go unnoticed.

Loading history...
864
						echo esc_html( sanitize_term_field( 'name', $term_name, $term_id, self::MENU_TAX, 'display' ) );
865
						edit_term_link( __( 'edit', 'jetpack' ), '<span class="edit-nova-section"><span class="dashicon dashicon-edit"></span>', '</span>', $term );
866
867
					} else {
868
						_e( 'Uncategorized' , 'jetpack' );
869
					}
870
				?></h3>
871
			</td>
872
			<td>
873
				<?php if ( $term instanceof WP_Term ) { ?>
0 ignored issues
show
Bug introduced by
The class WP_Term does not exist. Did you forget a USE statement, or did you not list all dependencies?

This error could be the result of:

1. Missing dependencies

PHP Analyzer uses your composer.json file (if available) to determine the dependencies of your project and to determine all the available classes and functions. It expects the composer.json to be in the root folder of your repository.

Are you sure this class is defined by one of your dependencies, or did you maybe not list a dependency in either the require or require-dev section?

2. Missing use statement

PHP does not complain about undefined classes in ìnstanceof checks. For example, the following PHP code will work perfectly fine:

if ($x instanceof DoesNotExist) {
    // Do something.
}

If you have not tested against this specific condition, such errors might go unnoticed.

Loading history...
874
				<a class="nova-move-menu-up" title="<?php esc_attr_e( 'Move menu section up', 'jetpack' ); ?>" href="<?php echo esc_url( $up_url ); ?>"><?php esc_html_e( 'UP', 'jetpack' ); ?></a>
875
				<br />
876
				<a class="nova-move-menu-down" title="<?php esc_attr_e( 'Move menu section down', 'jetpack' ); ?>" href="<?php echo esc_url( $down_url ); ?>"><?php esc_html_e( 'DOWN', 'jetpack' ); ?></a>
877
				<?php } ?>
878
			</td>
879
		</tr>
880
<?php
881
	}
882
883
/* Edit Many Items */
884
885
	function add_many_new_items_page_load() {
886
		if ( 'POST' === strtoupper( $_SERVER['REQUEST_METHOD'] ) ) {
887
			$this->process_form_request();
888
			exit;
889
		}
890
891
		$this->enqueue_many_items_scripts();
892
	}
893
894
	function enqueue_many_items_scripts() {
895
		wp_enqueue_script(
896
			'nova-many-items',
897
			Assets::get_file_url_for_environment(
898
				'_inc/build/custom-post-types/js/many-items.min.js',
899
				'modules/custom-post-types/js/many-items.js'
900
			),
901
			array( 'jquery' ),
902
			$this->version,
903
			true
904
		);
905
	}
906
907
	function process_form_request() {
908
		if ( !isset( $_POST['nova_title'] ) || !is_array( $_POST['nova_title'] ) ) {
909
			return;
910
		}
911
912
		$is_ajax = !empty( $_POST['ajax'] );
913
914
		if ( $is_ajax ) {
915
			check_ajax_referer( 'nova_many_items' );
916
		} else {
917
			check_admin_referer( 'nova_many_items' );
918
		}
919
920
		foreach ( array_keys( $_POST['nova_title'] ) as $key ) :
921
			// $_POST is already slashed
922
			$post_details = array(
923
				'post_status'  => 'publish',
924
				'post_type'    => self::MENU_ITEM_POST_TYPE,
925
				'post_content' => $_POST['nova_content'][$key],
926
				'post_title'   => $_POST['nova_title'][$key],
927
				'tax_input'    => array(
928
					self::MENU_ITEM_LABEL_TAX => $_POST['nova_labels'][$key],
929
					self::MENU_TAX            => isset( $_POST['nova_menu_tax'] ) ? $_POST['nova_menu_tax'] : null,
930
				),
931
			);
932
933
			$post_id = wp_insert_post( $post_details );
934
			if ( !$post_id || is_wp_error( $post_id ) ) {
935
				continue;
936
			}
937
938
			$this->set_price( $post_id, isset( $_POST['nova_price'][$key] ) ? stripslashes( $_POST['nova_price'][$key] ) : '' );
939
940
			if ( $is_ajax ) :
941
				$post = get_post( $post_id );
942
				$GLOBALS['post'] = $post;
943
				setup_postdata( $post );
944
945
?>
946
			<td><?php the_title(); ?></td>
947
			<td class="nova-price"><?php $this->display_price(); ?></td>
948
			<td><?php $this->list_labels( $post_id ); ?></td>
949
			<td><?php the_content(); ?></td>
950
<?php
951
			endif;
952
953
		endforeach;
954
955
		if ( $is_ajax ) {
956
			exit;
957
		}
958
959
		wp_safe_redirect( admin_url( 'edit.php?post_type=' . self::MENU_ITEM_POST_TYPE ) );
960
		exit;
961
	}
962
963
	function add_many_new_items_page() {
964
?>
965
	<div class="wrap">
966
		<h2><?php esc_html_e( 'Add Many Items', 'jetpack' ); ?></h2>
967
968
		<p><?php _e( 'Use the <kbd>TAB</kbd> key on your keyboard to move between colums and the <kbd>ENTER</kbd> or <kbd>RETURN</kbd> key to save each row and move on to the next.', 'jetpack' ); ?></p>
969
970
		<form method="post" action="" enctype="multipart/form-data">
971
			<p><h3><?php esc_html_e( 'Add to section:', 'jetpack' ); ?> <?php wp_dropdown_categories( array(
972
				'id'           => 'nova-menu-tax',
973
				'name'         => 'nova_menu_tax',
974
				'taxonomy'     => self::MENU_TAX,
975
				'hide_empty'   => false,
976
				'hierarchical' => true,
977
			) ); ?></h3></p>
978
979
			<table class="many-items-table wp-list-table widefat">
980
				<thead>
981
					<tr>
982
						<th scope="col"><?php esc_html_e( 'Name', 'jetpack' ); ?></th>
983
						<th scope="col" class="nova-price"><?php esc_html_e( 'Price', 'jetpack' ); ?></th>
984
						<th scope="col"><?php _e( 'Labels: <small>spicy, favorite, etc. <em>Separate Labels with commas</em></small>', 'jetpack' ); ?></th>
985
						<th scope="col"><?php esc_html_e( 'Description', 'jetpack' ); ?></th>
986
					</tr>
987
				</thead>
988
				<tbody>
989
					<tr>
990
						<td><input type="text" name="nova_title[]" aria-required="true" /></td>
991
						<td class="nova-price"><input type="text" name="nova_price[]" /></td>
992
						<td><input type="text" name="nova_labels[]" /></td>
993
						<td><textarea name="nova_content[]" cols="20" rows="1"></textarea>
994
					</tr>
995
				</tbody>
996
				<tbody>
997
					<tr>
998
						<td><input type="text" name="nova_title[]" aria-required="true" /></td>
999
						<td class="nova-price"><input type="text" name="nova_price[]" /></td>
1000
						<td><input type="text" name="nova_labels[]" /></td>
1001
						<td><textarea name="nova_content[]" cols="20" rows="1"></textarea>
1002
					</tr>
1003
				</tbody>
1004
				<tfoot>
1005
					<tr>
1006
						<th><a class="button button-secondary nova-new-row"><span class="dashicon dashicon-plus"></span> <?php esc_html_e( 'New Row' , 'jetpack' ); ?></a></th>
1007
						<th class="nova-price"></th>
1008
						<th></th>
1009
						<th></th>
1010
					</tr>
1011
				</tfoot>
1012
			</table>
1013
1014
			<p class="submit">
1015
				<input type="submit" class="button-primary" value="<?php esc_attr_e( 'Add These New Menu Items', 'jetpack' ); ?>" />
1016
				<?php wp_nonce_field( 'nova_many_items' ); ?>
1017
			</p>
1018
		</form>
1019
	</div>
1020
<?php
1021
	}
1022
1023
/* Edit One Item */
1024
1025
	function register_menu_item_meta_boxes() {
1026
		wp_enqueue_script( 'nova-menu-checkboxes' );
1027
1028
		add_meta_box( 'menu_item_price', __( 'Price', 'jetpack' ), array( $this, 'menu_item_price_meta_box' ), null, 'side', 'high' );
1029
	}
1030
1031
	function menu_item_price_meta_box( $post, $meta_box ) {
1032
		$price = $this->get_price( $post->ID );
1033
?>
1034
	<label for="nova-price-<?php echo (int) $post->ID; ?>" class="screen-reader-text"><?php esc_html_e( 'Price', 'jetpack' ); ?></label>
1035
	<input type="text" id="nova-price-<?php echo (int) $post->ID; ?>" class="widefat" name="nova_price[<?php echo (int) $post->ID; ?>]" value="<?php echo esc_attr( $price ); ?>" />
1036
<?php
1037
	}
1038
1039
	function add_post_meta( $post_id ) {
1040
		if ( !isset( $_POST['nova_price'][$post_id] ) ) {
1041
			return;
1042
		}
1043
1044
		$this->set_price( $post_id, stripslashes( $_POST['nova_price'][$post_id] ) );
1045
	}
1046
1047
/* Data */
1048
1049
	function get_menus( $args = array() ) {
1050
		$args = wp_parse_args( $args, array(
0 ignored issues
show
Documentation introduced by
array('hide_empty' => false) is of type array<string,false,{"hide_empty":"false"}>, but the function expects a string.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
1051
			'hide_empty' => false,
1052
		) );
1053
1054
		$terms = get_terms( self::MENU_TAX, $args );
1055
		if ( !$terms || is_wp_error( $terms ) ) {
1056
			return array();
1057
		}
1058
1059
		$terms_by_id = array();
1060
		foreach ( $terms as $term ) {
1061
			$terms_by_id["{$term->term_id}"] = $term;
1062
		}
1063
1064
		$term_order = get_option( 'nova_menu_order', array() );
1065
1066
		$return = array();
1067 View Code Duplication
		foreach ( $term_order as $term_id ) {
1068
			if ( isset( $terms_by_id["$term_id"] ) ) {
1069
				$return[] = $terms_by_id["$term_id"];
1070
				unset( $terms_by_id["$term_id"] );
1071
			}
1072
		}
1073
1074
		foreach ( $terms_by_id as $term_id => $term ) {
1075
			$return[] = $term;
1076
		}
1077
1078
		return $return;
1079
	}
1080
1081
	function get_menu_item_menu_leaf( $post_id ) {
1082
		// Get first menu taxonomy "leaf"
1083
		$term_ids = wp_get_object_terms( $post_id, self::MENU_TAX, array( 'fields' => 'ids' ) );
1084
1085
		foreach ( $term_ids as $term_id ) {
1086
			$children = get_term_children( $term_id, self::MENU_TAX );
1087
			if ( ! $children ) {
1088
				break;
1089
			}
1090
		}
1091
1092
		if ( ! isset( $term_id ) ) {
1093
			return false;
1094
		}
1095
1096
		return get_term( $term_id, self::MENU_TAX );
1097
1098
	}
1099
1100
	function list_labels( $post_id = 0 ) {
1101
		$post = get_post( $post_id );
1102
		echo get_the_term_list( $post->ID, self::MENU_ITEM_LABEL_TAX, '', _x( ', ', 'Nova label separator', 'jetpack' ), '' );
1103
	}
1104
1105
	function list_admin_labels( $post_id = 0 ) {
1106
		$post = get_post( $post_id );
1107
		$labels = get_the_terms( $post->ID, self::MENU_ITEM_LABEL_TAX );
1108
		if ( !empty( $labels ) ) {
1109
			$out = array();
1110
			foreach ( $labels as $label ) {
1111
				$out[] = sprintf( '<a href="%s">%s</a>',
1112
					esc_url( add_query_arg( array(
1113
						'post_type' => self::MENU_ITEM_POST_TYPE,
1114
						'taxonomy'  => self::MENU_ITEM_LABEL_TAX,
1115
						'term'      => $label->slug
1116
					), 'edit.php' ) ),
1117
					esc_html( sanitize_term_field( 'name', $label->name, $label->term_id, self::MENU_ITEM_LABEL_TAX, 'display' ) )
1118
				);
1119
			}
1120
1121
			echo join( _x( ', ', 'Nova label separator', 'jetpack' ), $out );
1122
		} else {
1123
			esc_html_e( 'No Labels', 'jetpack' );
1124
		}
1125
	}
1126
1127
	function set_price( $post_id = 0, $price = '' ) {
1128
		$post = get_post( $post_id );
1129
1130
		return update_post_meta( $post->ID, 'nova_price', $price );
1131
	}
1132
1133
	function get_price( $post_id = 0 ) {
1134
		$post = get_post( $post_id );
1135
1136
		return get_post_meta( $post->ID, 'nova_price', true );
1137
	}
1138
1139
	function display_price( $post_id = 0 ) {
1140
		echo esc_html( $this->get_price( $post_id ) );
1141
	}
1142
1143
/* Menu Item Loop Markup */
1144
1145
	/* Does not support nested loops */
1146
1147
	function get_menu_item_loop_markup( $field = null ) {
1148
		return $this->menu_item_loop_markup;
1149
	}
1150
1151
	/**
1152
	 * Sets up the loop markup.
1153
	 * Attached to the 'template_include' *filter*,
1154
	 * which fires only during a real blog view (not in admin, feeds, etc.)
1155
	 *
1156
	 * @param string Template File
1157
	 * @return string Template File.  VERY Important.
1158
	 */
1159
	function setup_menu_item_loop_markup__in_filter( $template ) {
1160
		add_action( 'loop_start', array( $this, 'start_menu_item_loop' ) );
1161
1162
		return $template;
1163
	}
1164
1165
	/**
1166
	 * If the Query is a Menu Item Query, start outputing the Menu Item Loop Marku
1167
	 * Attached to the 'loop_start' action.
1168
	 *
1169
	 * @param WP_Query
1170
	 */
1171
	function start_menu_item_loop( $query ) {
1172
		if ( !$this->is_menu_item_query( $query ) ) {
1173
			return;
1174
		}
1175
1176
		$this->menu_item_loop_last_term_id = false;
1177
		$this->menu_item_loop_current_term = false;
1178
1179
		add_action( 'the_post', array( $this, 'menu_item_loop_each_post' ) );
1180
		add_action( 'loop_end', array( $this, 'stop_menu_item_loop' ) );
1181
	}
1182
1183
	/**
1184
	 * Outputs the Menu Item Loop Marku
1185
	 * Attached to the 'the_post' action.
1186
	 *
1187
	 * @param WP_Post
1188
	 */
1189
	function menu_item_loop_each_post( $post ) {
1190
		$this->menu_item_loop_current_term = $this->get_menu_item_menu_leaf( $post->ID );
1191
1192
		if ( false === $this->menu_item_loop_last_term_id ) {
1193
			// We're at the very beginning of the loop
1194
1195
			$this->menu_item_loop_open_element( 'menu' ); // Start a new menu section
1196
			$this->menu_item_loop_header(); // Output the menu's header
1197
		} elseif ( $this->menu_item_loop_last_term_id != $this->menu_item_loop_current_term->term_id ) {
1198
			// We're not at the very beginning but still need to start a new menu section.  End the previous menu section first.
1199
1200
			$this->menu_item_loop_close_element( 'menu' ); // End the previous menu section
1201
			$this->menu_item_loop_open_element( 'menu' ); // Start a new menu section
1202
			$this->menu_item_loop_header(); // Output the menu's header
1203
		}
1204
1205
		$this->menu_item_loop_last_term_id = $this->menu_item_loop_current_term->term_id;
1206
	}
1207
1208
	/**
1209
	 * If the Query is a Menu Item Query, stop outputing the Menu Item Loop Marku
1210
	 * Attached to the 'loop_end' action.
1211
	 *
1212
	 * @param WP_Query
1213
	 */
1214
	function stop_menu_item_loop( $query ) {
1215
		if ( !$this->is_menu_item_query( $query ) ) {
1216
			return;
1217
		}
1218
1219
		remove_action( 'the_post', array( $this, 'menu_item_loop_each_post' ) );
1220
		remove_action( 'loop_start', array( $this, 'start_menu_item_loop' ) );
1221
		remove_action( 'loop_end', array( $this, 'stop_menu_item_loop' ) );
1222
1223
		$this->menu_item_loop_close_element( 'menu' ); // End the last menu section
1224
	}
1225
1226
	/**
1227
	 * Outputs the Menu Group Header
1228
	 */
1229
	function menu_item_loop_header() {
1230
		$this->menu_item_loop_open_element( 'menu_header' );
1231
			$this->menu_item_loop_open_element( 'menu_title' );
1232
				echo esc_html( $this->menu_item_loop_current_term->name ); // @todo tax filter
1233
			$this->menu_item_loop_close_element( 'menu_title' );
1234
		if ( $this->menu_item_loop_current_term->description ) :
1235
			$this->menu_item_loop_open_element( 'menu_description' );
1236
				echo esc_html( $this->menu_item_loop_current_term->description ); // @todo kses, tax filter
1237
			$this->menu_item_loop_close_element( 'menu_description' );
1238
		endif;
1239
		$this->menu_item_loop_close_element( 'menu_header' );
1240
	}
1241
1242
	/**
1243
	 * Outputs a Menu Item Markup element opening tag
1244
	 *
1245
	 * @param string $field - Menu Item Markup settings field.
1246
	 */
1247 View Code Duplication
	function menu_item_loop_open_element( $field ) {
1248
		$markup = $this->get_menu_item_loop_markup();
1249
		/**
1250
		 * Filter a menu item's element opening tag.
1251
		 *
1252
		 * @module custom-content-types
1253
		 *
1254
		 * @since 4.4.0
1255
		 *
1256
		 * @param string       $tag    Menu item's element opening tag.
1257
		 * @param string       $field  Menu Item Markup settings field.
1258
		 * @param array        $markup Array of markup elements for the menu item.
1259
		 * @param false|object $term   Taxonomy term for current menu item.
1260
		 */
1261
		echo apply_filters(
1262
			'jetpack_nova_menu_item_loop_open_element',
1263
			'<' . tag_escape( $markup["{$field}_tag"] ) . $this->menu_item_loop_class( $markup["{$field}_class"] ) . ">\n",
1264
			$field,
0 ignored issues
show
Unused Code introduced by
The call to apply_filters() has too many arguments starting with $field.

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress.

In this case you can add the @ignore PhpDoc annotation to the duplicate definition and it will be ignored.

Loading history...
1265
			$markup,
1266
			$this->menu_item_loop_current_term
1267
		);
1268
	}
1269
1270
	/**
1271
	 * Outputs a Menu Item Markup element closing tag
1272
	 *
1273
	 * @param string $field - Menu Item Markup settings field
1274
	 */
1275 View Code Duplication
	function menu_item_loop_close_element( $field ) {
1276
		$markup = $this->get_menu_item_loop_markup();
1277
		/**
1278
		 * Filter a menu item's element closing tag.
1279
		 *
1280
		 * @module custom-content-types
1281
		 *
1282
		 * @since 4.4.0
1283
		 *
1284
		 * @param string       $tag    Menu item's element closing tag.
1285
		 * @param string       $field  Menu Item Markup settings field.
1286
		 * @param array        $markup Array of markup elements for the menu item.
1287
		 * @param false|object $term   Taxonomy term for current menu item.
1288
		 */
1289
		echo apply_filters(
1290
			'jetpack_nova_menu_item_loop_close_element',
1291
			'</' . tag_escape( $markup["{$field}_tag"] ) . ">\n",
1292
			$field,
0 ignored issues
show
Unused Code introduced by
The call to apply_filters() has too many arguments starting with $field.

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress.

In this case you can add the @ignore PhpDoc annotation to the duplicate definition and it will be ignored.

Loading history...
1293
			$markup,
1294
			$this->menu_item_loop_current_term
1295
		);
1296
	}
1297
1298
	/**
1299
	 * Returns a Menu Item Markup element's class attribute.
1300
	 *
1301
	 * @param  string $class Class name.
1302
	 * @return string HTML   class attribute with leading whitespace.
1303
	 */
1304
	function menu_item_loop_class( $class ) {
1305
		if ( ! $class ) {
1306
			return '';
1307
		}
1308
1309
		/**
1310
		 * Filter a menu Item Markup element's class attribute.
1311
		 *
1312
		 * @module custom-content-types
1313
		 *
1314
		 * @since 4.4.0
1315
		 *
1316
		 * @param string       $tag    Menu Item Markup element's class attribute.
1317
		 * @param string       $class  Menu Item Class name.
1318
		 * @param false|object $term   Taxonomy term for current menu item.
1319
		 */
1320
		return apply_filters(
1321
			'jetpack_nova_menu_item_loop_class',
1322
			' class="' . esc_attr( $class ) . '"',
1323
			$class,
0 ignored issues
show
Unused Code introduced by
The call to apply_filters() has too many arguments starting with $class.

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress.

In this case you can add the @ignore PhpDoc annotation to the duplicate definition and it will be ignored.

Loading history...
1324
			$this->menu_item_loop_current_term
1325
		);
1326
	}
1327
}
1328
1329
add_action( 'init', array( 'Nova_Restaurant', 'init' ) );
1330