Créer un didacticiel WP pour les utilisateurs avec un pointeur administrateur à l'aide du bouton suivant pour la navigation


9

Mon objectif est de créer un tutoriel sur mes utilisateurs pour la zone d'administration. Pour y parvenir, j'utilise les pointeurs d'administration disponibles dans WP core. Mon but:

entrez la description de l'image ici

J'y suis presque. Ce que j'ai obtenu jusqu'à présent ...

Mettre les scripts wp-pointer en file d'attente:

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' );
    }
}

Fonctions d'assistance, y compris la vérification conditionnelle et le script de pied de page:

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
}

Nous sommes maintenant prêts à assembler le tableau de pointeurs:

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 ) )
        )
    );

}

Le code est explicite. Nous pouvons facilement ajouter plus de pointeurs en étendant le tableau. Tout fonctionne bien dans WP4.

Passons maintenant au problème: tous les pointeurs contextuels apparaissent en même temps, ce qui en fait une mauvaise interface pour un didacticiel.

Mon objectif est de montrer les pointeurs un par un et de permettre à l'utilisateur de cliquer sur un bouton Suivant pour naviguer dans le tutoriel. Le bouton suivant devrait ouvrir le pointeur suivant et fermer le dernier.

Comment puis-je faire ceci?

Réponses:


10

Vous appelez la .pointer( 'open' );fonction javascript sur tous les objets pointeurs, il n'est donc pas surprenant que tous les pointeurs apparaissent en même temps ...

Cela dit, je ne comprends pas pourquoi renvoyez tous les pointeurs (même non actifs) custom_admin_pointers(), puis ajoutez une fonction supplémentaire pour vérifier s'il existe des pointeurs actifs et une vérification à l'intérieur de la boucle de pointeurs ( if ( $array['active'] ) {) pour choisir d'ajouter un pointeur javascript ou pas. N'est-ce pas plus simple de ne renvoyer que des pointeurs actifs?

De plus, vous ajoutez ce javascript sur toutes les pages d'administration, ce n'est pas trop? Considérez également que certains éléments comme "# save-post" ne sont disponibles que sur la nouvelle page de publication, il n'est donc pas préférable d'ajouter les pointeurs uniquement dans la nouvelle page de pot?

Enfin, à quel point ce javascript est mélangé avec PHP, je pense que vous devriez envisager d'utiliser wp_localize_scriptpour transmettre des données à javascript.

Le plan:

  1. Déplacez les définitions de pointeurs en PHP dans un fichier séparé, de cette façon, il est facile de modifier et également de supprimer le balisage du code PHP, tout est plus lisible et maintenable
  2. Dans les pointeurs de configuration ajouter une propriété « où » qui sera utilisé pour définir dans quelle page admin une fenêtre devrait apparaître: post-new.php, index.php...
  3. Écrire une classe qui gérera le chargement, l'analyse et le filtrage des pointeurs info
  4. Écrivez quelques bonnes choses qui nous aideront à changer le bouton "Supprimer" par défaut en "Suivant"

Le # 4 peut (probablement) se faire facilement en connaissant bien le plugin pointeur, mais ce n'est pas mon cas. Je vais donc utiliser le code général jQuery pour obtenir le résultat, si quelqu'un peut améliorer mon code, j'apprécierai.


Éditer

J'ai édité le code (principalement js) car il y avait différentes choses que je n'avais pas considérées: certains pointeurs peuvent être ajoutés à la même ancre, ou les mêmes pointeurs peuvent être ajoutés à des ancres inexistantes ou non visibles. Dans tous les cas, le code précédent ne fonctionnait pas, la nouvelle version semble bien résoudre ce problème.

J'ai également configuré un Gist avec tout le code que j'ai utilisé pour tester.


Commençons par les points # 1 et # 2 : créez un fichier nommé pointers.phpet écrivez-y:

<?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; 

La configuration de tous les pointeurs est ici. Lorsque vous devez modifier quelque chose, ouvrez simplement ce fichier et modifiez-le.

Notez la propriété "where" qui est un tableau de pages où le pointeur doit être disponible.

Si vous souhaitez afficher des pointeurs dans une page générée par un plugin, recherchez cette ligne décrite ci public function filter( $page ) {- dessous et ajoutez-la die($page);immédiatement en dessous. Ouvrez ensuite la page de plug-in correspondante et utilisez cette chaîne dans la wherepropriété.

Ok, maintenant le point # 3 .

Avant d'écrire la classe, je veux juste coder une interface: j'y mettrai des commentaires pour que vous puissiez mieux comprendre ce que fera la classe.

<?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 );

}

Je pense que cela devrait être assez clair. Écrivons maintenant la classe, elle contiendra les 2 méthodes de l'interface plus le constructeur.

<?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;
  }
}

Le code est très simple et fait exactement ce que l'interface attend.

Cependant, la classe ne fait rien par elle-même, nous avons besoin d'un crochet où instancier la classe et lancer les 2 méthodes en passant les arguments appropriés.

Le 'admin_enqueue_scripts'est parfait pour notre portée: là, nous aurons accès à la page d'administration actuelle et nous pouvons également mettre en file d'attente les scripts et les styles nécessaires.

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 );
} );

Rien de spécial: il suffit d'utiliser la classe pour obtenir des données de pointeurs et si certains pointeurs passent les filtres, mettre en file d'attente les styles et les scripts. Ensuite, transmettez les données des pointeurs au script jusqu'à l'étiquette "Suivant" localisée pour le bouton.

Ok, maintenant la partie "la plus difficile": les js. Encore une fois, je tiens à souligner que je ne connais pas le plugin de pointeur utilisé par WordPress, donc ce que je fais dans mon code peut être mieux fait si quelqu'un le sait, mais mon code fait son travail et, en général, ce n'est pas si mal.

( 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`

Avec l'aide des commentaires, le code devrait être assez clair, du moins, je l'espère.

Ok, nous avons terminé. Notre PHP est plus simple et mieux organisé, notre javascript est plus lisible, les pointeurs sont plus faciles à éditer et, plus important encore, tout fonctionne.


1
@ChristineCooper bien sûr. Ok, les problèmes sont 2: 1 pour le fonctionnement du script maintenant, vous pouvez ajouter 1 pointeur pour 1 identifiant d'ancre: utiliser la même ancre pour plus d'un pointeur entraînera l'échec du script. Le deuxième problème est que certains pointeurs utilisent des ancres pour les identifiants qui peuvent ne pas être dans la page. Par exemple, un pointeur est pour '# comment-55' dans index.php, et il n'est pas trouvé. Certains pointeurs dans post.php ciblent les métaboxes qui peuvent être cachées ... et ainsi de suite. Une fois dans la version actuelle du script, les pointeurs sont "enchaînés" si aucun n'est trouvé, tous les sous-composants ne fonctionneront pas non plus. Je vais voir s'il existe un moyen simple de surmonter ces problèmes.
gmazzap

1
@ChristineCooper Heureux que cela ait fonctionné. Je vais copier tout le code de Gist ici. La condition peut être mise juste après le add_action( 'admin_enqueue_scripts', function( $page ) {simple retour si l'utilisateur n'a pas de rôle requis.
gmazzap

Veuillez modifier la valeur 30 à 120 en ligne: "scrollTop: $ pointer.offset (). Top - 30" - La raison en est que la barre d'outils supérieure couvre occasionnellement la fenêtre du pointeur lors du défilement.
Christine Cooper

J'ai un petit problème. La page dont j'ai besoin de quelques pointeurs pour apparaître est: "admin.php? Page = plugin-path / file.php" - qu'est-ce que j'ajoute exactement au tableau where ? J'ai essayé "admin.php", "plugin-path / file.php", "file.php" et toutes les variantes auxquelles je pouvais penser. Y a-t-il une raison pour laquelle il ne peut pas détecter cette page ou est-ce que je fais mal?
Christine Cooper

1
@ChristineCooper ouvre la page d'administration du plugin et copie l'url du navigateur . Après cela, ouvrez le fichier contenant mon code ci-dessus. Trouvez la ligne public function filter( $page ) {en PointersManagerclasse, et immédiatement après cette ligne, mettez die($page);. Ouvrez votre navigateur et collez l'URL, la page mourra avec une chaîne: c'est ce que vous devez utiliser 'where'.
gmazzap

7

Ahhh .. oui. Pointeurs WordPress. Vous savez, il y a beaucoup de sentiments mitigés quand il s'agit d'utiliser des pointeurs;)

Vous étiez sur la bonne voie avec votre code ci-dessus. Mais il y a quelques problèmes.

@GM a raison sur la pointer('open')commande ouvrant tous vos pointeurs à la fois. En outre, vous ne fournissez pas de méthode pour avancer dans les pointeurs.

J'ai combattu ce même problème .. et j'ai trouvé ma propre approche. J'utilise une variable de requête dans l'URL, recharge la page sur la page d'administration où je veux afficher le pointeur suivant et laisse jQuery gérer le reste.

Classe WP Pointers

J'ai décidé d'écrire ceci en classe. Mais je vais d'abord le montrer par incréments pour vous aider à mieux comprendre ce qui se passe.

Début de la classe

// 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. Nous avons défini la classe.
  2. Nous avons construit la classe et ajouté une action à admin_enqueue_scripts.
  3. Nous avons déterminé si nos pointeurs ont déjà été rejetés.
  4. Sinon, nous continuons de mettre en file d'attente les scripts nécessaires.

Vous n'avez PAS besoin de changer quoi que ce soit dans ces premières fonctions.

Configuration du tableau d'éléments de pointeur

L'étape suivante consiste à définir chacun des pointeurs. Il y a cinq éléments que nous devons définir (extrait pour le dernier pointeur). Nous le ferons en utilisant des tableaux. Jetons un coup d'œil à la fonction:

// 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);
    }
}

D'accord .. jetons un oeil à quelques choses ici.

Tout d'abord, notre $tourtableau. Il s'agit du tableau qui contient tous les pointeurs SAUF le premier pointeur qui est affiché pour l'utilisateur (plus d'informations à ce sujet plus tard). Donc, vous voudrez commencer par le deuxième pointeur que vous avez l'intention d'afficher .. et continuer jusqu'au dernier pointeur.

Ensuite, nous avons quelques éléments qui sont très importants.

  1. Les $tourclés du tableau doivent être uniques (quick_press, site_title, quick_press_last; comme exemples ci-dessus).
  2. La commande 'id' DOIT correspondre à l'identifiant d'élément html de l'élément que vous souhaitez attacher au pointeur.
  3. La functioncommande rechargera / relocalisera la fenêtre. C'est ce qui est utilisé pour afficher le pointeur suivant. Nous devons soit recharger la fenêtre, soit la déplacer vers la page d'administration suivante où un pointeur sera affiché.
  4. Nous exécutons la get_admin_url()fonction avec deux variables; la première est la page d'administration où nous voulons aller ensuite; et le second est la clé de tableau unique du pointeur que nous souhaitons afficher.

Plus bas, vous verrez le code qui commence if (!array_key_exists($tab, $tour)) {. C'est là que nous déterminons si une variable de requête URL a été définie. Si ce n'est PAS le cas, nous devons définir le premier pointeur à afficher.

Ce pointeur utilise exactement les mêmes id, content, button2, and functionéléments que ceux utilisés dans notre $tourtableau ci-dessus. N'oubliez pas que le deuxième argument de la get_admin_url()fonction DOIT être exactement le même que la clé du tableau dans la $tourvariable. C'est ce qui indique au script d'aller au pointeur suivant.

Le reste de la fonction est utilisé si une variable de requête est déjà définie dans l'url. Il n'est plus nécessaire de régler la fonction.

Obtention de l'URL d'administration La fonction suivante est en fait une fonction d'aide ... utilisée pour obtenir l'URL d'administration et faire avancer le pointeur.

// 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;
}

N'oubliez pas qu'il y a deux arguments; la page d'administration, nous allons .. et l'onglet. L'onglet sera la $tourclé du tableau dans laquelle nous voulons aller ensuite. CEUX-CI DOIVENT CORRESPONDRE .

Ainsi, lorsque nous appelons la fonction get_admin_url()et passons les deux variables; la première variable détermine la page d'administration suivante .. et la deuxième variable détermine le pointeur à afficher.

Enfin ... nous pouvons enfin imprimer le script admin dans le pied de page.

// 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();

Encore une fois, il n'est pas nécessaire de modifier quoi que ce soit ci-dessus. Ce script définira et affichera les deux boutons dans la fenêtre de superposition du pointeur. L'un sera toujours le bouton "Fermer"; et mettra à jour la méta- dismissed_pointersoption utilisateur actuelle .

Le deuxième bouton (le bouton d'action) exécutera la fonction (notre méthode de relocalisation de fenêtre).

Et nous fermons la classe.

Voici le code dans son intégralité. Classe WP Pointer

Vous pouvez copier / coller cela dans votre site de développement et visiter la page "Tableau de bord". Il vous guidera tout au long de la visite.

Rappelez-vous, c'est un peu déroutant que le premier pointeur soit défini en dernier dans le code. C'est ainsi que cela est censé fonctionner. Le tableau contiendra tous les autres pointeurs que vous souhaitez utiliser.

N'oubliez pas que l'élément de tableau «id» DOIT correspondre au deuxième argument de la get_admin_url()fonction de la commande précédente de «fonction» de l'élément de tableau. C'est ainsi que les pointeurs se «parlent» les uns aux autres et savent comment avancer.

Prendre plaisir!! :)


C'est adorable Josh, merci beaucoup! Je vais essayer cela et voir à quel point cela fonctionne. Je dois souligner que le code de GM est celui que j'attribuerai probablement cette prime car il possède quelques fonctionnalités essentielles que j'ai demandées et qui, selon moi, sont importantes pour créer un guide, en particulier pour plusieurs pages dans wp-admin. Néanmoins, il est bon de voir une autre approche à ce sujet et sera utile pour les autres utilisateurs qui recherchent une bonne solution. Par curiosité, vous avez dit qu'il y a pas mal de sentiments mitigés quand il s'agit d'utiliser des pointeurs , vous voulez élaborer?
Christine Cooper

2
Pas de soucis :) Eh bien, les pointeurs peuvent «gêner» lorsqu'ils sont utilisés en excès. Personne ne veut visiter une page et afficher trois ou quatre pointeurs ... surtout s'ils ne sont pas liés. Supposons que deux autres plugins affichent des pointeurs, puis nous ajoutons plus de pointeurs .. cela peut devenir exagéré. La plupart des gens disent de les utiliser avec parcimonie ... mais à chacun est le leur :) Heureux que vous l'ayez fait fonctionner correctement.
josh

1
C'est aussi génial Josh, je ferais une suggestion, pour rendre cela plus flexible et l'avoir où vous pouvez simplement passer le tableau dans une fonction publique au lieu de le stocker dans le code de classe lui-même. Deuxièmement, le premier pointeur, comment il est séparé, modifiez-le afin qu'il puisse être la première clé / valeur de tableau dans le tableau de pointeurs. Juste quelques idées pour que cette classe puisse être appelée à partir d'un autre script et simplement passée dans le tableau de pointeur. Je l'aime toujours beaucoup, merci pour le partage, je vais l'utiliser!
JasonDavis

Merci @jasondavis. J'ai en fait tiré ce code d'un autre plugin que j'ai développé pour quelqu'un. Je voulais seulement le faire fonctionner correctement. Mais oui, je suis absolument d'accord avec vous ... il faut le nettoyer. Peut-être que je m'arrêterai plus tard dans la journée et jouerai à nouveau avec ça :)
josh

C'est cool, je n'ai en fait jamais eu l'intention d'utiliser les pointeurs d'administration, principalement parce qu'ils ressemblaient à un cauchemar et 2e parce que je n'en ai pas vraiment l'usage, mais votre classe les rend si faciles à utiliser maintenant que je sens que je Je dois les utiliser avec ça étant si facile avec cette classe! J'adore les petits projets / bibliothèques comme ça, bonnes choses
JasonDavis
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.