Você está chamando a .pointer( 'open' );
função javascript em todos os objetos de ponteiros, portanto, não é surpresa que todos os ponteiros apareçam ao mesmo tempo ...
Dito isso, não entendo por que você retorna todos os ponteiros (mesmo os não ativos) custom_admin_pointers()
e adiciona uma função adicional para verificar se há alguns ponteiros ativos e uma verificação dentro dos ponteiros loop ( if ( $array['active'] ) {
) para optar por adicionar o ponteiro javascript ou não. Não é mais simples retornar apenas ponteiros ativos?
Além disso, você está adicionando javascript em todas as páginas de administração, não é demais? Considere também que alguns elementos como "# save-post" estão disponíveis apenas na nova página de postagem; portanto, não é melhor adicionar os ponteiros apenas na nova página do pote?
Finalmente, quão confuso é o javascript misturado ao PHP, acho que você deve considerar usar wp_localize_script
para passar dados para o javascript.
O plano:
- Mova as definições de ponteiros no PHP para um arquivo separado, dessa maneira é fácil editar e também remover a marcação do código PHP, tudo resulta mais legível e fácil de manter
- Nos ponteiros configuração adicionar uma propriedade "onde", que será usado para definir em que página de administração um pop-up deve aparecer:
post-new.php
, index.php
...
- Escreva uma classe que lide com o carregamento, a análise e a filtragem das informações dos ponteiros
- Escreva alguma coisa js que nos ajude a alterar o botão "Remover" padrão para "Avançar"
O número 4 pode (provavelmente) conhecer facilmente o plug-in do ponteiro, mas não é o meu caso. Então, usarei o código jQuery geral para obter o resultado, se alguém puder melhorar meu código, eu aprecio.
Editar
Editei o código (principalmente js) porque existem coisas diferentes que eu não havia considerado: alguns ponteiros podem ser adicionados à mesma âncora ou os mesmos ponteiros podem ser adicionados a âncoras inexistentes ou não visíveis. Em todo o caso, o código anterior não funcionou, a nova versão parece tratar bem esses problemas.
Também configurei um Gist com todo o código que usei para testar.
Vamos começar com os pontos 1 e 2 : crie um arquivo nomeado pointers.php
e escreva lá:
<?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;
Toda a configuração de ponteiros está aqui. Quando você precisar alterar algo, basta abrir este arquivo e editá-lo.
Observe a propriedade "where" que é uma matriz de páginas onde o ponteiro deve estar disponível.
Se você deseja exibir ponteiros em uma página gerada por um plug-in, procure esta linha descrita abaixo public function filter( $page ) {
e adicione die($page);
imediatamente abaixo dela. Em seguida, abra a respectiva página do plug-in e use essa sequência na where
propriedade
Ok, agora o ponto # 3 .
Antes de escrever a classe, eu apenas quero codificar uma interface: lá colocarei comentários para que você possa entender melhor o que a classe fará.
<?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 );
}
Eu acho que deveria ficar bem claro. Agora vamos escrever a classe, ela conterá os 2 métodos da interface mais o construtor.
<?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;
}
}
O código é muito simples e faz exatamente o que a interface espera.
No entanto, a classe não faz nada por si mesma, precisamos de um gancho para instanciar a classe e lançar os 2 métodos, passando argumentos apropriados.
O 'admin_enqueue_scripts'
é perfeito para o nosso escopo: lá teremos acesso à página de administração atual e também podemos enfileirar os scripts e estilos necessários.
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 );
} );
Nada de especial: basta usar a classe para obter dados dos ponteiros e, se alguns deles passarem nos filtros, enfileiram estilos e scripts. Em seguida, passe os dados dos ponteiros para o script junto ao rótulo "Avançar" localizado para o botão.
Ok, agora a parte "mais difícil": os js. Mais uma vez, quero destacar que não conheço o plug-in de ponteiro que o WordPress usa, portanto, o que faço no meu código pode ser melhor se alguém o souber, no entanto, meu código faz seu trabalho e, de maneira geral, não é tão ruim.
( 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`
Com a ajuda dos comentários, o código deve ficar bem claro, pelo menos, espero que sim.
Ok, terminamos. Nosso PHP é mais simples e melhor organizado, nosso javascript é mais legível, os ponteiros são mais fáceis de editar e, mais importante, tudo funciona.
add_action( 'admin_enqueue_scripts', function( $page ) {
retorno, se o usuário não tiver uma função necessária.public function filter( $page ) {
naPointersManager
classe e imediatamente após essa linhadie($page);
. Abra o navegador e cole de volta o URL, a página morrerá com uma string: é isso que você deve usar'where'
.Ahhh sim. Ponteiros do WordPress. Você sabe, existem muitos sentimentos contraditórios quando se trata de usar ponteiros;)
Você estava no caminho certo com o seu código acima. Mas existem alguns problemas.
@GM está correto sobre o
pointer('open')
comando que abre todos os seus ponteiros de uma só vez. Além disso, você não está fornecendo um método para avançar através de ponteiros.Eu lutei contra esse mesmo problema ... e criei minha própria abordagem. Uso uma variável de consulta no URL, recarrego a página na página de administração onde desejo exibir o próximo ponteiro e deixo o jQuery lidar com o resto.
Classe de ponteiros WP
Eu decidi escrever isso como uma aula. Mas eu vou mostrá-lo em incrementos no início para ajudar você a entender melhor o que está acontecendo.
Começando a aula
admin_enqueue_scripts
.Você NÃO precisa alterar nada nessas primeiras funções.
Configurando a matriz de itens de ponteiro
O próximo passo é definir cada um dos ponteiros. Há cinco itens que precisamos definir (exceto o último ponteiro). Faremos isso usando matrizes. Vamos dar uma olhada na função:
Ok .. vamos dar uma olhada em algumas coisas aqui.
Primeiro, nossa
$tour
matriz. Essa é a matriz que contém todos os ponteiros, EXCETO o primeiro ponteiro exibido ao usuário (mais sobre isso posteriormente). Portanto, você deseja começar com o segundo ponteiro que deseja mostrar .. e continuar até o último ponteiro.Em seguida, temos alguns itens que são muito importantes.
$tour
chaves da matriz devem ser exclusivas (quick_press, site_title, quick_press_last; como exemplos acima).function
comando recarregará / realocará a janela. É isso que é usado para mostrar o próximo ponteiro. Temos que recarregar a janela ou realocá-la para a próxima página de administração, onde um ponteiro será exibido.get_admin_url()
função com duas variáveis; a primeira é a página de administração para onde queremos ir; e o segundo é a chave de matriz exclusiva do ponteiro que queremos exibir.Mais abaixo, você verá o código que começa
if (!array_key_exists($tab, $tour)) {
. É aqui que determinamos se uma variável de consulta de URL foi definida. Se não tiver, precisamos definir o primeiro ponteiro a ser exibido.Esse ponteiro usa exatamente os mesmos
id, content, button2, and function
itens usados em nossa$tour
matriz acima. Lembre-se, o segundo argumento daget_admin_url()
função DEVE ser exatamente o mesmo que a chave da matriz na$tour
variável. Isso é o que diz ao script para ir para o próximo ponteiro.O restante da função é usado se uma variável de consulta já estiver definida no URL. Não há necessidade de ajustar mais a função.
Obtendo o URL do administrador A próxima função é na verdade uma função auxiliar ... usada para obter o URL do administrador e avançar o ponteiro.
Lembre-se, existem dois argumentos; a página de administração que vamos .. e a guia. A guia será a
$tour
chave da matriz que queremos ir para a próxima. Estes devem corresponder .Então, quando chamamos a função
get_admin_url()
e passamos as duas variáveis; a primeira variável determina a próxima página de administração. e a segunda variável determina qual ponteiro deve ser exibido.Por fim ... finalmente podemos imprimir o script de administração no rodapé.
Novamente, não há necessidade de alterar nada acima. Este script definirá e produzirá os dois botões na janela de sobreposição do ponteiro. Um sempre será o botão "Fechar"; e atualizará a
dismissed_pointers
opção atual de meta do usuário .O segundo botão (o botão de ação) executará a função (nosso método de realocação de janelas).
E encerramos a aula.
Aqui está o código em sua totalidade. Classe WP Pointer
Você pode copiar / colar isso no seu site de desenvolvimento e visitar a página "Painel". Ele irá guiá-lo através do passeio.
Lembre-se, é um pouco confuso que o primeiro ponteiro seja definido por último no código. É assim que deve funcionar. A matriz conterá todo o restante dos ponteiros que você deseja usar.
Lembre-se, o item da matriz 'id' DEVE coincidir com o segundo argumento da
get_admin_url()
função do comando 'função' do item da matriz anterior. É assim que os ponteiros 'conversam' e sabem como avançar.Aproveitar!! :)
fonte