Mostrar elemento del menú de navegación condicionalmente según las capacidades del usuario

7 abr 2011, 17:21:28
Vistas: 21.7K
Votos: 5

Tengo una "rama" del árbol de navegación principal de mi sitio que solo debería ser accesible para un conjunto de usuarios registrados y conectados. Entiendo cómo consultar el rol y las capacidades de un usuario. Esta pregunta es específicamente sobre cuál es la mejor manera de aprovechar el menú de navegación incorporado, pero ocultando un elemento de forma condicional.

¿Necesito sobrecargar la navegación predeterminada incorporada y escribir una consulta personalizada y construir la estructura de navegación manualmente? Me encantaría evitar esto si es posible.

No necesito muestras de código completas, solo sus ideas y enfoque general del framework.

¡Agradezco el consejo!

T

1
Comentarios

Escribí una publicación en el blog relacionada con este problema. Para el caso de uso específico de ocultar elementos del menú a usuarios que no tienen acceso, consulta esta publicación detallada.

Tom Auger Tom Auger
9 dic 2011 19:47:19
Todas las respuestas a la pregunta 6
4

Utiliza tu propio walker y verifica los permisos antes de crear un elemento.

7 abr 2011 17:24:52
Comentarios

Absolutamente brillante. Gracias por esto, aunque me sorprende que sea tan complejo como eso. Como dijiste, ¿posiblemente un descuido?

Tom Auger Tom Auger
21 abr 2011 22:43:30

Entonces, el walker es bueno para asegurarse de que un elemento del menú marcado como "protegido" no aparezca. Pero, ¿cómo creas un elemento del menú que esté marcado como "protegido" o, mejor aún, como perteneciente a un rol o capacidad específica? Parece que estamos extendiendo los elementos del menú y añadiendo más parámetros configurables por el usuario al formulario del elemento del menú...?

Tom Auger Tom Auger
17 may 2011 18:54:02

@TomAuger Añade los metadatos apropiados al objeto de la publicación enlazada, por ejemplo una taxonomía personalizada o un campo de metadatos de la publicación. Verifica el valor del campo en el walker.

fuxia fuxia
17 may 2011 18:56:56

Gracias por la aclaración. Sin duda, una taxonomía personalizada o un campo personalizado sería la forma correcta de proceder. Otra opción que se me ocurrió se basa en la plantilla. No me gusta tanto mi solución porque no deberías acoplar una plantilla a funcionalidades integradas de esa manera, pero hay una conexión lógica entre la plantilla (que debería verificar si el usuario tiene acceso para ver el contenido) y el menú. Voy a publicar mi código en una respuesta, a continuación, para otros usuarios con la misma pregunta.

Tom Auger Tom Auger
20 may 2011 15:42:05
0

Alguien ha creado un complemento brillante para hacer esto sin necesidad de codificación. Incluso incluye casillas de verificación en la interfaz del editor de menús para seleccionar los roles aprobados por cada elemento del menú.

http://wordpress.org/extend/plugins/nav-menu-roles/

2 feb 2013 23:23:15
3

La respuesta de toscho es correcta, pero para las pocas personas que saben lo que están haciendo y cómo hacerlo. :) Estoy añadiendo esto para el resto del mundo como una solución más simple para los usuarios menos avanzados. La idea sería tener diferentes menús y mostrarlos según el rol del usuario.

Digamos que tienes 3 menús llamados editor, autor y predeterminado:

if (current_user_can('Editor')){
    //menú para rol de editor
    wp_nav_menu( array('menu' => 'editor' ));

}elseif(current_user_can('Author')){
    //menú para rol de autor
    wp_nav_menu( array('menu' => 'author' ));

}else{
    //menú predeterminado
    wp_nav_menu( array('menu' => 'default' ));
}
7 abr 2011 19:02:22
Comentarios

Ahora tienes que gestionar un menú para cada rol. Preferiría añadir una casilla de verificación a los elementos del menú en el editor. Desafortunadamente no hay un do_action() en class Walker_Nav_Menu_Edit - no hay API para hacer eso. Parece un descuido.

fuxia fuxia
7 abr 2011 19:53:34

Estoy de acuerdo y yo mismo preferiría añadir una casilla de verificación o un campo de entrada simple para guardar el nombre del rol deseado, pero de nuevo publiqué esta respuesta como alternativa a la tuya. Hay una opción para usar el cuadro de descripción para cada elemento y en base a eso puedes verificar el rol, pero entonces pierdes la capacidad de descripción. Es una API nueva, dale algo de tiempo y abre tantos tickets trac como puedas para que avance :)

Bainternet Bainternet
7 abr 2011 20:21:31

Gracias por este enfoque más directo. Personalmente me horroriza tener que definir mis menús mediante código - para mí socava completamente una de las características básicas de wp_admin pero quizás no haya una alternativa más fácil.

Tom Auger Tom Auger
21 abr 2011 22:45:08
2

El problema con sobrescribir start_el y end_el es que al hacerlo solo se controla la visualización del elemento del menú en cuestión, no afecta la visualización de los elementos hijos. Creo que necesitas sobrescribir display_element para ocultar también los elementos hijos.

Además, es posible usar el campo de descripción del elemento del menú para almacenar información sobre si debe mostrarse o no cada elemento.

Este código busca en la descripción de cada elemento del menú una lista de capacidades separadas por comas como [capability: esta_capacidad, siguiente_capacidad] y si el usuario actual no tiene ninguna de las capacidades, no mostrará el elemento (ni ninguno de sus hijos). Es bastante fácil eliminar las cadenas de la descripción si realmente deseas usar la descripción para su propósito original.

/*
 * ocultar o mostrar menús basados en capacidades de usuario
 *
 * Usa el campo de descripción del elemento de menú personalizado. Si contiene una cadena como [capability: xx, xx] 
 * muestra el elemento del menú solo si el usuario tiene al menos una de las capacidades.
 * Si un elemento del menú no se muestra, tampoco se mostrarán sus subelementos.
 */
/* Walker personalizado */
class NASS_Nav_Walker extends Walker_Nav_Menu {

        // sobrescribe el método padre
        function display_element( $element, &$children_elements, $max_depth, $depth=0, $args, &$output ) {
            // sabemos que $element es un elemento del menú  
            // queremos verificar su campo de descripción para ver si está bien mostrarlo
            // en cuyo caso simplemente recurrimos al método padre
            $desc = $element->description;
            if ( should_display_menu_item($desc) ) {
                parent::display_element( $element, &$children_elements, $max_depth, $depth=0, $args, &$output );
            } else {
                return;
            }
        }    
}
/* ¿Deberíamos mostrar el elemento del menú, si esta es su descripción? */
function should_display_menu_item( $desc ) {
    // primero vemos si la descripción contiene una especificación de capacidad del formato
    // [capability: lista, separada, por, comas]
    // asumimos que todos los nombres de capacidad consisten solo en minúsculas con guiones bajos
    $prefix = "\[capability:";
    $postfix = "\]";
    $pattern = '@' . $prefix . '([a-z_, ]+)' . $postfix . '@';
    $answer = true;
    if ( preg_match($pattern, $desc, $matches) ) { // si tenemos una coincidencia
        $found = $matches[1];   // la parte entre paréntesis de la regex
        $caps = array_map('trim', explode(",", $found));
        if ( count ($caps) > 0 ) { // hay al menos una
            $answer = false;
            // ahora vemos si el usuario tiene alguna de ellas
            foreach ($caps as $cap) {
                if ( current_user_can ($cap) ) $answer = true;
            }
        }
    }
    return $answer;

}
24 ago 2011 00:42:01
Comentarios

He agregado esta respuesta aunque ha pasado un tiempo desde que esta pregunta estuvo activa porque pensé que podría ser útil para otros.

lpryor lpryor
24 ago 2011 00:46:19

+1 ¡Gracias por tomarte el tiempo de agregar tu solución! Siempre es apreciado por otros que se topan con la pregunta en una búsqueda de Google o SE. ¡Saludos!

Tom Auger Tom Auger
24 ago 2011 15:25:58
0

He intentado utilizar el campo de descripción para determinar qué roles pueden acceder a qué elementos del menú, y he basado mis modificaciones en un código que obtuve aquí - Potenciar mi menú de WP

Mi versión modificada:

<?php

/***
* Menu WALKER - para restringir la visibilidad de los elementos del menú
* Código modificado por - Trupti Bhatt (http://3sided.co.in)
* utilizando el código original publicado aquí - http://www.tisseur-de-toile.fr/wordpress-tricks/pimp-my-wordpress-menu-part-2-access-granted-to-authorized-personnel-only.html
***/
class description_walker extends Walker_Nav_Menu
{
        /*
                 *      Variable personalizada para almacenar el rol actual
                 */
                private $current_user_role = "";

                /*
                 *      Obtener el rol del usuario actual
                 */
                private function getCurrentUserRole()
                {
                                global $current_user;
                                if ( is_user_logged_in() )
                                {
                                        if ( $this->current_user_role == "" )
                                        {
                                                $this->current_user_role = $current_user->roles[0];
                                        }

                                        return $this->current_user_role;
                                }
                                else
                                {
                                        $this->current_user_role='visitante';
                                        return $this->current_user_role;
                                }
                }

                /*
                 *      Verificar si el usuario es administrador
                 */
                private function isAdmin()
                {
                                $current_role = $this->getCurrentUserRole();

                if ( $current_role == "administrador" )
                                {
                                                return true;
                                }
                                else
                                {
                                                return false;
                                }
                }

                /*
                 *      Obtener todas las restricciones
                 */
                private function getAllRestrictions()
                {
                        global $menu_restricted_access_array;


                                $all_restrictions_array = array();

                                foreach ( $menu_restricted_access_array as $one_restriction )
                                {
                                        $all_restrictions_array = array_merge($all_restrictions_array, $one_restriction);
                                }
                                $all_restrictions_array = array_unique($all_restrictions_array);

                                return $all_restrictions_array;
                }

                /*
                 *      Verificar el acceso
                 */
                private function isAccessGranted( $id_menu_item )
                {
                                global $menu_restricted_access_array;

                if ( $this->isAdmin() )
                                {
                                                return true;
                                }
                else if ( isset($menu_restricted_access_array[$this->current_user_role]) )
                {
                    $restricted_access = $menu_restricted_access_array[$this->current_user_role];

                    if ( in_array($id_menu_item, $restricted_access) )
                                        {
                        return true;
                                        }
                                        else
                                        {
                        return false;
                                        }
                }
                else {
                        return true;
                                        }

                }

     /*
                 *      Renderizar elemento
                 */
                function start_el(&$output, $item, $depth, $args)
        {

            global $wp_query, $menu_restricted_access_array;
            global $g_role,$g_pageid;
            $indent = ( $depth ) ? str_repeat( "\t", $depth ) : '';
                        $g_role=strtolower((trim($item->description)));

                        $str = explode(',',$g_role);
                        for( $i=0; $i< count($str); $i++)
                                {                      

                                        if (strtolower(trim($str[$i]))==$this->current_user_role)
                                        {                      
                                                $restriction =$item->object_id;        
                                                $menu_restricted_access_array[$this->current_user_role] =array( $restriction);
                                        }


                                }


            $class_names = $value = '';

            $classes = empty( $item->classes ) ? array() : (array) $item->classes;
            $classes[] = 'menu-item-' . $item->ID;


            /*
             *  Primer test, añadir clase personalizada a cada elemento del menú
             */
                $classes[] = 'mi-clase-personalizada-de-menu';

            /*
             *  Detectar el elemento del menú que coincide con la página no publicada
             *  Detectar el elemento del menú que coincide con la página no publicada
             */
                // -> Bandera para mostrar la salida
                $item_to_display = true;
                $is_item_published = true;

                // -> Recopilar datos del objeto enlazado
                $item_data = get_post($item->object_id);

                // --> Si es una página, actuar sobre la bandera

                if ( !empty($item_data) && ($item->object == "page") )
                {
                    $is_item_published = ( $item_data->post_status == "publicado" ) ? true : false;
                    $item_output = "";
                }

            /*
             *  Detectar y mostrar por Rol de usuario
             **/
                if ( _USE_RESTRICTED_ACCESS )
                {
                    $restrictions_array = $this->getAllRestrictions();
                    $this->isAccessGranted($item->object_id);

                }
                else
                {
                    $item_to_display = $is_item_published;
                }

            $class_names = join( ' ', apply_filters( 'nav_menu_css_class', array_filter( $classes ), $item ) );
            $class_names = ' class="' . esc_attr( $class_names ) . '"';
16 may 2011 08:29:25
0

Voy a publicar mi solución para otros que puedan encontrarse con este hilo. No estoy 100% satisfecho con ella porque no deberías acoplar una plantilla con funcionalidad de menú (el enfoque de Toscho de usar metadatos o una taxonomía personalizada probablemente sea más correcto). Sin embargo, es una solución rápida y sucia. He intentado mitigar el acoplamiento estrecho proporcionando algunas constantes cerca de la parte superior de functions.php para ayudar a futuros desarrolladores a mantener el código:

// define la plantilla personalizada protegida por contraseña que se usa para determinar si este elemento está protegido o no en los menús
define ('ZG_PROTECTED_PAGE_TEMPLATE', 'page-membersonly.php');
// define el nombre de la capacidad personalizada para páginas protegidas
define ('ZG_PROTECTED_PAGE_CAPABILITY', 'view_member_pages');

Así que la plantilla de página protegida es solo una variante de single.php, pero verificará si current_user_can(ZG_PROTECTED_PAGE_CAPABILITY) antes de mostrar cualquier contenido, por razones de seguridad.

A continuación, implemento un walker personalizado, como sugirió Toscho. El walker en este caso es extremadamente simple: sobrescribimos los métodos públicos start_el y end_el de Walker_Nav_Menu, solo interceptándolos el tiempo suficiente para hacer la pregunta: ¿tenemos acceso para ver el elemento del menú?

La regla es simple: si la página no es una página "privada" (que en este caso se determina por esa página que usa una plantilla de página particular) es visible. Si ES una página "privada", y el usuario está autenticado en un rol que tiene la capacidad personalizada que estamos buscando, entonces es visible. De lo contrario, no es visible.

Si tenemos acceso, entonces solo tenemos que usar los métodos integrados de Walker_Nav_Menu sin modificación, así que llamamos al método parent:: del mismo nombre.

/* Walker personalizado para evitar que aparezcan páginas protegidas por contraseña en la lista */
    class HALCO_Nav_Walker extends Walker_Nav_Menu {

        protected $is_private = false;
        protected $page_is_visible = false;

        // sobrescribe el método padre
        function start_el(&$output, $item, $depth, $args) {
            // ¿este elemento de menú hace referencia a una página que usa nuestra plantilla protegida?
            $is_private = get_post_meta($item->object_id, '_wp_page_template', true) == ZG_PROTECTED_PAGE_TEMPLATE;
            $page_is_visible = !$is_private || ($is_private && current_user_can(ZG_PROTECTED_PAGE_CAPABILITY));

            if ($page_is_visible){
                parent::start_el(&$output, $item, $depth, $args);
            }
        }

        // sobrescribe el método padre
        function end_el(&$output, $item, $depth) {
            if ($page_is_visible){
                parent::end_el(&$output, $item, $depth);
            }
        }
    }
20 may 2011 16:19:40