Vous appelez la .pointer( 'open' );
fonction javascript sur tous les objets pointeurs, il n'est donc pas surprenant que tous les pointeurs apparaissent en même temps ...
Cela dit, je ne comprends pas pourquoi renvoyez tous les pointeurs (même non actifs) custom_admin_pointers()
, puis ajoutez une fonction supplémentaire pour vérifier s'il existe des pointeurs actifs et une vérification à l'intérieur de la boucle de pointeurs ( if ( $array['active'] ) {
) pour choisir d'ajouter un pointeur javascript ou pas. N'est-ce pas plus simple de ne renvoyer que des pointeurs actifs?
De plus, vous ajoutez ce javascript sur toutes les pages d'administration, ce n'est pas trop? Considérez également que certains éléments comme "# save-post" ne sont disponibles que sur la nouvelle page de publication, il n'est donc pas préférable d'ajouter les pointeurs uniquement dans la nouvelle page de pot?
Enfin, à quel point ce javascript est mélangé avec PHP, je pense que vous devriez envisager d'utiliser wp_localize_script
pour transmettre des données à javascript.
Le plan:
- Déplacez les définitions de pointeurs en PHP dans un fichier séparé, de cette façon, il est facile de modifier et également de supprimer le balisage du code PHP, tout est plus lisible et maintenable
- Dans les pointeurs de configuration ajouter une propriété « où » qui sera utilisé pour définir dans quelle page admin une fenêtre devrait apparaître:
post-new.php
, index.php
...
- Écrire une classe qui gérera le chargement, l'analyse et le filtrage des pointeurs info
- Écrivez quelques bonnes choses qui nous aideront à changer le bouton "Supprimer" par défaut en "Suivant"
Le # 4 peut (probablement) se faire facilement en connaissant bien le plugin pointeur, mais ce n'est pas mon cas. Je vais donc utiliser le code général jQuery pour obtenir le résultat, si quelqu'un peut améliorer mon code, j'apprécierai.
Éditer
J'ai édité le code (principalement js) car il y avait différentes choses que je n'avais pas considérées: certains pointeurs peuvent être ajoutés à la même ancre, ou les mêmes pointeurs peuvent être ajoutés à des ancres inexistantes ou non visibles. Dans tous les cas, le code précédent ne fonctionnait pas, la nouvelle version semble bien résoudre ce problème.
J'ai également configuré un Gist avec tout le code que j'ai utilisé pour tester.
Commençons par les points # 1 et # 2 : créez un fichier nommé pointers.php
et écrivez-y:
<?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 configuration de tous les pointeurs est ici. Lorsque vous devez modifier quelque chose, ouvrez simplement ce fichier et modifiez-le.
Notez la propriété "where" qui est un tableau de pages où le pointeur doit être disponible.
Si vous souhaitez afficher des pointeurs dans une page générée par un plugin, recherchez cette ligne décrite ci public function filter( $page ) {
- dessous et ajoutez-la die($page);
immédiatement en dessous. Ouvrez ensuite la page de plug-in correspondante et utilisez cette chaîne dans la where
propriété.
Ok, maintenant le point # 3 .
Avant d'écrire la classe, je veux juste coder une interface: j'y mettrai des commentaires pour que vous puissiez mieux comprendre ce que fera la classe.
<?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 );
}
Je pense que cela devrait être assez clair. Écrivons maintenant la classe, elle contiendra les 2 méthodes de l'interface plus le constructeur.
<?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;
}
}
Le code est très simple et fait exactement ce que l'interface attend.
Cependant, la classe ne fait rien par elle-même, nous avons besoin d'un crochet où instancier la classe et lancer les 2 méthodes en passant les arguments appropriés.
Le 'admin_enqueue_scripts'
est parfait pour notre portée: là, nous aurons accès à la page d'administration actuelle et nous pouvons également mettre en file d'attente les scripts et les styles nécessaires.
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 );
} );
Rien de spécial: il suffit d'utiliser la classe pour obtenir des données de pointeurs et si certains pointeurs passent les filtres, mettre en file d'attente les styles et les scripts. Ensuite, transmettez les données des pointeurs au script jusqu'à l'étiquette "Suivant" localisée pour le bouton.
Ok, maintenant la partie "la plus difficile": les js. Encore une fois, je tiens à souligner que je ne connais pas le plugin de pointeur utilisé par WordPress, donc ce que je fais dans mon code peut être mieux fait si quelqu'un le sait, mais mon code fait son travail et, en général, ce n'est pas si mal.
( 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`
Avec l'aide des commentaires, le code devrait être assez clair, du moins, je l'espère.
Ok, nous avons terminé. Notre PHP est plus simple et mieux organisé, notre javascript est plus lisible, les pointeurs sont plus faciles à éditer et, plus important encore, tout fonctionne.