Sie rufen die .pointer( 'open' );
Javascript-Funktion für alle Zeigerobjekte auf, daher ist es nicht verwunderlich, dass alle Zeiger gleichzeitig angezeigt werden ...
Ich verstehe jedoch nicht, warum Sie alle Zeiger (auch nicht aktive) von zurückgeben custom_admin_pointers()
und dann eine zusätzliche Funktion hinzufügen, um zu überprüfen, ob einige aktive Zeiger vorhanden sind, und eine Überprüfung innerhalb der Zeigerschleife ( if ( $array['active'] ) {
), um den Javascript-Zeiger hinzuzufügen oder nicht. Ist es nicht einfacher, nur aktive Zeiger zurückzugeben?
Darüber hinaus fügen Sie das Javascript auf allen Admin-Seiten hinzu, ist nicht zu viel? Bedenken Sie auch, dass einige Elemente wie "# save-post" nur auf einer neuen Post-Seite verfügbar sind. Ist es also nicht besser, die Zeiger nur auf einer neuen Pot-Seite hinzuzufügen?
Schließlich, wie chaotisch dieses mit PHP vermischte Javascript ist, sollten Sie in Betracht ziehen, wp_localize_script
Daten an Javascript zu übergeben.
Der Plan:
- Verschieben Sie die Zeigerdefinitionen in PHP in eine separate Datei. Auf diese Weise ist es einfach, Markups aus dem PHP-Code zu bearbeiten und zu entfernen. Alles führt zu einer besseren Lesbarkeit und Wartung
- Fügen Sie in der Zeigerkonfiguration eine Eigenschaft "where" hinzu, mit der festgelegt wird, auf welcher Administrationsseite ein Popup angezeigt werden soll :
post-new.php
, index.php
...
- Schreiben Sie eine Klasse, die das Laden, Parsen und Filtern von Zeigerinformationen übernimmt
- Schreiben Sie einige js Güte, die uns helfen, die Standardschaltfläche "Entfernen" in "Weiter" zu ändern.
Die # 4 kann (wahrscheinlich) leicht das Zeiger-Plugin gut kennen, aber es ist nicht mein Fall. Also werde ich allgemeinen jQuery-Code verwenden, um das Ergebnis zu erhalten. Wenn jemand meinen Code verbessern kann, werde ich es zu schätzen wissen.
Bearbeiten
Ich habe den Code (hauptsächlich js) bearbeitet, weil ich verschiedene Dinge nicht berücksichtigt habe: Einige Zeiger können demselben Anker hinzugefügt werden, oder dieselben Zeiger können nicht vorhandenen oder nicht sichtbaren Ankern hinzugefügt werden. In all dem Fall, in dem der vorherige Code nicht funktioniert hat, scheint die neue Version diese Probleme gut zu lösen.
Ich habe auch einen Gist mit dem gesamten Code eingerichtet, den ich zum Testen verwendet habe.
Beginnen wir mit den Punkten 1 und 2 : Erstellen Sie eine Datei mit dem Namen pointers.php
und schreiben Sie dort:
<?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;
Die Konfiguration aller Zeiger finden Sie hier. Wenn Sie etwas ändern müssen, öffnen Sie einfach diese Datei und bearbeiten Sie sie.
Beachten Sie die Eigenschaft "where", bei der es sich um ein Array von Seiten handelt, auf denen der Zeiger verfügbar sein soll.
Wenn Sie Zeiger auf einer von einem Plugin generierten Seite anzeigen möchten, suchen Sie nach dieser unten beschriebenen Zeile public function filter( $page ) {
und fügen Sie sie die($page);
unmittelbar darunter hinzu. Öffnen Sie dann die entsprechende Plugin-Seite und verwenden Sie diese Zeichenfolge in der where
Eigenschaft.
Ok, jetzt der Punkt # 3 .
Bevor ich die Klasse schreibe, möchte ich nur eine Schnittstelle codieren: Dort werde ich Kommentare einfügen, damit Sie besser verstehen, was die Klasse tun wird.
<?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 );
}
Ich denke sollte ziemlich klar sein. Schreiben wir nun die Klasse, sie enthält die 2 Methoden von interface plus den Konstruktor.
<?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;
}
}
Code ist sehr einfach und macht genau das, was die Schnittstelle erwartet.
Die Klasse macht jedoch nichts für sich. Wir benötigen einen Hook, um die Klasse zu instanziieren und die beiden Methoden zu starten, die die richtigen Argumente übergeben.
Das 'admin_enqueue_scripts'
ist perfekt für unseren Bereich: Dort haben wir Zugriff auf die aktuelle Administrationsseite und können die benötigten Skripte und Stile in die Warteschlange stellen.
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 );
} );
Nichts Besonderes: Verwenden Sie einfach die Klasse, um Zeigerdaten abzurufen, und wenn einige Zeiger die Filter übergeben, werden Stile und Skripte in die Warteschlange gestellt. Übergeben Sie dann Zeigerdaten an das Skript zusammen mit der lokalisierten Bezeichnung "Weiter" für die Schaltfläche.
Ok, jetzt der "schwierigste" Teil: der js. Ich möchte noch einmal hervorheben, dass ich das Zeiger-Plugin, das WordPress verwendet, nicht kenne. Was ich also in meinem Code mache, kann besser gemacht werden, wenn jemand es weiß, aber mein Code macht seine Arbeit und - im Allgemeinen - ist es nicht so schlecht.
( 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`
Mit Hilfe von Kommentaren sollte der Code zumindest ziemlich klar sein, ich hoffe es.
Ok wir sind fertig. Unser PHP ist einfacher und besser organisiert, unser Javascript besser lesbar, Zeiger sind einfacher zu bearbeiten und, was noch wichtiger ist, alles funktioniert.