Mostra gli elementi del menu di navigazione in modo condizionale in base alle autorizzazioni dell'utente

7 apr 2011, 17:21:28
Visualizzazioni: 21.7K
Voti: 5

Ho un "ramo" dell'albero di navigazione principale del mio sito che dovrebbe essere accessibile solo a un gruppo di utenti registrati e loggati. So come interrogare il ruolo e le autorizzazioni di un utente. Questa domanda riguarda specificamente qual è il modo migliore per sfruttare il menu di navigazione integrato, nascondendo semplicemente un elemento in modo condizionale.

Devo sovrascrivere la navigazione predefinita integrata e scrivere una query personalizzata e costruire manualmente la struttura di navigazione? Mi piacerebbe evitarlo se possibile.

Non ho bisogno di esempi di codice completi, solo delle vostre idee e dell'approccio generale al framework.

Grazie per i consigli!

T

1
Commenti

Ho scritto un articolo sul blog relativo a questo problema. Per il caso d'uso specifico di nascondere le voci di menu agli utenti che non hanno accesso, consulta questo articolo approfondito.

Tom Auger Tom Auger
9 dic 2011 19:47:19
Tutte le risposte alla domanda 6
4

Utilizza il tuo walker personalizzato e verifica i permessi prima di creare un elemento.

7 apr 2011 17:24:52
Commenti

Assolutamente brillante. Grazie per questo - anche se sono sorpreso che sia così complesso. Come hai detto, forse una svista?

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

Quindi il walker è utile per assicurarsi che una voce di menu contrassegnata come "protetta" non appaia. Ma come si crea una voce di menu contrassegnata come "protetta" o, meglio ancora, come appartenente a un ruolo o capacità specifica? Sembra che stiamo estendendo le voci di menu e aggiungendo alcuni parametri configurabili dall'utente al modulo della voce di menu...?

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

@TomAuger Aggiungi i metadati appropriati all'oggetto post collegato, ad esempio una tassonomia personalizzata o un campo meta del post. Controlla il valore del campo nel walker.

fuxia fuxia
17 mag 2011 18:56:56

Grazie per la chiarificazione. Assolutamente una tassonomia personalizzata o un campo personalizzato sarebbe la strada corretta da seguire. Un'altra opzione che ho elaborato si basa sul template. Non mi piace la mia soluzione perché non dovresti accoppiare un template a funzionalità integrate in quel modo, ma c'è una connessione logica tra il template (che dovrebbe verificare se l'utente ha accesso per vedere il contenuto) e il menu. Pubblicherò il mio codice in una risposta, qui sotto, per altri utenti con la stessa domanda.

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

Qualcuno ha creato un plugin brillante per farlo senza bisogno di codificare. Include persino delle caselle di controllo nell'interfaccia dell'editor dei menu per selezionare i ruoli autorizzati per ogni voce di menu.

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

2 feb 2013 23:23:15
3

La risposta di toscho è corretta ma è adatta solo per chi sa esattamente cosa sta facendo e come farlo. :) Aggiungo questa soluzione per il resto del mondo, come approccio più semplice per gli utenti meno avanzati. L'idea è avere menu diversi e visualizzarli in base al ruolo dell'utente.

Supponiamo di avere 3 menu chiamati editor, author e default:

if (current_user_can('Editor')){
    //menu per il ruolo editor
    wp_nav_menu( array('menu' => 'editor' ));

}elseif(current_user_can('Author')){
    //menu per il ruolo author
    wp_nav_menu( array('menu' => 'author' ));

}else{
    //menu di default
    wp_nav_menu( array('menu' => 'default' ));
}
7 apr 2011 19:02:22
Commenti

Ora devi gestire un menu per ogni ruolo. Preferirei aggiungere una casella di controllo agli elementi del menu nell'editor. Sfortunatamente non c'è un do_action() in class Walker_Nav_Menu_Edit – nessuna API per farlo. Sembra una svista.

fuxia fuxia
7 apr 2011 19:53:34

Sono d'accordo e personalmente preferirei aggiungere una casella di controllo o un semplice campo di input per inserire il nome del ruolo desiderato, ma ho pubblicato questa risposta come alternativa alla tua. C'è l'opzione di utilizzare la casella di descrizione per ogni elemento e in base a quella puoi verificare il ruolo, ma così perdi la possibilità di usare la descrizione. È una nuova API, dagli un po' di tempo e apri più ticket trac che puoi per farla andare avanti :)

Bainternet Bainternet
7 apr 2011 20:21:31

Grazie per questo approccio più diretto. Personalmente mi fa rabbrividire l'idea di dover definire i miei menu tramite codice - per me mina completamente una delle funzionalità di base di wp_admin ma forse non c'è un'alternativa più semplice.

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

Il problema con l'override di start_el e end_el è che così facendo si controlla solo la visualizzazione del singolo elemento del menu - non si influisce sulla visualizzazione dei suoi elementi figli. Penso sia necessario eseguire l'override di display_element per nascondere anche i figli.

Inoltre, è possibile utilizzare il campo description degli elementi del menu per memorizzare informazioni su ciascun elemento riguardo a se mostrarlo o meno.

Questo codice cerca nella descrizione di ogni elemento del menu una lista di capacità separate da virgole come [capability: questa_una, prossima_una] e se l'utente corrente non ha nessuna di queste capacità non mostrerà l'elemento (né alcuno dei suoi figli). È abbastanza facile rimuovere le stringhe dalla descrizione se si vuole effettivamente utilizzare la descrizione per il suo scopo originale.

/*
 * Nascondi o mostra i menu in base alle capacità dell'utente
 *
 * Utilizza il campo description dell'elemento del menu personalizzato. Se contiene una stringa come [capability: xx, xx] 
 * mostra l'elemento del menu solo se l'utente ha almeno una delle capacità specificate.
 * Se un elemento del menu non viene mostrato, lo stesso vale per tutti i suoi elementi figli.
 */
/* Custom Walker */
class NASS_Nav_Walker extends Walker_Nav_Menu {

        // override del metodo parent
        function display_element( $element, &$children_elements, $max_depth, $depth=0, $args, &$output ) {
            // sappiamo che $element è un elemento del menu  
            // vogliamo verificare il suo campo description per vedere se è OK mostrarlo
            // in tal caso ci limitiamo a richiamare il metodo parent
            $desc = $element->description;
            if ( should_display_menu_item($desc) ) {
                parent::display_element( $element, &$children_elements, $max_depth, $depth=0, $args, &$output );
            } else {
                return;
            }
        }    
}
/* Dovremmo mostrare l'elemento del menu, data questa descrizione? */
function should_display_menu_item( $desc ) {
    // prima verifichiamo se la descrizione contiene una specifica di capacità nella forma
    // [capability: lista, separata, da virgole]
    // assumiamo che tutti i nomi delle capacità contengano solo lettere minuscole e underscore
    $prefix = "\[capability:";
    $postfix = "\]";
    $pattern = '@' . $prefix . '([a-z_, ]+)' . $postfix . '@';
    $answer = true;
    if ( preg_match($pattern, $desc, $matches) ) { // se abbiamo una corrispondenza
        $found = $matches[1];   // la parte tra parentesi della regex
        $caps = array_map('trim', explode(",", $found));
        if ( count ($caps) > 0 ) { // c'è almeno una capacità
            $answer = false;
            // ora verifichiamo se l'utente ha almeno una di queste capacità
            foreach ($caps as $cap) {
                if ( current_user_can ($cap) ) $answer = true;
            }
        }
    }
    return $answer;

}
24 ago 2011 00:42:01
Commenti

Ho aggiunto questa risposta anche se è passato un po' di tempo da quando questa domanda era attiva perché pensavo potesse essere utile ad altri.

lpryor lpryor
24 ago 2011 00:46:19

+1 Grazie per aver dedicato del tempo a condividere la tua soluzione! È sempre apprezzato da chi si imbatte nella domanda attraverso una ricerca su Google o SE. Saluti!

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

Ho provato a utilizzare il campo descrizione per definire quali ruoli possono accedere a quali voci di menu, basandomi su un codice trovato qui - Pimp my WP Menu

La mia versione modificata:

<?php

/***
* Menu WALKER - per limitare la visibilità delle voci di menu
* Codice modificato da - Trupti Bhatt (http://3sided.co.in)
* utilizzando il codice originale postato qui - 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
{
        /*
                 *      Variabile personalizzata per memorizzare il ruolo corrente
                 */
                private $current_user_role = "";

                /*
                 *      Ottiene il ruolo dell'utente corrente
                 */
                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='visitor';
                                        return $this->current_user_role;
                                }
                }

                /*
                 *      Verifica se l'utente è un amministratore
                 */
                private function isAdmin()
                {
                                $current_role = $this->getCurrentUserRole();

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

                /*
                 *      Ottiene tutte le restrizioni
                 */
                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;
                }

                /*
                 *      Verifica l'accesso
                 */
                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;
                                        }

                }

     /*
                 *      Render dell'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;


            /*
             *  Primo test, aggiunge una classe personalizzata a ogni voce di menu
             */
                $classes[] = 'my-custom-menu-class';

            /*
             *  Rileva la voce di menu corrispondente alla pagina non pubblicata
             *  Rileva la voce di menu corrispondente alla pagina non pubblicata
             */
                // -> Flag per visualizzare l'output
                $item_to_display = true;
                $is_item_published = true;

                // -> Raccoglie i dati dell'oggetto collegato
                $item_data = get_post($item->object_id);

                // --> Se è una pagina, agisce sul flag

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

            /*
             *  Rileva e visualizza in base al ruolo utente
             **/
                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 mag 2011 08:29:25
0

Pubblico la mia soluzione per chi potrebbe imbattersi in questa discussione. Non sono al 100% soddisfatto perché non dovresti accoppiare un template con la funzionalità del menu (l'approccio di Toscho di usare metadati o una tassonomia personalizzata è probabilmente più corretto). Tuttavia, è una soluzione rapida e sporca. Ho cercato di mitigare il forte accoppiamento fornendo alcune costanti all'inizio di functions.php per aiutare gli sviluppatori futuri a mantenere il codice:

// definisci il template personalizzato protetto da password che viene usato per determinare se questo elemento è protetto o meno nei menu
define ('ZG_PROTECTED_PAGE_TEMPLATE', 'page-membersonly.php');
// definisci il nome della capability personalizzata per le pagine protette
define ('ZG_PROTECTED_PAGE_CAPABILITY', 'view_member_pages');

Quindi il template della pagina protetta è semplicemente una variante di single.php, ma controllerà se current_user_can(ZG_PROTECTED_PAGE_CAPABILITY) prima di mostrare qualsiasi contenuto, per motivi di sicurezza.

Successivamente, implemento un walker personalizzato, come suggerito da Toscho. Il walker in questo caso è estremamente semplice - sovrascriviamo i metodi pubblici start_el e end_el di Walker_Nav_Menu, intercettandoli solo il tempo necessario per porci la domanda: abbiamo accesso per vedere la voce del menu?

La regola è semplice: se la pagina non è una pagina "privata" (che in questo caso è determinata dal fatto che quella pagina usa un particolare template di pagina) è visibile. Se È una pagina "privata" e l'utente è autenticato in un ruolo che ha la capability personalizzata che stiamo cercando, allora è visibile. Altrimenti, non è visibile.

Se abbiamo accesso, allora dobbiamo solo usare i metodi integrati di Walker_Nav_Menu senza modifiche, quindi chiamiamo il metodo parent:: con lo stesso nome.

/* Custom Walker per prevenire la comparsa di pagine protette da password nella lista */
    class HALCO_Nav_Walker extends Walker_Nav_Menu {

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

        // sovrascrive il metodo del parent
        function start_el(&$output, $item, $depth, $args) {
            // questa voce di menu si riferisce a una pagina che usa il nostro template protetto?
            $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);
            }
        }

        // sovrascrive il metodo del parent
        function end_el(&$output, $item, $depth) {
            if ($page_is_visible){
                parent::end_el(&$output, $item, $depth);
            }
        }
    }
20 mag 2011 16:19:40