Crie um tutorial do WP para usuários com ponteiro de administrador usando o botão Avançar para navegação

9

Eu pretendo criar um tutorial sobre meus usuários para a área de administração. Para conseguir isso, estou usando os ponteiros de administrador disponíveis no núcleo do WP. Meu gol:

insira a descrição da imagem aqui

Estou quase lá. O que eu tenho até agora ...

Enfileirar scripts wp-pointer:

add_action( 'admin_enqueue_scripts', 'custom_admin_pointers_header' );

function custom_admin_pointers_header() {
    if ( custom_admin_pointers_check() ) {
        add_action( 'admin_print_footer_scripts', 'custom_admin_pointers_footer' );

        wp_enqueue_script( 'wp-pointer' );
        wp_enqueue_style( 'wp-pointer' );
    }
}

Funções auxiliares, incluindo verificação condicional e script de rodapé:

function custom_admin_pointers_check() {
    $admin_pointers = custom_admin_pointers();
    foreach ( $admin_pointers as $pointer => $array ) {
        if ( $array['active'] )
            return true;
    }
}

function custom_admin_pointers_footer() {
    $admin_pointers = custom_admin_pointers();
    ?>
    <script type="text/javascript">
        /* <![CDATA[ */
        ( function($) {
            <?php
            foreach ( $admin_pointers as $pointer => $array ) {
               if ( $array['active'] ) {
                  ?>
            $( '<?php echo $array['anchor_id']; ?>' ).pointer( {
                content: '<?php echo $array['content']; ?>',
                position: {
                    edge: '<?php echo $array['edge']; ?>',
                    align: '<?php echo $array['align']; ?>'
                },
                close: function() {
                    $.post( ajaxurl, {
                        pointer: '<?php echo $pointer; ?>',
                        action: 'dismiss-wp-pointer'
                    } );
                }
            } ).pointer( 'open' );
            <?php
         }
      }
      ?>
        } )(jQuery);
        /* ]]> */
    </script>
<?php
}

Agora estamos prontos para montar o conjunto de ponteiros:

function custom_admin_pointers() {
    $dismissed = explode( ',', (string) get_user_meta( get_current_user_id(), 'dismissed_wp_pointers', true ) );
    $version = '1_0'; // replace all periods in 1.0 with an underscore
    $prefix = 'custom_admin_pointers' . $version . '_';

    $new_pointer_content = '<h3>' . __( 'Add New Item' ) . '</h3>';
    $new_pointer_content .= '<p>' . __( 'Easily add a new post, media item, link, page or user by selecting from this drop down menu.' ) . '</p>';

    $story_pointer_content = '<h3>' . __( 'Another info' ) . '</h3>';
    $story_pointer_content .= '<p>' . __( 'Lorem ipsum...' ) . '</p>';


    return array(
        $prefix . 'new_items' => array(
            'content' => $new_pointer_content,
            'anchor_id' => '#wp-admin-bar-new-content',
            'edge' => 'top',
            'align' => 'left',
            'active' => ( ! in_array( $prefix . 'new_items', $dismissed ) )
        ),
        $prefix.'story_cover_help' => array(
            'content' => $story_pointer_content,
            'anchor_id' => '#save-post',
            'edge' => 'top',
            'align' => 'right',
            'active' => ( ! in_array( $prefix . 'story_cover_help', $dismissed ) )
        )
    );

}

O código é auto-explicativo. Podemos facilmente adicionar mais indicadores estendendo a matriz. Tudo funciona bem no WP4.

Agora, o problema: todos os ponteiros pop-up aparecem ao mesmo tempo, tornando essa uma interface ruim para um tutorial.

Meu objetivo é mostrar os ponteiros um por um e permitir que o usuário clique no botão Avançar para navegar pelo tutorial. O próximo botão deve abrir o próximo ponteiro e fechar o último.

Como posso fazer isso?

Christine Cooper
fonte

Respostas:

10

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_scriptpara passar dados para o javascript.

O plano:

  1. 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
  2. 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...
  3. Escreva uma classe que lide com o carregamento, a análise e a filtragem das informações dos ponteiros
  4. 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.phpe 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 wherepropriedade

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.

gmazzap
fonte
11
@ChristineCooper com certeza. Ok, os problemas são 2: 1º para como o script funciona agora, você pode adicionar 1 ponteiro para 1 ID de âncora: usar a mesma âncora para mais de um ponteiro fará com que o script falhe. O segundo problema é que alguns ponteiros usam âncora nos IDs que podem não estar na página. Por exemplo, um ponteiro é para '# comment-55' no index.php e não foi encontrado. Alguns ponteiros no post.php têm como meta metaboxes que podem estar ocultos ... e assim por diante. Uma vez na versão atual do script, os ponteiros são "encadeados" se um não for encontrado, todos os subsequentes também não funcionarão. Vou ver se existe uma maneira simples de superar esses problemas.
gmazzap
11
@ChristineCooper Ainda bem que funcionou. Vou copiar todo o código do Gist aqui. A condição pode ser colocada logo após o add_action( 'admin_enqueue_scripts', function( $page ) {retorno, se o usuário não tiver uma função necessária.
gmazzap
Altere o valor 30 para 120 on-line: "scrollTop: $ pointer.offset (). Top - 30" - O motivo é que a barra de ferramentas superior cobre a janela do ponteiro ocasionalmente ao rolar.
Christine Cooper
Eu tenho um pequeno problema. A página em que preciso exibir alguns ponteiros é: "admin.php? Page = plugin-path / file.php" - o que exatamente adiciono à matriz where ? Eu tentei "admin.php", "plugin-path / file.php", "file.php" e qualquer variação que eu pudesse pensar. Existe uma razão pela qual não é possível detectar esta página ou estou fazendo isso errado?
Christine Cooper
11
@ChristineCooper abra a página de administração do plug-in e copie o URL do navegador . Depois disso, abra o arquivo que contém o meu código acima. Encontre a linha public function filter( $page ) {na PointersManagerclasse e imediatamente após essa linha die($page);. Abra o navegador e cole de volta o URL, a página morrerá com uma string: é isso que você deve usar 'where'.
gmazzap
7

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

// Create as a class
class testWPpointers {

    // Define pointer version
    const DISPLAY_VERSION = 'v1.0';

    // Initiate construct
    function __construct () {
        add_action('admin_enqueue_scripts', array($this, 'admin_enqueue_scripts'));  // Hook to admin_enqueue_scripts
    }

    function admin_enqueue_scripts () {

        // Check to see if user has already dismissed the pointer tour
        $dismissed = explode (',', get_user_meta (wp_get_current_user ()->ID, 'dismissed_wp_pointers', true));
        $do_tour = !in_array ('test_wp_pointer', $dismissed);

        // If not, we are good to continue
        if ($do_tour) {

            // Enqueue necessary WP scripts and styles
            wp_enqueue_style ('wp-pointer');
            wp_enqueue_script ('wp-pointer');

            // Finish hooking to WP admin areas
            add_action('admin_print_footer_scripts', array($this, 'admin_print_footer_scripts'));  // Hook to admin footer scripts
            add_action('admin_head', array($this, 'admin_head'));  // Hook to admin head
        }
    }

    // Used to add spacing between the two buttons in the pointer overlay window.
    function admin_head () {
        ?>
        <style type="text/css" media="screen">
            #pointer-primary {
                margin: 0 5px 0 0;
            }
        </style>
        <?php
    }
  1. Nós definimos a classe.
  2. Construímos a classe e adicionamos uma ação a admin_enqueue_scripts.
  3. Determinamos se nossos indicadores já foram descartados.
  4. Caso contrário, continuamos a enfileirar os scripts necessários.

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:

// Define footer scripts
function admin_print_footer_scripts () {

    // Define global variables
    global $pagenow;
    global $current_user;

    //*****************************************************************************************************
    // This is our array of individual pointers.
    // -- The array key should be unique.  It is what will be used to 'advance' to the next pointer.
    // -- The 'id' should correspond to an html element id on the page.
    // -- The 'content' will be displayed inside the pointer overlay window.
    // -- The 'button2' is the text to show for the 'action' button in the pointer overlay window.
    // -- The 'function' is the method used to reload the window (or relocate to a new window).
    //    This also creates a query variable to add to the end of the url.
    //    The query variable is used to determine which pointer to display.
    //*****************************************************************************************************
    $tour = array (
        'quick_press' => array (
            'id' => '#dashboard_quick_press',
            'content' => '<h3>' . __('Congratulations!', 'test_lang') . '</h3>'
                . '<p><strong>' . __('WP Pointers is working properly.', 'test_lang') . '</strong></p>'
                . '<p>' . __('This pointer is attached to the "Quick Draft" admin widget.', 'test_lang') . '</p>'
                . '<p>' . __('Our next pointer will take us to the "Settings" admin menu.', 'test_lang') . '</p>',
            'button2' => __('Next', 'test_lang'),
            'function' => 'window.location="' . $this->get_admin_url('options-general.php', 'site_title') . '"'  // We are relocating to "Settings" page with the 'site_title' query var
            ),
        'site_title' => array (
            'id' => '#blogname',
            'content' => '<h3>' . __('Moving along to Site Title.', 'test_lang') . '</h3>'
            . '<p><strong>' . __('Another WP Pointer.', 'test_lang') . '</strong></p>'
            . '<p>' . __('This pointer is attached to the "Blog Title" input field.', 'test_lang') . '</p>',
            'button2' => __('Next', 'test_lang'),
            'function' => 'window.location="' . $this->get_admin_url('index.php', 'quick_press_last') . '"'  // We are relocating back to "Dashboard" with 'quick_press_last' query var
            ),
        'quick_press_last' => array (
            'id' => '#dashboard_quick_press',
            'content' => '<h3>' . __('This concludes our WP Pointers tour.', 'test_lang') . '</h3>'
            . '<p><strong>' . __('Last WP Pointer.', 'test_lang') . '</strong></p>'
            . '<p>' . __('When closing the pointer tour; it will be saved in the users custom meta.  The tour will NOT be shown to that user again.', 'test_lang') . '</p>'
            )
        );

    // Determine which tab is set in the query variable
    $tab = isset($_GET['tab']) ? $_GET['tab'] : '';
    // Define other variables
    $function = '';
    $button2 = '';
    $options = array ();
    $show_pointer = false;

    // *******************************************************************************************************
    // This will be the first pointer shown to the user.
    // If no query variable is set in the url.. then the 'tab' cannot be determined... and we start with this pointer.
    // *******************************************************************************************************
    if (!array_key_exists($tab, $tour)) {

        $show_pointer = true;
        $file_error = true;

        $id = '#dashboard_right_now';  // Define ID used on page html element where we want to display pointer
        $content = '<h3>' . sprintf (__('Test WP Pointers %s', 'test_lang'), self::DISPLAY_VERSION) . '</h3>';
        $content .= __('<p>Welcome to Test WP Pointers admin tour!</p>', 'test_lang');
        $content .= __('<p>This pointer is attached to the "At a Glance" dashboard widget.</p>', 'test_lang');
        $content .= '<p>' . __('Click the <em>Begin Tour</em> button to get started.', 'test_lang' ) . '</p>';

        $options = array (
            'content' => $content,
            'position' => array ('edge' => 'top', 'align' => 'left')
            );
        $button2 = __('Begin Tour', 'test_lang' );
        $function = 'document.location="' . $this->get_admin_url('index.php', 'quick_press') . '";';
    }
    // Else if the 'tab' is set in the query variable.. then we can determine which pointer to display
    else {

        if ($tab != '' && in_array ($tab, array_keys ($tour))) {

            $show_pointer = true;

            if (isset ($tour[$tab]['id'])) {
                $id = $tour[$tab]['id'];
            }

            $options = array (
                'content' => $tour[$tab]['content'],
                'position' => array ('edge' => 'top', 'align' => 'left')
            );

            $button2 = false;
            $function = '';

            if (isset ($tour[$tab]['button2'])) {
                $button2 = $tour[$tab]['button2'];
            }
            if (isset ($tour[$tab]['function'])) {
                $function = $tour[$tab]['function'];
            }
        }
    }

    // If we are showing a pointer... let's load the jQuery.
    if ($show_pointer) {
        $this->make_pointer_script ($id, $options, __('Close', 'test_lang'), $button2, $function);
    }
}

Ok .. vamos dar uma olhada em algumas coisas aqui.

Primeiro, nossa $tourmatriz. 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.

  1. As $tourchaves da matriz devem ser exclusivas (quick_press, site_title, quick_press_last; como exemplos acima).
  2. O comando 'id' DEVE corresponder ao id do elemento html do item que você deseja anexar ao ponteiro.
  3. O functioncomando 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.
  4. Nós executamos a 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 functionitens usados ​​em nossa $tourmatriz acima. Lembre-se, o segundo argumento da get_admin_url()função DEVE ser exatamente o mesmo que a chave da matriz na $tourvariá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.

// This function is used to reload the admin page.
// -- $page = the admin page we are passing (index.php or options-general.php)
// -- $tab = the NEXT pointer array key we want to display
function get_admin_url($page, $tab) {

    $url = admin_url();
    $url .= $page.'?tab='.$tab;

    return $url;
}

Lembre-se, existem dois argumentos; a página de administração que vamos .. e a guia. A guia será a $tourchave 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é.

// Print footer scripts
function make_pointer_script ($id, $options, $button1, $button2=false, $function='') {

    ?>
    <script type="text/javascript">

        (function ($) {

            // Define pointer options
            var wp_pointers_tour_opts = <?php echo json_encode ($options); ?>, setup;

            wp_pointers_tour_opts = $.extend (wp_pointers_tour_opts, {

                // Add 'Close' button
                buttons: function (event, t) {

                    button = jQuery ('<a id="pointer-close" class="button-secondary">' + '<?php echo $button1; ?>' + '</a>');
                    button.bind ('click.pointer', function () {
                        t.element.pointer ('close');
                    });
                    return button;
                },
                close: function () {

                    // Post to admin ajax to disable pointers when user clicks "Close"
                    $.post (ajaxurl, {
                        pointer: 'test_wp_pointer',
                        action: 'dismiss-wp-pointer'
                    });
                }
            });

            // This is used for our "button2" value above (advances the pointers)
            setup = function () {

                $('<?php echo $id; ?>').pointer(wp_pointers_tour_opts).pointer('open');

                <?php if ($button2) { ?>

                    jQuery ('#pointer-close').after ('<a id="pointer-primary" class="button-primary">' + '<?php echo $button2; ?>' + '</a>');
                    jQuery ('#pointer-primary').click (function () {
                        <?php echo $function; ?>  // Execute button2 function
                    });
                    jQuery ('#pointer-close').click (function () {

                        // Post to admin ajax to disable pointers when user clicks "Close"
                        $.post (ajaxurl, {
                            pointer: 'test_wp_pointer',
                            action: 'dismiss-wp-pointer'
                        });
                    })
                <?php } ?>
            };

            if (wp_pointers_tour_opts.position && wp_pointers_tour_opts.position.defer_loading) {

                $(window).bind('load.wp-pointers', setup);
            }
            else {
                setup ();
            }
        }) (jQuery);
    </script>
    <?php
}
} 
$testWPpointers = new testWPpointers();

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_pointersopçã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!! :)

Josh
fonte
Isso é adorável Josh, muito obrigado! Vou tentar isso e ver como ele funciona. Devo destacar que o código da GM é o que provavelmente concederá a essa recompensa, pois possui algumas características essenciais que solicitei e que acredito serem importantes para criar um guia, principalmente para várias páginas no wp-admin. No entanto, é ótimo ver outra abordagem disso e será útil para outros usuários que procuram uma boa solução. Por curiosidade, você disse que existem muitos sentimentos contraditórios quando se trata de usar ponteiros , quer elaborar?
Christine Cooper
2
Não se preocupe :) Bem, os ponteiros podem "atrapalhar" quando usados ​​em excesso. Ninguém quer visitar uma página e exibir três ou quatro ponteiros ... especialmente se eles não estiverem relacionados. Digamos que outros dois plug-ins estejam mostrando ponteiros, e depois adicionamos mais ponteiros. Ele pode se tornar um exagero. A maioria das pessoas diz usá-las com moderação ... mas cada uma é sua :) :) Fico feliz que você tenha funcionado corretamente.
josh
11
Isso também é incrível, Josh, eu faria uma sugestão, para tornar isso mais flexível e permitir que você possa simplesmente passar o array para uma função pública, em vez de armazená-lo dentro do próprio código de classe. Segundo, o primeiro ponteiro, como está separado, modifica-o para que possa ser a primeira chave / valor da matriz no array de ponteiros. Apenas algumas idéias para que essa classe possa ser chamada de outro script e simplesmente transmitida na matriz do ponteiro. Ainda gosto muito, obrigado por compartilhar. Vou usar isso!
JasonDavis
Obrigado @jasondavis. Na verdade, eu peguei esse código de outro plug-in que desenvolvi para alguém. Eu só estava interessado em fazê-lo funcionar corretamente. Mas sim, concordo plenamente com você ... ele precisa ser limpo. Talvez eu pare hoje mais tarde e mexa com isso de novo :) Você é demais, mano!
josh
É legal, eu nunca tive a intenção de usar os Admin Pointers, principalmente porque eles pareciam um pesadelo e o segundo porque eu não tenho nenhum uso real para eles, mas sua classe faz com que pareçam tão fáceis de usar agora que sinto que tem que usá-los com isso é tão fácil com essa classe! Eu amo pequenos projectos / bibliotecas assim, coisas boas
JasonDavis