Dzwonisz .pointer( 'open' );
funkcji JavaScript na wszystkich obiektów wskazówek, więc nie jest zaskoczeniem, że wszystkie wskaźniki wyświetlane w tym samym czasie ...
To powiedziawszy, nie rozumiem, dlaczego zwracasz wszystkie wskaźniki (nawet nieaktywne), custom_admin_pointers()
a następnie dodajesz dodatkową funkcję, aby sprawdzić, czy są jakieś aktywne wskaźniki i sprawdź wewnątrz pętli wskaźników ( if ( $array['active'] ) {
), aby wybrać dodanie wskaźnika javascript albo nie. Czy nie jest prostsze tylko zwracanie tylko aktywnych wskaźników?
Co więcej, dodajesz, że javascript na wszystkich stronach administracyjnych, nie jest za dużo? Weź również pod uwagę, że niektóre elementy, takie jak „# save-post” są dostępne tylko na nowej stronie postu, więc czy nie lepiej dodawać wskaźniki tylko na nowej stronie puli?
Wreszcie, jak niechlujny jest ten javascript pomieszany z PHP, myślę, że powinieneś rozważyć przesłanie wp_localize_script
danych do javascript.
Plan:
- Przenieś definicje wskaźników w PHP do osobnego pliku, w ten sposób można łatwo edytować, a także usuwać znaczniki z kodu PHP, dzięki czemu wszystko jest bardziej czytelne i łatwe do utrzymania
- W wskaźników konfiguracyjnych dodać obiekt „gdzie”, który będzie używany do zestawu, w którym administrator strony powinien pojawić się okienko:
post-new.php
, index.php
...
- Napisz klasę, która zajmie się ładowaniem, analizowaniem i filtrowaniem informacji o wskaźnikach
- Napisz js dobroć, która pomoże nam zmienić domyślny przycisk „Usuń” na „Dalej”
# 4 puszki (prawdopodobnie) łatwo zrobić znając wskaźnik wtyczki dobrze, ale to nie moja sprawa. Więc użyję ogólnego kodu jQuery, aby uzyskać wynik, jeśli ktoś może ulepszyć mój kod, docenię.
Edytować
Edytowałem kod (głównie js), ponieważ istnieją różne rzeczy, których nie wziąłem pod uwagę: niektóre wskaźniki można dodać do tej samej kotwicy lub takie same wskaźniki można dodać do nieistniejących lub niewidocznych kotwic. We wszystkich tych przypadkach poprzedni kod nie działał, wydaje się, że nowa wersja ładnie rozwiązuje te problemy.
Skonfigurowałem także Gist z całym kodem, którego użyłem do testowania.
Zacznijmy od punktów 1 i 2 : utwórz plik o nazwie pointers.php
i napisz tam:
<?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;
Konfiguracja wszystkich wskaźników jest tutaj. Gdy musisz coś zmienić, po prostu otwórz ten plik i edytuj go.
Zwróć uwagę na właściwość „where”, która jest tablicą stron, na których wskaźnik powinien być dostępny.
Jeśli chcesz wyświetlić wskaźniki na stronie generowanej przez wtyczkę, poszukaj tej linii opisanej poniżej public function filter( $page ) {
i dodaj die($page);
bezpośrednio pod nią. Następnie otwórz odpowiednią stronę wtyczki i użyj tego ciągu we where
właściwości.
Ok, teraz punkt # 3 .
Przed napisaniem zajęć chcę po prostu napisać interfejs: tam dodam komentarze, abyście mogli lepiej zrozumieć, co zrobi klasa.
<?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 );
}
Myślę, że powinno być całkiem jasne. Teraz napiszmy klasę, która będzie zawierać 2 metody z interfejsu oraz konstruktora.
<?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;
}
}
Kod jest bardzo prosty i robi dokładnie to, czego oczekuje interfejs.
Jednak klasa sama z siebie nic nie robi, potrzebujemy haka, w którym należy utworzyć instancję klasy i uruchomić 2 metody, przekazując odpowiednie argumenty.
'admin_enqueue_scripts'
Jest idealny dla naszego zakresu: nie będziemy mieli dostępu do bieżącej strony administratora i możemy również enqueue skrypty i style potrzebne.
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 );
} );
Nic specjalnego: użycie klasy do pobrania danych wskaźników i jeśli niektóre wskaźniki przejdą przez filtry, zamieniają style i skrypty. Następnie przekaż dane wskaźników do skryptu wraz ze zlokalizowaną etykietą „Dalej” dla przycisku.
Ok, teraz „najtrudniejsza” część: js. Ponownie chcę podkreślić, że nie znam wtyczki wskaźnika, której używa WordPress, więc to, co robię w kodzie, można zrobić lepiej, jeśli ktoś to wie, jednak mój kod działa i - mówiąc ogólnie - nie jest tak źle.
( 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`
Przy pomocy komentarzy kod powinien być dość wyraźny, przynajmniej mam taką nadzieję.
Ok, skończyliśmy. Nasz PHP jest prostszy i lepiej zorganizowany, nasz javascript jest bardziej czytelny, wskaźniki są łatwiejsze do edycji i, co ważniejsze, wszystko działa.