Afișarea condițională a elementelor din meniul de navigare în funcție de capacitățile utilizatorului
Am o "ramură" în arborele de navigare principal al site-ului meu care ar trebui să fie accesibilă doar unui set de utilizatori înregistrați și autentificați. Înțeleg cum să interoghez rolul și capacitățile unui utilizator. Această întrebare se referă în mod specific la care este cea mai bună modalitate de a folosi meniul de navigare integrat, dar să ascund un element în mod condițional.
Trebuie să suprascriu navigarea implicită și să scriu o interogare personalizată și să construiesc manual structura de navigare? Mi-ar plăcea să evit acest lucru dacă este posibil.
Nu am nevoie de exemple complete de cod, doar de ideile și abordarea/framework-ul general.
Apreciez sfaturile!
T

Folosește propriul tău walker și verifică capacitatea înainte de a crea un element.

Absolut genial. Mulțumesc pentru asta - deși sunt surprins că este chiar atât de complex. Cum ai spus, probabil o omisiune?

Deci walker-ul este bun pentru a te asigura că un element de meniu marcat ca "protejat" nu apare. Dar cum creezi un element de meniu care este marcat ca "protejat" sau, mai bine, ca aparținând unui anumit rol sau capabilitate? Se pare că extindem elementele de meniu și adăugăm mai mulți parametri configurabili de utilizator în formularul de element de meniu...?

@TomAuger Adaugă metadate adecvate obiectului postării legate, de exemplu o taxonomie personalizată sau un câmp meta pentru post. Verifică valoarea câmpului în walker.

Mulțumesc pentru clarificare. Cu siguranță, o taxonomie personalizată sau un câmp personalizat ar fi soluția corectă. O altă opțiune la care m-am gândit se bazează pe șablon. Nu-mi place prea mult soluția mea pentru că nu ar trebui să cuplezi un șablon cu funcționalități integrate în acest fel, dar există o legătură logică între șablon (care ar trebui să verifice dacă utilizatorul are acces pentru a vedea conținutul) și meniu. Voi posta codul meu într-un răspuns, mai jos, pentru alți utilizatori cu aceeași întrebare.

Cineva a creat un plugin genial pentru a face asta fără să fie nevoie de codare. Are chiar și casete de bifat în interfața editorului de meniu pentru a selecta rolurile aprobate pentru fiecare element de meniu.

Răspunsul lui toscho este corect, dar pentru puținii care știu exact ce fac și cum să o facă. :) Adaug acest lucru pentru restul lumii ca o soluție mai simplă pentru utilizatorii mai puțin avansați. Ideea ar fi să ai meniuri diferite și să le afișezi în funcție de rolul utilizatorului.
Să zicem că ai 3 meniuri numite editor, autor și implicit:
if (current_user_can('Editor')){
//meniu pentru rolul de editor
wp_nav_menu( array('menu' => 'editor' ));
}elseif(current_user_can('Author')){
//meniu pentru rolul de autor
wp_nav_menu( array('menu' => 'author' ));
}else{
//meniul implicit
wp_nav_menu( array('menu' => 'default' ));
}

Acum trebuie să gestionezi un meniu pentru fiecare rol. Aș prefera să adaug o casetă de bifat la elementele de meniu în editor. Din păcate, nu există do_action()
în class Walker_Nav_Menu_Edit
– nu există o interfață de programare (API) pentru a face asta. Pare o omisiune.

Sunt de acord și aș prefera și eu să adaug o casetă de bifat sau un câmp simplu pentru a introduce numele rolului dorit, dar din nou am postat acest răspuns ca o alternativă la al tău. Există opțiunea de a utiliza caseta de descriere pentru fiecare element și pe baza acesteia poți verifica rolul, dar atunci pierzi capacitatea de a folosi descrierea. Este un API nou, dă-i puțin timp și deschide cât mai multe tickete Trac pentru a-l îmbunătăți :)

Problema cu suprascrierea metodelor start_el și end_el este că aceasta controlează doar afișarea elementului de meniu în cauză - nu afectează afișarea elementelor copil. Cred că trebuie să suprascrieți display_element pentru a ascunde și elementele copil.
De asemenea, este posibil să utilizați câmpul de descriere al elementului de meniu pentru a stoca informații despre fiecare element de meniu, inclusiv dacă să-l afișați sau nu.
Acest cod caută în descrierea fiecărui element de meniu o listă de capabilități separate prin virgulă, cum ar fi [capability: aceasta, urmatoarea] și dacă utilizatorul curent nu are niciuna dintre capabilități, nu va afișa elementul (sau oricare dintre elementele sale copil). Este destul de ușor să eliminați aceste șiruri din descriere dacă doriți să utilizați descrierea în scopul său inițial.
/*
* ascunde sau afișează meniuri în funcție de capabilitățile utilizatorului
*
* Utilizați câmpul de descriere al elementului de meniu personalizat. Dacă acesta conține un șir ca [capability: xx, xx]
* afișați elementul de meniu doar dacă utilizatorul are cel puțin una dintre capabilități.
* Dacă un element de meniu nu este afișat, niciunul dintre elementele sale copil nu va fi afișat.
*/
/* Walker personalizat */
class NASS_Nav_Walker extends Walker_Nav_Menu {
// suprascrie metoda părinte
function display_element( $element, &$children_elements, $max_depth, $depth=0, $args, &$output ) {
// știm că $element este un element de meniu
// vrem să verificăm câmpul său de descriere pentru a vedea dacă este OK să-l afișăm
// în acest caz, ne întoarcem pur și simplu la metoda părinte
$desc = $element->description;
if ( should_display_menu_item($desc) ) {
parent::display_element( $element, &$children_elements, $max_depth, $depth=0, $args, &$output );
} else {
return;
}
}
}
/* ar trebui să afișăm elementul de meniu, dacă aceasta este descrierea sa? */
function should_display_menu_item( $desc ) {
// mai întâi verificăm dacă descrierea conține o specificație de capabilitate de forma
// [capability: listă, separată, prin virgulă]
// presupunem că toate numele de capabilități constau doar din litere mici și underscore-uri
$prefix = "\[capability:";
$postfix = "\]";
$pattern = '@' . $prefix . '([a-z_, ]+)' . $postfix . '@';
$answer = true;
if ( preg_match($pattern, $desc, $matches) ) { // dacă avem o potrivire
$found = $matches[1]; // partea din regex dintre paranteze
$caps = array_map('trim', explode(",", $found));
if ( count ($caps) > 0 ) { // există cel puțin una
$answer = false;
// acum verificăm dacă utilizatorul are oricare dintre ele
foreach ($caps as $cap) {
if ( current_user_can ($cap) ) $answer = true;
}
}
}
return $answer;
}

Am adăugat acest răspuns, deși a trecut ceva timp de când această întrebare a fost activă, pentru că am crezut că ar putea fi util pentru alții.

Am încercat să folosesc câmpul de descriere pentru a specifica ce roluri pot accesa anumite elemente de meniu, bazându-mi modificările pe codul obținut de aici - Pimp my WP Menu
Versiunea mea modificată:
<?php
/***
* Menu WALKER - pentru restricționarea vizibilității elementelor de meniu
* Cod modificat de - Trupti Bhatt (http://3sided.co.in)
* folosind codul original postat aici - 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
{
/*
* Variabilă personalizată pentru a stoca rolul curent
*/
private $current_user_role = "";
/*
* Obține rolul utilizatorului curent
*/
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='vizitator';
return $this->current_user_role;
}
}
/*
* Verifică dacă utilizatorul este administrator
*/
private function isAdmin()
{
$current_role = $this->getCurrentUserRole();
if ( $current_role == "administrator" )
{
return true;
}
else
{
return false;
}
}
/*
* Obține toate restricțiile
*/
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;
}
/*
* Verifică accesul
*/
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;
}
}
/*
* Randare element
*/
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;
/*
* Primul test, adaugă clasă personalizată fiecărui element de meniu
*/
$classes[] = 'my-custom-menu-class';
/*
* Detectează elementul de meniu corespunzător paginii nepublicate
*/
// -> Flag pentru afișarea output-ului
$item_to_display = true;
$is_item_published = true;
// -> Colectează datele obiectului legat
$item_data = get_post($item->object_id);
// --> Dacă este o pagină, acționează asupra flag-ului
if ( !empty($item_data) && ($item->object == "page") )
{
$is_item_published = ( $item_data->post_status == "publish" ) ? true : false;
$item_output = "";
}
/*
* Detectează și afișează în funcție de rolul utilizatorului
**/
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 ) . '"';
$id = apply_filters( 'nav_menu_item_id', 'menu-item-'. $item->ID, $item, $args );
$id = strlen( $id ) ? ' id="' . esc_attr( $id ) . '"' : '';
$output .= $indent . '<li id="menu-item-'. $item->ID . '"' . $value . $class_names .'>';
$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 ) .'"' : '';
if($depth != 0)
{
$description = $append = $prepend = "";
}
// --> Dacă flag-ul este true, afișează output-ul
if ( $item_to_display )
{
$item_output = $args->before;
$item_output .= '<a'. $attributes .'>';
$item_output .= $args->link_before .apply_filters( 'the_title', $item->title, $item->ID ).$append;
$item_output .= '</a>';
$item_output .= $args->after;
}
$output .= apply_filters( 'walker_nav_menu_start_el', $item_output, $item, $depth, $args );
}
}
/*
* Configurație restricții
* 2 este ID-ul paginii de Acasă
**/
define("_USE_RESTRICTED_ACCESS", true);
$menu_restricted_access_array['subscriber'] = array('2');
?>
Nu este cea mai curată versiune încă, dar funcționează. Sper că și alții o vor putea folosi cu succes.

Voi posta soluția mea pentru alții care ar putea da peste acest fir. Nu sunt 100% mulțumit de ea pentru că nu ar trebui să cuplezi un șablon cu funcționalitatea de meniu (abordarea lui Toscho de a utiliza metadate sau o taxonomie personalizată este probabil mai corectă). Cu toate acestea, este o soluție rapidă și improvizată. Am încercat să atenuez cuplarea strânsă prin furnizarea unor constante în partea de sus a functions.php pentru a ajuta viitorii dezvoltatori să întrețină codul:
// definește șablonul personalizat pentru paginile protejate prin parolă, folosit pentru a determina dacă acest element este protejat sau nu în meniuri
define ('ZG_PROTECTED_PAGE_TEMPLATE', 'page-membersonly.php');
// definește numele capabilității personalizate pentru paginile protejate
define ('ZG_PROTECTED_PAGE_CAPABILITY', 'view_member_pages');
Deci șablonul pentru pagina protejată este doar o variantă a single.php, dar va verifica dacă current_user_can(ZG_PROTECTED_PAGE_CAPABILITY) înainte de a afișa orice conținut, din motive de securitate.
Apoi, implementez un walker personalizat, conform sugestiei lui Toscho. Walker-ul în acest caz este extrem de simplu - suprascriem metodele publice start_el și end_el din Walker_Nav_Menu, interceptându-le doar suficient pentru a pune întrebarea: avem acces să vedem elementul de meniu?
Regula este simplă: dacă pagina nu este o pagină "privată" (care în acest caz este determinată de faptul că acea pagină folosește un anumit șablon de pagină), atunci este vizibilă. Dacă ESTE o pagină "privată" și utilizatorul este autentificat într-un rol care are capabilitatea personalizată pe care o căutăm, atunci este vizibilă. Altfel, nu este vizibilă.
Dacă avem acces, atunci trebuie doar să folosim metodele încorporate ale lui Walker_Nav_Menu fără modificări, așa că apelăm metoda parent:: cu același nume.
/* Walker personalizat pentru a preveni afișarea paginilor protejate prin parolă în listă */
class HALCO_Nav_Walker extends Walker_Nav_Menu {
protected $is_private = false;
protected $page_is_visible = false;
// suprascrie metoda părinte
function start_el(&$output, $item, $depth, $args) {
// acest element de meniu se referă la o pagină care folosește șablonul nostru protejat?
$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);
}
}
// suprascrie metoda părinte
function end_el(&$output, $item, $depth) {
if ($page_is_visible){
parent::end_el(&$output, $item, $depth);
}
}
}
