Вы вызываете .pointer( 'open' );
функцию javascript для всех объектов указателей, поэтому неудивительно, что все указатели появляются одновременно ...
Тем не менее, я не понимаю, почему вы возвращаете все указатели (даже неактивные) из, custom_admin_pointers()
а затем добавляете дополнительную функцию для проверки наличия каких-либо активных указателей и проверки внутри цикла указателей ( if ( $array['active'] ) {
), чтобы выбрать добавление указателя javascript или нет. Не проще ли просто вернуть только активные указатели?
Более того, вы добавляете этот javascript на все страницы администратора, не слишком ли много? Также учтите, что некоторые элементы, такие как "# save-post", доступны только на новой странице поста, так что не лучше ли добавлять указатели только на новую страницу банка?
Наконец, насколько беспорядочно этот javascript перепутан с PHP, я думаю, вам стоит подумать об использовании wp_localize_script
для передачи данных в javascript.
План:
- Переместите определения указателей в PHP в отдельный файл, таким образом, его легко редактировать, а также удалять разметку из кода PHP, все становится более читабельным и понятным
- В указателях конфигурации добавить свойство «где» , который будет использоваться для набора , в котором администратор страницы должно появиться всплывающее окно:
post-new.php
, index.php
...
- Напишите класс, который будет обрабатывать загрузку, разбор и фильтрацию информации указателей.
- Напишите некоторые js goodness, которые помогут нам изменить стандартную кнопку «Удалить» на «Далее»
# 4 может (вероятно) легко сделать , зная указатель плагин хорошо, но это не мой случай. Поэтому я буду использовать общий код jQuery для получения результата, если кто-то может улучшить мой код, я буду признателен.
редактировать
Я отредактировал код (в основном js), потому что есть разные вещи, которые я не учел: некоторые указатели могут быть добавлены к одной и той же привязке, или одинаковые указатели могут быть добавлены к несуществующим или невидимым привязкам. Во всем этом случае предыдущий код не работал, новая версия, кажется, хорошо решает эти проблемы.
Я также настроил Gist со всем кодом, который использовал для тестирования.
Давайте начнем с пунктов # 1 и # 2 : создайте файл с именем pointers.php
и напишите там:
<?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;
Все настройки указателей здесь. Когда вам нужно что-то изменить, просто откройте этот файл и отредактируйте его.
Обратите внимание на свойство where, которое представляет собой массив страниц, где указатель должен быть доступен.
Если вы хотите отобразить указатели на странице, сгенерированной плагином, найдите эту строку, обрисованную в общих чертах ниже, public function filter( $page ) {
и добавьте die($page);
сразу под ней. Затем откройте соответствующую страницу плагина и используйте эту строку в where
свойстве.
Хорошо, теперь точка № 3 .
Прежде чем писать класс, я просто хочу написать интерфейс: там я добавлю комментарии, чтобы вы могли лучше понять, что будет делать класс.
<?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 );
}
Я думаю, должно быть довольно ясно. Теперь давайте напишем класс, он будет содержать 2 метода из интерфейса плюс конструктор.
<?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;
}
}
Код очень прост и выполняет именно то, что ожидает интерфейс.
Однако сам по себе класс ничего не делает, нам нужен хук, чтобы создать экземпляр класса и запустить 2 метода, передавая соответствующие аргументы.
'admin_enqueue_scripts'
Идеально подходит для нашей области: там мы будем иметь доступ к текущей странице администратора , и мы можем также Епдиеие скрипты и стили необходимы.
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 );
} );
Ничего особенного: просто использование класса для получения данных указателей и, если некоторые указатели проходят фильтры, ставят в очередь стили и сценарии. Затем передайте данные указателей в скрипт вместе с локализованной меткой «Далее» для кнопки.
Хорошо, теперь самая сложная часть: JS. Опять же, я хочу подчеркнуть, что я не знаю, какой плагин-указатель использует WordPress, поэтому то, что я делаю в своем коде, можно сделать лучше, если кто-то это знает, однако мой код работает и, вообще говоря, это не так уж плохо.
( 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`
С помощью комментариев код должен быть довольно понятным, по крайней мере, я на это надеюсь.
Хорошо, мы закончили. Наш PHP проще и лучше организован, наш JavaScript более читабелен, указатели легче редактировать и, что более важно, все работает.
add_action( 'admin_enqueue_scripts', function( $page ) {
простого возврата, если у пользователя нет требуемой роли.public function filter( $page ) {
вPointersManager
классе и сразу после этой строки поставьтеdie($page);
. Откройте браузер и вставьте URL-адрес, страница умрет со строкой: это то, что вы должны использовать как'where'
.Аааа .. да. Указатели WordPress. Вы знаете, когда дело доходит до использования указателей, существует довольно много смешанных чувств;)
Вы были на правильном пути с вашим кодом выше. Но есть пара вопросов.
@GM прав насчет
pointer('open')
команды, открывающей все ваши указатели одновременно. Кроме того, вы не предоставляете метод продвижения по указателям.Я боролся с этой же проблемой .. и придумал свой собственный подход. Я использую переменную запроса в URL, перезагружаю страницу на страницу администратора, где я хочу отобразить следующий указатель, и позволяю jQuery обрабатывать все остальное.
WP Pointers Class
Я решил написать это как класс. Но сначала я собираюсь показать это постепенно, чтобы помочь вам лучше понять, что происходит.
Начало урока
admin_enqueue_scripts
.Вам не нужно ничего менять в этих первых функциях.
Настройка массива элементов указателя
Следующим шагом является определение каждого из указателей. Нам нужно определить пять элементов (кроме последнего указателя). Мы сделаем это с помощью массивов. Давайте посмотрим на функцию:
Хорошо ... давайте посмотрим на несколько вещей здесь.
Во-первых, наш
$tour
массив. Это массив, который содержит все указатели, КРОМЕ первого указателя, который отображается пользователю (подробнее об этом позже). Итак, вы хотите начать со второго указателя, который вы намереваетесь показать ... и продолжить до последнего указателя.Далее у нас есть несколько пунктов, которые очень важны.
$tour
ключи массива должны быть уникальными (quick_press, SITE_TITLE, quick_press_last; в качестве примеров выше).function
перезагрузит / переместит окно. Это то, что используется для отображения следующего указателя. Мы должны либо перезагрузить окно, либо переместить его на следующую страницу администратора, где будет отображаться указатель.get_admin_url()
функцию с двумя переменными; первая страница администратора, куда мы хотим перейти; а второй - это уникальный ключ массива указателя, который мы хотим отобразить.Далее вы увидите код, который начинается
if (!array_key_exists($tab, $tour)) {
. Здесь мы определяем, была ли установлена переменная URL-запроса. Если он НЕ, то нам нужно определить первый указатель для отображения.Этот указатель использует те же
id, content, button2, and function
элементы, что и в нашем$tour
массиве выше. Помните, что второй аргументget_admin_url()
функции ДОЛЖЕН быть точно таким же, как ключ массива в$tour
переменной. Это то, что говорит сценарию перейти к следующему указателю.Остальная часть функции используется, если в URL уже установлена переменная запроса. Больше нет необходимости настраивать функцию.
Получение адреса администратора Следующая функция на самом деле является вспомогательной функцией ... используется для получения адреса администратора и перемещения указателя.
Помните, что есть два аргумента; страницу администратора мы собираемся .. и вкладку. Вкладка будет
$tour
ключом массива, к которому мы хотим перейти далее. ЭТО ДОЛЖНО СООТВЕТСТВОВАТЬ .Итак, когда мы вызываем функцию
get_admin_url()
и передаем две переменные; первая переменная определяет следующую страницу администратора .. а вторая переменная определяет, какой указатель отображать.Наконец ... мы можем наконец напечатать скрипт администратора в нижний колонтитул.
Опять же, нет необходимости что-либо менять выше. Этот скрипт определит и выведет две кнопки в окне наложения указателя. Всегда будет кнопка «Закрыть»; и обновит мета-
dismissed_pointers
опцию текущего пользователя .Вторая кнопка (кнопка действия) выполнит функцию (наш метод перемещения окна).
И мы закрываем класс.
Вот код целиком. WP Pointer Class
Вы можете скопировать / вставить это на свой сайт разработчика и посетить страницу «Панель инструментов». Он проведет вас через тур.
Помните, это немного сбивает с толку, что первый указатель определяется последним в коде. Вот как это должно работать. Массив будет содержать все остальные указатели, которые вы хотите использовать.
Помните, что элемент массива 'id' ДОЛЖЕН совпадать со вторым аргументом
get_admin_url()
функции из предыдущей команды 'function' элемента массива. Именно так указатели «общаются» друг с другом и знают, как продвигаться.Наслаждаться!! :)
источник