Stai chiamando la .pointer( 'open' );
funzione javascript su tutti gli oggetti puntatori, quindi non è una sorpresa che tutti i puntatori vengano visualizzati contemporaneamente ...
Detto questo, non capisco perché restituisci tutti i puntatori (anche quelli non attivi) da custom_admin_pointers()
e quindi aggiungi una funzione aggiuntiva per verificare se ci sono alcuni puntatori attivi e un controllo all'interno del ciclo di puntatori ( if ( $array['active'] ) {
) per scegliere di aggiungere il puntatore javascript o no. Non è più semplice restituire solo i puntatori attivi?
Inoltre, stai aggiungendo che javascript su tutte le pagine di amministrazione, non è troppo? Considera anche che alcuni elementi come "# save-post" sono disponibili solo nella nuova pagina dei post, quindi non è meglio aggiungere i puntatori solo nella nuova pagina del piatto?
Infine, quanto è disordinato quel javascript confuso con PHP, penso che dovresti considerare di usare wp_localize_script
per passare i dati a javascript.
Il piano:
- Sposta le definizioni dei puntatori in PHP in un file separato, in questo modo è facile modificarlo e anche rimuovere il markup dal codice PHP, tutto risulta più leggibile e manutenibile
- Nei puntatori configurazione aggiungere una proprietà "dove" che verrà utilizzato per impostare in cui pagina di amministrazione dovrebbe comparire un popup:
post-new.php
, index.php
...
- Scrivi una classe che gestirà il caricamento, l'analisi e il filtraggio delle informazioni sui puntatori
- Scrivi qualche bontà js che ci aiuterà a cambiare il pulsante "Rimuovi" predefinito in "Avanti"
Il # 4 può (probabilmente) facilmente conoscere bene il plugin del puntatore, ma non è il mio caso. Quindi userò il codice jQuery generale per ottenere il risultato, se qualcuno può migliorare il mio codice lo apprezzerò.
modificare
Ho modificato il codice (principalmente js) perché ci sono diverse cose che non avevo preso in considerazione: alcuni puntatori possono essere aggiunti alla stessa ancora, oppure è possibile aggiungere gli stessi puntatori a ancore inesistenti o non visibili. In tutti questi casi, il codice precedente non funzionava, la nuova versione sembra risolvere bene questi problemi.
Ho anche installato un Gist con tutto il codice che ho usato per testare.
Cominciamo con i punti n. 1 e n. 2 : creare un file denominato pointers.php
e scrivere lì:
<?php
$pointers = array();
$pointers['new-items'] = array(
'title' => sprintf( '<h3>%s</h3>', esc_html__( 'Add New Item' ) ),
'content' => sprintf( '<p>%s</p>', esc_html__( 'Easily add a new post..' ) ),
'anchor_id' => '#wp-admin-bar-new-content',
'edge' => 'top',
'align' => 'left',
'where' => array( 'index.php', 'post-new.php' ) // <-- Please note this
);
$pointers['story_cover_help'] = array(
'title' => sprintf( '<h3>%s</h3>', esc_html__( 'Another info' ) ),
'content' => sprintf( '<p>%s</p>', esc_html__( 'Lore ipsum....' ) ),
'anchor_id' => '#save-post',
'edge' => 'top',
'align' => 'right',
'where' => array( 'post-new.php' ) // <-- Please note this
);
// more pointers here...
return $pointers;
La configurazione di tutti i puntatori è qui. Quando devi cambiare qualcosa, basta aprire questo file e modificarlo.
Nota la proprietà "where" che è una matrice di pagine in cui dovrebbe essere disponibile il puntatore.
Se si desidera visualizzare i puntatori in una pagina generata da un plug-in, cercare questa riga descritta di seguito public function filter( $page ) {
e aggiungere die($page);
immediatamente al di sotto di essa. Quindi apri la rispettiva pagina del plugin e usa quella stringa nella where
proprietà.
Ok, ora il punto 3 .
Prima di scrivere la lezione voglio solo codificare un'interfaccia: lì inserirò dei commenti in modo che tu possa capire meglio quale classe farà.
<?php
interface PointersManagerInterface {
/**
* Load pointers from file and setup id with prefix and version.
* Cast pointers to objects.
*/
public function parse();
/**
* Remove from parse pointers dismissed ones and pointers
* that should not be shown on given page
*
* @param string $page Current admin page file
*/
public function filter( $page );
}
Penso che dovrebbe essere abbastanza chiaro. Ora scriviamo la classe, conterrà i 2 metodi dall'interfaccia più il costruttore.
<?php namespace GM;
class PointersManager implements PointersManagerInterface {
private $pfile;
private $version;
private $prefix;
private $pointers = array();
public function __construct( $file, $version, $prefix ) {
$this->pfile = file_exists( $file ) ? $file : FALSE;
$this->version = str_replace( '.', '_', $version );
$this->prefix = $prefix;
}
public function parse() {
if ( empty( $this->pfile ) ) return;
$pointers = (array) require_once $this->pfile;
if ( empty($pointers) ) return;
foreach ( $pointers as $i => $pointer ) {
$pointer['id'] = "{$this->prefix}{$this->version}_{$i}";
$this->pointers[$pointer['id']] = (object) $pointer;
}
}
public function filter( $page ) {
if ( empty( $this->pointers ) ) return array();
$uid = get_current_user_id();
$no = explode( ',', (string) get_user_meta( $uid, 'dismissed_wp_pointers', TRUE ) );
$active_ids = array_diff( array_keys( $this->pointers ), $no );
$good = array();
foreach( $this->pointers as $i => $pointer ) {
if (
in_array( $i, $active_ids, TRUE ) // is active
&& isset( $pointer->where ) // has where
&& in_array( $page, (array) $pointer->where, TRUE ) // current page is in where
) {
$good[] = $pointer;
}
}
$count = count( $good );
if ( $good === 0 ) return array();
foreach( array_values( $good ) as $i => $pointer ) {
$good[$i]->next = $i+1 < $count ? $good[$i+1]->id : '';
}
return $good;
}
}
Il codice è molto semplice e fa esattamente ciò che l'interfaccia si aspetta.
Tuttavia, la classe non fa nulla da sola, abbiamo bisogno di un hook dove istanziare la classe e lanciare i 2 metodi passando argomenti appropriati.
Il 'admin_enqueue_scripts'
è perfetto per il nostro campo: non avremo accesso alla pagina di amministrazione corrente e possiamo anche gli script di accodamento e stili necessario.
add_action( 'admin_enqueue_scripts', function( $page ) {
$file = plugin_dir_path( __FILE__ ) . 'pointers.php';
// Arguments: pointers php file, version (dots will be replaced), prefix
$manager = new PointersManager( $file, '5.0', 'custom_admin_pointers' );
$manager->parse();
$pointers = $manager->filter( $page );
if ( empty( $pointers ) ) { // nothing to do if no pointers pass the filter
return;
}
wp_enqueue_style( 'wp-pointer' );
$js_url = plugins_url( 'pointers.js', __FILE__ );
wp_enqueue_script( 'custom_admin_pointers', $js_url, array('wp-pointer'), NULL, TRUE );
// data to pass to javascript
$data = array(
'next_label' => __( 'Next' ),
'close_label' => __('Close'),
'pointers' => $pointers
);
wp_localize_script( 'custom_admin_pointers', 'MyAdminPointers', $data );
} );
Niente di speciale: basta usare la classe per ottenere i dati dei puntatori e se alcuni puntatori passano i filtri accodano stili e script. Quindi passare i dati dei puntatori allo script insieme all'etichetta "Avanti" localizzata per il pulsante.
Ok, ora la parte "più difficile": la js. Ancora una volta voglio evidenziare che non conosco il plug-in del puntatore utilizzato da WordPress, quindi ciò che faccio nel mio codice può essere fatto meglio se qualcuno lo conosce, tuttavia il mio codice fa il suo lavoro e, parlando in termini generali, non è poi così male.
( function($, MAP) {
$(document).on( 'MyAdminPointers.setup_done', function( e, data ) {
e.stopImmediatePropagation();
MAP.setPlugin( data ); // open first popup
} );
$(document).on( 'MyAdminPointers.current_ready', function( e ) {
e.stopImmediatePropagation();
MAP.openPointer(); // open a popup
} );
MAP.js_pointers = {}; // contain js-parsed pointer objects
MAP.first_pointer = false; // contain first pointer anchor jQuery object
MAP.current_pointer = false; // contain current pointer jQuery object
MAP.last_pointer = false; // contain last pointer jQuery object
MAP.visible_pointers = []; // contain ids of pointers whose anchors are visible
MAP.hasNext = function( data ) { // check if a given pointer has valid next property
return typeof data.next === 'string'
&& data.next !== ''
&& typeof MAP.js_pointers[data.next].data !== 'undefined'
&& typeof MAP.js_pointers[data.next].data.id === 'string';
};
MAP.isVisible = function( data ) { // check if anchor for given pointer is visible
return $.inArray( data.id, MAP.visible_pointers ) !== -1;
};
// given a pointer object, return its the anchor jQuery object if available
// otherwise return first available, lookin at next property of subsequent pointers
MAP.getPointerData = function( data ) {
var $target = $( data.anchor_id );
if ( $.inArray(data.id, MAP.visible_pointers) !== -1 ) {
return { target: $target, data: data };
}
$target = false;
while( MAP.hasNext( data ) && ! MAP.isVisible( data ) ) {
data = MAP.js_pointers[data.next].data;
if ( MAP.isVisible( data ) ) {
$target = $(data.anchor_id);
}
}
return MAP.isVisible( data )
? { target: $target, data: data }
: { target: false, data: false };
};
// take pointer data and setup pointer plugin for anchor element
MAP.setPlugin = function( data ) {
if ( typeof MAP.last_pointer === 'object') {
MAP.last_pointer.pointer('destroy');
MAP.last_pointer = false;
}
MAP.current_pointer = false;
var pointer_data = MAP.getPointerData( data );
if ( ! pointer_data.target || ! pointer_data.data ) {
return;
}
$target = pointer_data.target;
data = pointer_data.data;
$pointer = $target.pointer({
content: data.title + data.content,
position: { edge: data.edge, align: data.align },
close: function() {
// open next pointer if it exists
if ( MAP.hasNext( data ) ) {
MAP.setPlugin( MAP.js_pointers[data.next].data );
}
$.post( ajaxurl, { pointer: data.id, action: 'dismiss-wp-pointer' } );
}
});
MAP.current_pointer = { pointer: $pointer, data: data };
$(document).trigger( 'MyAdminPointers.current_ready' );
};
// scroll the page to current pointer then open it
MAP.openPointer = function() {
var $pointer = MAP.current_pointer.pointer;
if ( ! typeof $pointer === 'object' ) {
return;
}
$('html, body').animate({ // scroll page to pointer
scrollTop: $pointer.offset().top - 30
}, 300, function() { // when scroll complete
MAP.last_pointer = $pointer;
var $widget = $pointer.pointer('widget');
MAP.setNext( $widget, MAP.current_pointer.data );
$pointer.pointer( 'open' ); // open
});
};
// if there is a next pointer set button label to "Next", to "Close" otherwise
MAP.setNext = function( $widget, data ) {
if ( typeof $widget === 'object' ) {
var $buttons = $widget.find('.wp-pointer-buttons').eq(0);
var $close = $buttons.find('a.close').eq(0);
$button = $close.clone(true, true).removeClass('close');
$buttons.find('a.close').remove();
$button.addClass('button').addClass('button-primary');
has_next = false;
if ( MAP.hasNext( data ) ) {
has_next_data = MAP.getPointerData(MAP.js_pointers[data.next].data);
has_next = has_next_data.target && has_next_data.data;
}
var label = has_next ? MAP.next_label : MAP.close_label;
$button.html(label).appendTo($buttons);
}
};
$(MAP.pointers).each(function(index, pointer) { // loop pointers data
if( ! $().pointer ) return; // do nothing if pointer plugin isn't available
MAP.js_pointers[pointer.id] = { data: pointer };
var $target = $(pointer.anchor_id);
if ( $target.length && $target.is(':visible') ) { // anchor exists and is visible?
MAP.visible_pointers.push(pointer.id);
if ( ! MAP.first_pointer ) {
MAP.first_pointer = pointer;
}
}
if ( index === ( MAP.pointers.length - 1 ) && MAP.first_pointer ) {
$(document).trigger( 'MyAdminPointers.setup_done', MAP.first_pointer );
}
});
} )(jQuery, MyAdminPointers); // MyAdminPointers is passed by `wp_localize_script`
Con l'aiuto dei commenti il codice dovrebbe essere abbastanza chiaro, almeno, lo spero.
Ok abbiamo finito. Il nostro PHP è più semplice e meglio organizzato, il nostro javascript è più leggibile, i puntatori sono più facili da modificare e, cosa più importante, tutto funziona.