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.
add_action( 'admin_enqueue_scripts', function( $page ) {
zwykłym powrocie, jeśli użytkownik nie ma wymaganej roli.public function filter( $page ) {
wPointersManager
klasie i natychmiast po jej umieszczeniudie($page);
. Otwórz przeglądarkę i wklej adres URL, strona umrze z ciągiem: właśnie tego musisz użyć'where'
.Ahhh .. tak. Wskaźniki WordPress. Wiesz, istnieje wiele mieszanych uczuć, jeśli chodzi o używanie wskaźników;)
Twój kod był na dobrej drodze. Ale jest kilka problemów.
@GM ma rację co do
pointer('open')
polecenia otwierającego wszystkie wskaźniki na raz. Ponadto nie zapewniasz metody przechodzenia przez wskaźniki.Walczyłem z tym samym problemem ... i wpadłem na własne podejście. Używam zmiennej zapytania w adresie URL, ponownie ładuję stronę do strony administratora, na której chcę wyświetlić następny wskaźnik, i pozwalam jQuery zająć się resztą.
Klasa wskaźników WP
Postanowiłem napisać to jako lekcję. Ale najpierw pokażę to stopniowo, aby lepiej zrozumieć, co się dzieje.
Rozpoczęcie zajęć
admin_enqueue_scripts
.NIE musisz nic zmieniać w tych pierwszych funkcjach.
Konfigurowanie tablicy pozycji wskaźnika
Następnym krokiem jest zdefiniowanie każdego ze wskaźników. Jest pięć elementów, które musimy zdefiniować (z wyjątkiem ostatniego wskaźnika). Zrobimy to za pomocą tablic. Rzućmy okiem na funkcję:
Okej. Rzućmy okiem na kilka rzeczy tutaj.
Po pierwsze, nasza
$tour
tablica. Jest to tablica zawierająca wszystkie wskaźniki Z WYJĄTKIEM pierwszego wskaźnika wyświetlanego użytkownikowi (więcej na ten temat później). Więc zacznij od drugiego wskaźnika, który zamierzasz pokazać ... i przejdź do ostatniego wskaźnika.Następnie mamy kilka bardzo ważnych przedmiotów.
$tour
kluczami musi być niepowtarzalny (quick_press, SITE_TITLE, quick_press_last; jako przykłady powyżej).function
Polecenie reload / przenieść okno. To jest używane do wyświetlenia następnego wskaźnika. Musimy albo ponownie załadować okno, albo przenieść je na następną stronę administratora, na której wyświetli się wskaźnik.get_admin_url()
funkcję z dwiema zmiennymi; pierwsza to strona administratora, do której chcemy przejść dalej; a drugi to unikalny klucz tablicy wskaźnika, który chcemy wyświetlić.W dalszej części zobaczysz kod, który się zaczyna
if (!array_key_exists($tab, $tour)) {
. Tutaj określamy, czy ustawiono zmienną zapytania adresu URL. Jeśli NIE, to musimy zdefiniować pierwszy wskaźnik do wyświetlenia.Ten wskaźnik używa dokładnie tych samych
id, content, button2, and function
elementów, które zostały użyte w$tour
powyższej tablicy. Pamiętaj, że drugi argumentget_admin_url()
funkcji MUSI być dokładnie taki sam jak klucz tablicy w$tour
zmiennej. To mówi skryptowi, aby przejść do następnego wskaźnika.Reszta funkcji jest używana, jeśli zmienna zapytania jest już ustawiona w adresie URL. Nie trzeba już dostosowywać tej funkcji.
Pobieranie adresu URL następnej funkcji Następna funkcja jest w rzeczywistości funkcją pomocnika ... używaną do uzyskania adresu URL administratora i przesunięcia wskaźnika.
Pamiętaj, że istnieją dwa argumenty; stronę administratora, do której jedziemy .. i zakładkę. Zakładka będzie
$tour
kluczem tablicy, do którego chcemy przejść dalej. TE MUSZĄ DOPASOWAĆ .Kiedy wywołujemy funkcję
get_admin_url()
i przekazujemy dwie zmienne; pierwsza zmienna określa następną stronę administratora .. a druga zmienna określa wskaźnik do wyświetlenia.Wreszcie ... możemy wreszcie wydrukować skrypt administratora w stopce.
Ponownie nie ma potrzeby zmieniać niczego powyżej. Ten skrypt zdefiniuje i wyświetli dwa przyciski w oknie nakładki wskaźnika. Zawsze będzie przycisk „Zamknij”; i zaktualizuje bieżącą
dismissed_pointers
opcję meta użytkownika .Drugi przycisk (przycisk akcji) wykona funkcję (nasza metoda relokacji okna).
I zamykamy klasę.
Oto kod w całości. Klasa wskaźnika WP
Możesz skopiować / wkleić to na swojej stronie deweloperskiej i odwiedzić stronę „Dashboard”. Poprowadzi Cię przez wycieczkę.
Pamiętaj, że trochę mylące jest to, że pierwszy wskaźnik jest zdefiniowany jako ostatni w kodzie. Tak ma to działać. Tablica pomieści wszystkie pozostałe wskaźniki, których chcesz użyć.
Pamiętaj, że element tablicy „id” MUSI pasować do drugiego argumentu
get_admin_url()
funkcji z polecenia „funkcja” poprzedniego elementu tablicy. W ten sposób wskaźniki „rozmawiają” ze sobą i wiedzą, jak się rozwijać.Cieszyć się!! :)
źródło