image_size_1 = '24x24'; $this->image_size_2 = '36x36'; $this->image_size_3 = '48x48'; $image_parts_1 = explode('x', $this->image_size_1); $image_parts_2 = explode('x', $this->image_size_2); $image_parts_3 = explode('x', $this->image_size_3); /** * Self provided image sizes for most menu usage. * * @var array */ $this->image_sizes = array( 'menu-' . $this->image_size_1 => array( $image_parts_1[0], $image_parts_1[1], false ), 'menu-' . $this->image_size_2 => array( $image_parts_2[0], $image_parts_2[1], false ), 'menu-' . $this->image_size_3 => array( $image_parts_3[0], $image_parts_3[1], false ), ); // Actions. add_action( 'init', array( $this, 'menu_image_init' ) ); add_action( 'save_post_nav_menu_item', array( $this, 'menu_image_save_post_action' ), 10, 3 ); add_action( 'admin_head-nav-menus.php', array( $this, 'menu_image_admin_head_nav_menus_action' ) ); add_action( 'wp_enqueue_scripts', array( $this, 'menu_image_add_inline_style_action' ) ); add_action( 'admin_action_delete-menu-item-image', array( $this, 'menu_image_delete_menu_item_image_action' ) ); add_action( 'wp_ajax_set-menu-item-thumbnail', array( $this, 'wp_ajax_set_menu_item_thumbnail' ) ); // Add support of WPML menus sync. add_action( 'wp_update_nav_menu_item', array( $this, 'wp_update_nav_menu_item_action' ), 10, 2 ); add_action( 'admin_init', array( $this, 'admin_init' ), 99 ); // Filters. add_filter( 'wp_setup_nav_menu_item', array( $this, 'menu_image_wp_setup_nav_menu_item' ) ); add_filter( 'nav_menu_link_attributes', array( $this, 'menu_image_nav_menu_link_attributes_filter' ), 10, 4 ); add_filter( 'manage_nav-menus_columns', array( $this, 'menu_image_nav_menu_manage_columns' ), 11 ); add_filter( 'nav_menu_item_title', array( $this, 'menu_image_nav_menu_item_title_filter' ), 10, 4 ); add_filter( 'the_title', array( $this, 'menu_image_nav_menu_item_title_filter' ), 10, 4 ); // Add support for additional image types. add_filter( 'file_is_displayable_image', array( $this, 'file_is_displayable_image' ), 10, 2 ); add_filter( 'jetpack_photon_override_image_downsize', array( $this, 'jetpack_photon_override_image_downsize_filter' ), 10, 2 ); add_filter( 'wp_get_attachment_image_attributes', array( $this, 'wp_get_attachment_image_attributes' ), 99, 3 ); // Add support for Max Megamenu. if ( function_exists( 'max_mega_menu_is_enabled' ) ) { add_filter( 'megamenu_nav_menu_link_attributes', array( $this, 'menu_image_nav_menu_link_attributes_filter' ), 10, 3 ); add_filter( 'megamenu_the_title', array( $this, 'menu_image_nav_menu_item_title_filter' ), 10, 2 ); } } /** * Admin init action with lowest execution priority */ public function admin_init() { // Add custom field for menu edit walker if ( ! has_action( 'wp_nav_menu_item_custom_fields' ) ) { add_filter( 'wp_edit_nav_menu_walker', array( $this, 'menu_image_edit_nav_menu_walker_filter' ) ); } add_action( 'wp_nav_menu_item_custom_fields', array( $this, 'menu_item_custom_fields' ), 10, 4 ); } /** * Filter adds additional validation for image type * * @param bool $result * @param string $path * * @return bool */ public function file_is_displayable_image( $result, $path ) { if ( $result ) { return true; } $fileExtension = pathinfo( $path, PATHINFO_EXTENSION ); return in_array( $fileExtension, $this->additionalDisplayableImageExtensions ); } /** * Validade Options form submission. */ public function handle_options_form(){ if( ! isset( $_POST['menu_image_form'] ) || ! wp_verify_nonce( $_POST['menu_image_form'], 'menu_image_options_update' ) ) { ?>

image_sizes = apply_filters( 'menu_image_default_sizes', $this->image_sizes ); load_plugin_textdomain( 'bzoago', false, basename( dirname( __FILE__ ) ) . '/languages' ); } /** * Adding images as screen options. * * If not checked screen option 'image', uploading form not showed. * * @param array $columns * * @return array */ public function menu_image_nav_menu_manage_columns( $columns ) { return $columns + array( 'image' => __( 'Image', 'bzoago' ) ); } /** * Saving post action. * * Saving uploaded images and attach/detach to image post type. * * @param int $post_id * @param WP_Post $post */ public function menu_image_save_post_action( $post_id, $post ) { $menu_image_settings = array( 'menu_item_image_size', 'menu_item_image_title_position', ); foreach ( $menu_image_settings as $setting_name ) { if ( isset( $_POST[ $setting_name ][ $post_id ] ) && ! empty( $_POST[ $setting_name ][ $post_id ] ) ) { if ( $post->{"_$setting_name"} != $_POST[ $setting_name ][ $post_id ] ) { update_post_meta( $post_id, "_$setting_name", esc_sql( $_POST[ $setting_name ][ $post_id ] ) ); } } } } /** * Save item settings while WPML sync menus. * * @param $item_menu_id * @param $menu_item_db_id */ public function wp_update_nav_menu_item_action( $item_menu_id, $menu_item_db_id ) { global $sitepress, $icl_menus_sync; if ( class_exists( 'SitePress' ) && $sitepress instanceof SitePress && class_exists( 'ICLMenusSync' ) && $icl_menus_sync instanceof ICLMenusSync ) { static $run_times = array(); $menu_image_settings = array( 'menu_item_image_size', 'menu_item_image_title_position', 'thumbnail_id', 'thumbnail_hover_id', ); // iterate synchronized menus. foreach ( $icl_menus_sync->menus as $menu_id => $menu_data ) { if ( ! isset( $_POST[ 'sync' ][ 'add' ][ $menu_id ] ) ) { continue; } // remove cache and get language current item menu. $cache_key = md5( serialize( array( $item_menu_id, 'tax_nav_menu' ) ) ); $cache_group = 'get_language_for_element'; wp_cache_delete( $cache_key, $cache_group ); $lang = $sitepress->get_language_for_element( $item_menu_id, 'tax_nav_menu' ); if ( ! isset( $run_times[ $menu_id ][ $lang ] ) ) { $run_times[ $menu_id ][ $lang ] = 0; } // Count static var for each menu id and saved item language // and get original item id from counted position of synchronized // items from POST data. That's all magic. $post_item_ids = array(); foreach ( $_POST['sync']['add'][ $menu_id ] as $id => $lang_array ) { if ( array_key_exists( $lang, $lang_array ) ) { $post_item_ids[ ] = $id; } } if ( ! array_key_exists( $run_times[ $menu_id ][ $lang ], $post_item_ids ) ) { continue; } $orig_item_id = $post_item_ids[ $run_times[ $menu_id ][ $lang ] ]; // iterate all item settings and save it for new item. $orig_item_meta = get_metadata( 'post', $orig_item_id ); foreach ( $menu_image_settings as $meta ) { if ( isset( $orig_item_meta[ "_$meta" ] ) && isset( $orig_item_meta[ "_$meta" ][0] ) ) { update_post_meta( $menu_item_db_id, "_$meta", $orig_item_meta[ "_$meta" ][0] ); } } $run_times[ $menu_id ][ $lang ] ++; break; } } } /** * Replacement edit menu walker class. * * @return string */ public function menu_image_edit_nav_menu_walker_filter() { return 'BZOTech_Menu_Image_Walker_Nav_Menu_Edit'; } /** * Load menu image meta for each menu item. * * @since 2.0 */ public function menu_image_wp_setup_nav_menu_item( $item ) { if ( ! isset( $item->thumbnail_id ) ) { $item->thumbnail_id = get_post_thumbnail_id( $item->ID ); } if ( ! isset( $item->thumbnail_hover_id ) && $item->thumbnail_id > 0 ) { $item->thumbnail_hover_id = get_post_meta( $item->ID, '_thumbnail_hover_id', true ); } if ( ! isset( $item->image_size ) && $item->thumbnail_id > 0 ) { $item->image_size = get_post_meta( $item->ID, '_menu_item_image_size', true ); } if ( ! isset( $item->title_position ) && $item->thumbnail_id > 0 ) { $item->title_position = get_post_meta( $item->ID, '_menu_item_image_title_position', true ); } return $item; } /** * Filters the HTML attributes applied to a menu item's anchor element. * * @param array $atts { * The HTML attributes applied to the menu item's `` element, empty strings are ignored. * * @type string $title Title attribute. * @type string $target Target attribute. * @type string $rel The rel attribute. * @type string $href The href attribute. * } * @param WP_Post $item The current menu item. * @param stdClass $args An object of wp_nav_menu() arguments. * @param int $depth Depth of menu item. Used for padding. * * @return array Link attributes. */ public function menu_image_nav_menu_link_attributes_filter( $atts, $item, $args, $depth = null ) { if ( '' !== $item->thumbnail_id && $item->thumbnail_id > 0 ) { $this->setProcessed( $item->ID ); $position = $item->title_position ? $item->title_position : apply_filters( 'menu_image_default_title_position', 'after' ); $class = ! empty( $atts['class'] ) ? $atts['class'] : ''; $class .= " menu-image-title-{$position}"; if ( $item->thumbnail_hover_id ) { $class .= ' menu-image-hovered'; } elseif ( $item->thumbnail_id ) { $class .= ' menu-image-not-hovered'; } // Fix dropdown menu for Flatsome theme. if ( ! empty( $args->walker ) && class_exists( 'FlatsomeNavDropdown' ) && $args->walker instanceof FlatsomeNavDropdown && ! is_null( $depth ) && $depth === 0 ) { $class .= ' nav-top-link'; } $atts['class'] = trim( $class ); } return $atts; } /** * Replacement default menu item output. * * @param string $title Default item output * @param object $item Menu item data object. * @param int $depth Depth of menu item. Used for padding. * @param object $args * * @return string */ public function menu_image_nav_menu_item_title_filter( $title, $item = null, $depth = null, $args = null ) { if ( strpos( $title, 'menu-image' ) > 0 || ! is_nav_menu_item( $item ) || ! isset( $item ) ) { return $title; } if ( is_numeric( $item ) && $item < 0 ) { return $title; } if ( is_numeric( $item ) && $item > 0 ) { $item = wp_setup_nav_menu_item( get_post( $item ) ); } // Process only if there is an menu image associated with the menu item. if ( '' !== $item->thumbnail_id && $item->thumbnail_id > 0 ) { $image_size = $item->image_size ? $item->image_size : apply_filters( 'menu_image_default_size', 'menu-36x36' ); $position = $item->title_position ? $item->title_position : apply_filters( 'menu_image_default_title_position', 'after' ); $class = "menu-image-title-{$position}"; $this->setUsedAttachments( $image_size, $item->thumbnail_id ); $image = ''; if ( $item->thumbnail_hover_id ) { $this->setUsedAttachments( $image_size, $item->thumbnail_hover_id ); $hover_image_src = wp_get_attachment_image_src( $item->thumbnail_hover_id, $image_size ); $margin_size = $hover_image_src[1]; $image = ""; $image .= wp_get_attachment_image( $item->thumbnail_id, $image_size, false, "class=menu-image {$class}" ); $image .= wp_get_attachment_image( $item->thumbnail_hover_id, $image_size, false, array( 'class' => "hovered-image {$class}", 'style' => "margin-left: -{$margin_size}px;", ) ); $image .= ''; } elseif ( $item->thumbnail_id ) { $image = wp_get_attachment_image( $item->thumbnail_id, $image_size, false, "class=menu-image {$class}" ); } $none = ''; // Sugar. $image = apply_filters( 'menu_image_img_html', $image ); $class .= ' menu-image-title'; switch ( $position ) { case 'hide': case 'before': case 'above': $item_args = array( $none, $class, $title, $image ); break; case 'after': default: $item_args = array( $image, $class, $title, $none ); break; } $title = vsprintf( '%s%s%s', $item_args ); } return $title; } /** * Replacement default menu item output. * * @param string $item_output Default item output. * @param object $item Menu item data object. * @param int $depth Depth of menu item. Used for padding. * @param object $args * * @return string */ public function menu_image_nav_menu_item_filter( $item_output, $item, $depth, $args ) { if ( $this->isProcessed( $item->ID ) ) { return $item_output; } if ( '' !== $item->thumbnail_id && $item->thumbnail_id > 0 ) { $attributes = ! empty( $item->attr_title ) ? ' title="' . esc_attr( $item->attr_title ) . '"' : ''; $attributes .= ! empty( $item->target ) ? ' target="' . esc_attr( $item->target ) . '"' : ''; $attributes .= ! empty( $item->xfn ) ? ' rel="' . esc_attr( $item->xfn ) . '"' : ''; $attributes .= ! empty( $item->url ) ? ' href="' . esc_attr( $item->url ) . '"' : ''; $attributes_array = shortcode_parse_atts( $attributes ); $image_size = $item->image_size ? $item->image_size : apply_filters( 'menu_image_default_size', 'menu-36x36' ); $position = $item->title_position ? $item->title_position : apply_filters( 'menu_image_default_title_position', 'after' ); $class = "menu-image-title-{$position}"; $this->setUsedAttachments( $image_size, $item->thumbnail_id ); $image = ''; if ( $item->thumbnail_hover_id ) { $this->setUsedAttachments( $image_size, $item->thumbnail_hover_id ); $hover_image_src = wp_get_attachment_image_src( $item->thumbnail_hover_id, $image_size ); $margin_size = $hover_image_src[1]; $image = ""; $image .= wp_get_attachment_image( $item->thumbnail_id, $image_size, false, "class=menu-image {$class}" ); $image .= wp_get_attachment_image( $item->thumbnail_hover_id, $image_size, false, array( 'class' => "hovered-image {$class}", 'style' => "margin-left: -{$margin_size}px;", ) ); $image .= ''; $class .= ' menu-image-hovered'; } elseif ( $item->thumbnail_id ) { $image = wp_get_attachment_image( $item->thumbnail_id, $image_size, false, "class=menu-image {$class}" ); $class .= ' menu-image-not-hovered'; } $attributes_array['class'] = $class; /** * Filter the menu link attributes. * * @since 2.6.7 * * @param array $attributes An array of attributes. * @param object $item Menu item data object. * @param int $depth Depth of menu item. Used for padding. * @param object $args */ $attributes_array = apply_filters( 'menu_image_link_attributes', $attributes_array, $item, $depth, $args ); $attributes = ''; foreach ( $attributes_array as $attr_name => $attr_value ) { $attributes .= "{$attr_name}=\"$attr_value\" "; } $attributes = trim( $attributes ); $item_output = "{$args->before}"; $link = $args->link_before . apply_filters( 'the_title', $item->title, $item->ID ) . $args->link_after; $none = ''; // Sugar. $image = apply_filters( 'menu_image_img_html', $image ); switch ( $position ) { case 'hide': case 'before': case 'above': $item_args = array( $none, $link, $image ); break; case 'after': default: $item_args = array( $image, $link, $none ); break; } $item_output .= vsprintf( '%s%s%s', $item_args ); $item_output .= "{$args->after}"; } return $item_output; } /** * Loading additional stylesheet. * * Loading custom stylesheet to fix images positioning in match themes */ public function menu_image_add_inline_style_action() { // wp_register_style( 'BZOTech-menu-image', BZOTech_EXTENSIONS_DIR . 'BZOTech-menu-image/assets/css/menu-image.css', array(), '1.0' ); // wp_enqueue_style( 'BZOTech-menu-image' ); } /** * Loading media-editor script ot nav-menus page. * * @since 2.0 */ public function menu_image_admin_head_nav_menus_action() { wp_enqueue_script( 'BZOTech-menu-image-admin', BZOTech_EXTENSIONS_DIR . 'BZOTech-menu-image/assets/js/menu-image-admin.js', array( 'jquery' ), '1.0' ); wp_localize_script( 'BZOTech-menu-image-admin', 'menuImage', array( 'l10n' => array( 'uploaderTitle' => __( 'Chose menu image', 'bzoago' ), 'uploaderButtonText' => __( 'Select', 'bzoago' ), ), 'settings' => array( 'nonce' => wp_create_nonce( 'update-menu-item' ), ), ) ); wp_enqueue_media(); wp_enqueue_style( 'editor-buttons' ); wp_register_style( 'BZOTech-menu-image', BZOTech_EXTENSIONS_DIR . 'BZOTech-menu-image/assets/css/menu-image.css', array(), '1.0' ); wp_enqueue_style( 'BZOTech-menu-image' ); } /** * When menu item removed remove menu image metadata. */ public function menu_image_delete_menu_item_image_action() { $menu_item_id = (int) $_REQUEST['menu-item']; check_admin_referer( 'delete-menu_item_image_' . $menu_item_id ); if ( is_nav_menu_item( $menu_item_id ) && has_post_thumbnail( $menu_item_id ) ) { delete_post_thumbnail( $menu_item_id ); delete_post_meta( $menu_item_id, '_thumbnail_hover_id' ); delete_post_meta( $menu_item_id, '_menu_item_image_size' ); delete_post_meta( $menu_item_id, '_menu_item_image_title_position' ); } } /** * Output HTML for the menu item images. * * @since 2.0 * * @param int $item_id The post ID or object associated with the thumbnail, defaults to global $post. * * @return string html */ public function wp_post_thumbnail_only_html( $item_id ) { $default_size = apply_filters( 'menu_image_default_size', 'menu-36x36' ); $markup = '

'; $thumbnail_id = get_post_thumbnail_id( $item_id ); $content = sprintf( $markup, esc_html__( 'Menu image', 'bzoago' ), $thumbnail_id ? esc_attr__( 'Change menu item image', 'bzoago' ) : esc_attr__( 'Set menu item image', 'bzoago' ), '', $item_id, $thumbnail_id ? wp_get_attachment_image( $thumbnail_id, $default_size ) : esc_html__( 'Set image', 'bzoago' ), $thumbnail_id ? '' . __( 'Remove', 'bzoago' ) . '' : '' ); // Menu image on hover if enabled. $hover_id = get_post_meta( $item_id, '_thumbnail_hover_id', true ); $content .= sprintf( $markup, esc_html__( 'Image on hover', 'bzoago' ), $hover_id ? esc_attr__( 'Change menu item image on hover', 'bzoago' ) : esc_attr__( 'Set menu item image on hover', 'bzoago' ), ' hover-image', $item_id, $hover_id ? wp_get_attachment_image( $hover_id, $default_size ) : esc_html__( 'Set image on hover', 'bzoago' ), $hover_id ? '' . __( 'Remove', 'bzoago' ) . '' : '' ); return $content; } /** * Output HTML for the menu item images section. * * @since 2.0 * * @param int $item_id The post ID or object associated with the thumbnail, defaults to global $post. * * @return string html */ public function wp_post_thumbnail_html( $item_id ) { $default_size = apply_filters( 'menu_image_default_size', 'menu-36x36' ); $content = $this->wp_post_thumbnail_only_html( $item_id ); $image_size = get_post_meta( $item_id, '_menu_item_image_size', true ); if ( ! $image_size ) { $image_size = $default_size; } $title_position = get_post_meta( $item_id, '_menu_item_image_title_position', true ); if ( ! $title_position ) { $title_position = apply_filters( 'menu_image_default_title_position', 'after' ); } ob_start(); ?> $content" . ob_get_clean(); /** * Filter the admin menu item thumbnail HTML markup to return. * * @since 2.0 * * @param string $content Admin menu item images HTML markup. * @param int $item_id Post ID. */ return apply_filters( 'admin_menu_item_thumbnail_html', $content, $item_id ); } /** * Update item thumbnail via ajax action. * * @since 2.0 */ public function wp_ajax_set_menu_item_thumbnail() { $json = ! empty( $_REQUEST['json'] ); $post_ID = intval( $_POST['post_id'] ); if ( ! current_user_can( 'edit_post', $post_ID ) ) { wp_die( - 1 ); } $thumbnail_id = intval( $_POST['thumbnail_id'] ); $is_hovered = (bool) $_POST['is_hover']; check_ajax_referer( 'update-menu-item' ); if ( $thumbnail_id == '-1' ) { if ( $is_hovered ) { $success = delete_post_meta( $post_ID, '_thumbnail_hover_id' ); } else { $success = delete_post_thumbnail( $post_ID ); } } else { if ( $is_hovered ) { $success = update_post_meta( $post_ID, '_thumbnail_hover_id', $thumbnail_id ); } else { $success = set_post_thumbnail( $post_ID, $thumbnail_id ); } } if ( $success ) { $return = $this->wp_post_thumbnail_only_html( $post_ID ); $json ? wp_send_json_success( $return ) : wp_die( $return ); } wp_die( 0 ); } /** * Add custom fields to menu item. * * @param int $item_id * @param object $item * @param int $depth * @param array $args */ public function menu_item_custom_fields( $item_id, $item, $depth, $args ) { if ( ! $item_id && isset( $item->ID ) ) { $item_id = $item->ID; } ?>
wp_post_thumbnail_html( $item_id ); ?>
isAttachmentUsed( $data[ 'attachment_id' ], $data[ 'size' ] ); } /** * Set used attachment ids. * * @param string $size * @param int $id */ public function setUsedAttachments( $size, $id ) { $this->used_attachments[ $size ][ ] = $id; } /** * Check if attachment is used in menu items. * * @param int $id * @param string $size * * @return bool */ public function isAttachmentUsed( $id, $size = null ) { if ( ! is_null( $size ) ) { return is_string( $size ) && isset( $this->used_attachments[ $size ] ) && in_array( $id, $this->used_attachments[ $size ] ); } else { foreach ( $this->used_attachments as $used_attachment ) { if ( in_array( $id, $used_attachment ) ) { return true; } } return false; } } /** * Filters the list of attachment image attributes. * * @since 2.8.0 * * @param array $attr Attributes for the image markup. * @param WP_Post $attachment Image attachment post. * @param string|array $size Requested size. Image size or array of width and height values * (in that order). Default 'thumbnail'. * * @return array Valid array of image attributes. */ public function wp_get_attachment_image_attributes( $attr, $attachment, $size ) { if ( $this->isAttachmentUsed( $attachment->ID, $size ) ) { unset( $attr['sizes'], $attr['srcset'] ); } return $attr; } /** * Mark item as processed to prevent re-processing it again. * * @param int $id */ protected function setProcessed( $id ) { $this->processed[] = $id; } /** * Check if was already processed. * * @param int $id * * @return bool */ protected function isProcessed( $id ) { return in_array( $id, $this->processed ); } } $menu_image = new BZOTech_Menu_Image(); require_once( ABSPATH . 'wp-admin/includes/nav-menu.php' ); class BZOTech_Menu_Image_Walker_Nav_Menu_Edit extends Walker_Nav_Menu_Edit { public function start_el( &$output, $item, $depth = 0, $args = array(), $id = 0 ) { global $_wp_nav_menu_max_depth; $_wp_nav_menu_max_depth = $depth > $_wp_nav_menu_max_depth ? $depth : $_wp_nav_menu_max_depth; ob_start(); $item_id = esc_attr( $item->ID ); $removed_args = array( 'action', 'customlink-tab', 'edit-menu-item', 'menu-item', 'page-tab', '_wpnonce', ); $original_title = false; if ( 'taxonomy' == $item->type ) { $original_title = get_term_field( 'name', $item->object_id, $item->object, 'raw' ); if ( is_wp_error( $original_title ) ) { $original_title = false; } } elseif ( 'post_type' == $item->type ) { $original_object = get_post( $item->object_id ); $original_title = get_the_title( $original_object->ID ); } elseif ( 'post_type_archive' == $item->type ) { $original_object = get_post_type_object( $item->object ); if ( $original_object ) { $original_title = $original_object->labels->archives; } } $classes = array( 'menu-item menu-item-depth-' . $depth, 'menu-item-' . esc_attr( $item->object ), 'menu-item-edit-' . ( ( isset( $_GET['edit-menu-item'] ) && $item_id == $_GET['edit-menu-item'] ) ? 'active' : 'inactive'), ); $title = $item->title; if ( ! empty( $item->_invalid ) ) { $classes[] = 'menu-item-invalid'; /* translators: %s: title of menu item which is invalid */ $title = sprintf( __( '%s (Invalid)','bzoago' ), $item->title ); } elseif ( isset( $item->post_status ) && 'draft' == $item->post_status ) { $classes[] = 'pending'; /* translators: %s: title of menu item in draft status */ $title = sprintf( __( '%s (Pending)','bzoago' ), $item->title ); } $title = ( ! isset( $item->label ) || '' == $item->label ) ? $title : $item->label; $submenu_text = ''; if ( 0 == $depth ) $submenu_text = 'style="display: none;"'; ?>