Tạo hướng dẫn WP cho người dùng bằng con trỏ quản trị bằng nút tiếp theo để điều hướng


9

Tôi đặt mục tiêu tạo một hướng dẫn về người dùng của mình cho khu vực quản trị. Để đạt được điều này, tôi đang sử dụng các con trỏ quản trị viên có sẵn trong lõi WP. Mục tiêu của tôi:

nhập mô tả hình ảnh ở đây

Tôi gần như có. Những gì tôi đã nhận được cho đến nay ...

Enqueue wp-con trỏ script:

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

Các hàm trợ giúp, bao gồm kiểm tra có điều kiện và tập lệnh chân trang:

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
}

Bây giờ chúng tôi đã sẵn sàng để kết hợp các mảng con trỏ:

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

}

Mã này là tự giải thích. Chúng ta có thể dễ dàng thêm nhiều con trỏ bằng cách mở rộng mảng. Mọi thứ hoạt động tốt trong WP4.

Bây giờ đến vấn đề: Tất cả các con trỏ bật lên xuất hiện cùng một lúc, làm cho điều này trở thành một giao diện kém cho một hướng dẫn.

Mục đích của tôi là hiển thị từng con trỏ và cho phép người dùng nhấp vào nút Tiếp theo để điều hướng qua hướng dẫn. Nút tiếp theo sẽ mở con trỏ tiếp theo và đóng cái cuối cùng.

Tôi có thể làm cái này như thế nào?

Câu trả lời:


10

Bạn đang gọi .pointer( 'open' );hàm javascript trên tất cả các đối tượng con trỏ, vì vậy không có gì ngạc nhiên khi tất cả các con trỏ xuất hiện cùng một lúc ...

Điều đó nói rằng, tôi không hiểu tại sao bạn trả lại tất cả các con trỏ (ngay cả những con trỏ không hoạt động) custom_admin_pointers()và sau đó thêm một chức năng bổ sung để kiểm tra xem có một số con trỏ hoạt động và kiểm tra bên trong vòng lặp con trỏ ( if ( $array['active'] ) {) để chọn thêm con trỏ javascript hay không. Không đơn giản hơn chỉ trả về con trỏ hoạt động?

Hơn nữa, bạn đang thêm javascript đó trên tất cả các trang quản trị, có quá nhiều không? Cũng xem xét rằng một số yếu tố như "# save-post" chỉ khả dụng trên trang bài đăng mới, vì vậy không tốt hơn chỉ thêm các con trỏ trong trang pot mới?

Cuối cùng, làm thế nào lộn xộn mà javascript trộn lẫn với PHP, tôi nghĩ bạn nên xem xét sử dụng wp_localize_scriptđể truyền dữ liệu sang javascript.

Kế hoạch:

  1. Di chuyển các định nghĩa con trỏ trong PHP sang một tệp riêng biệt, theo cách này dễ dàng chỉnh sửa và cũng loại bỏ đánh dấu khỏi mã PHP, mọi thứ đều dễ đọc và dễ bảo trì hơn
  2. Trong cấu hình con trỏ, thêm một thuộc tính "trong đó" sẽ được sử dụng để đặt trong đó trang quản trị sẽ xuất hiện một cửa sổ bật lên : post-new.php, index.php...
  3. Viết một lớp sẽ xử lý việc tải, phân tích cú pháp và lọc thông tin con trỏ
  4. Viết một số tính tốt của js sẽ giúp chúng ta thay đổi nút "Xóa" mặc định thành "Tiếp theo"

Các # 4 lon (có lẽ) thực hiện một cách dễ dàng biết con trỏ cắm tốt, nhưng nó không phải là trường hợp của tôi. Vì vậy, tôi sẽ sử dụng mã jQuery chung để thu được kết quả, nếu ai đó có thể cải thiện mã của tôi, tôi sẽ đánh giá cao.


Biên tập

Tôi đã chỉnh sửa mã (chủ yếu là js) vì có những điều khác nhau mà tôi chưa xem xét: một số con trỏ có thể được thêm vào cùng một neo hoặc có thể thêm cùng một con trỏ vào các neo không tồn tại hoặc không nhìn thấy được. Trong tất cả trường hợp mã trước đó không hoạt động, phiên bản mới dường như giải quyết vấn đề đó một cách độc đáo.

Tôi cũng đã thiết lập Gist với tất cả mã tôi đã sử dụng để kiểm tra.


Hãy bắt đầu với các điểm # 1# 2 : tạo một tệp có tên pointers.phpvà viết ở đó:

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

Tất cả các cấu hình con trỏ ở đây. Khi bạn cần thay đổi một cái gì đó, chỉ cần mở tệp này và chỉnh sửa nó.

Lưu ý thuộc tính "where" là một mảng các trang nơi con trỏ sẽ có sẵn.

Nếu bạn muốn hiển thị các con trỏ trong một trang được tạo bởi một plugin, hãy tìm dòng này được nêu bên dưới public function filter( $page ) {và thêm die($page);ngay bên dưới nó. Sau đó mở trang plugin tương ứng và sử dụng chuỗi đó trong thuộc wheretính.

Ok, bây giờ là điểm số 3 .

Trước khi viết lớp tôi chỉ muốn viết mã một giao diện: ở đó tôi sẽ đưa ý kiến ​​để bạn có thể hiểu rõ hơn về những gì lớp sẽ làm.

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

}

Tôi nghĩ nên khá rõ ràng. Bây giờ hãy viết lớp, nó sẽ chứa 2 phương thức từ giao diện cộng với hàm tạo.

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

Mã rất đơn giản và thực hiện chính xác những gì giao diện mong đợi.

Tuy nhiên, lớp không làm gì cả, chúng ta cần một cái móc để khởi tạo lớp nad khởi chạy 2 phương thức truyền các đối số thích hợp.

Điều 'admin_enqueue_scripts'này là hoàn hảo cho phạm vi của chúng tôi: ở đó chúng tôi sẽ có quyền truy cập vào trang quản trị hiện tại và chúng tôi cũng có thể liệt kê các tập lệnh và kiểu cần thiết.

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

Không có gì đặc biệt: chỉ sử dụng lớp để lấy dữ liệu con trỏ và nếu một số con trỏ vượt qua các bộ lọc và các kiểu script. Sau đó chuyển dữ liệu con trỏ tới tập lệnh cùng với nhãn "Tiếp theo" được bản địa hóa cho nút.

Ok, bây giờ là phần "khó nhất": js. Một lần nữa tôi muốn nhấn mạnh rằng tôi không biết plugin con trỏ mà WordPress sử dụng, vì vậy những gì tôi làm trong mã của mình có thể được thực hiện tốt hơn nếu ai đó biết, tuy nhiên mã của tôi hoạt động tốt và nói chung - nó không quá tệ.

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

Với sự giúp đỡ của các bình luận, mã này sẽ khá rõ ràng, ít nhất, tôi hy vọng vậy.

Ok chúng ta đã xong. PHP của chúng tôi đơn giản hơn và được tổ chức tốt hơn, javascript của chúng tôi dễ đọc hơn, các con trỏ dễ chỉnh sửa hơn và quan trọng hơn, mọi thứ đều hoạt động.


1
@ChristineCooper chắc chắn. Ok, vấn đề là 2: Thứ nhất về cách hoạt động của tập lệnh bây giờ, bạn có thể thêm 1 con trỏ cho 1 neo id: sử dụng cùng một neo cho nhiều hơn một con trỏ sẽ khiến tập lệnh thất bại. Vấn đề thứ hai là một số con trỏ sử dụng neo cho id có thể không có trong trang. Ví dụ: một con trỏ dành cho '# comment-55' trong tệp index.php và không tìm thấy. Một số con trỏ trong metaboxes mục tiêu post.php có thể bị ẩn ... và cứ thế. Một khi trong phiên bản hiện tại của các con trỏ tập lệnh bị "xiềng xích" nếu không tìm thấy thì tất cả các phần sau sẽ không hoạt động. Tôi sẽ xem liệu có cách nào đơn giản để khắc phục những vấn đề đó không.
gmazzap

1
@ChristineCooper Vui mừng vì nó hoạt động. Tôi sẽ sao chép tất cả mã từ Gist trở lại đây. Điều kiện có thể được đặt ngay sau khi add_action( 'admin_enqueue_scripts', function( $page ) {trở lại đơn giản nếu người dùng không có vai trò cần thiết.
gmazzap

Vui lòng thay đổi giá trị 30 thành 120 trên dòng: "scrollTop: $ con trỏ.offset (). Top - 30" - Lý do là vì thanh công cụ trên cùng thỉnh thoảng che cửa sổ con trỏ khi cuộn.
Christine Cooper

Tôi có một vấn đề nhỏ. Trang tôi cần một số gợi ý để xuất hiện là: "admin.php page = cắm-path / file.php?" - chính xác những gì tôi thêm vào các nơi mảng? Tôi đã thử "admin.php", "plugin-path / file.php", "file.php" và bất kỳ biến thể nào tôi có thể nghĩ ra. Có một lý do tại sao nó không thể phát hiện trang này hoặc tôi đang làm điều này sai?
Christine Cooper

1
@ChristineCooper mở trang quản trị plugin và sao chép url từ trình duyệt . Sau đó, mở tệp chứa mã của tôi ở trên. Tìm dòng public function filter( $page ) {trong PointersManagerlớp, và ngay sau dòng đó đặt die($page);. Mở trình duyệt của bạn và dán lại url, trang sẽ chết với một chuỗi: đó là những gì bạn phải sử dụng như 'where'.
gmazzap

7

À .. vâng. Con trỏ WordPress. Bạn biết đấy, có khá nhiều cảm giác lẫn lộn khi sử dụng con trỏ;)

Bạn đã đi đúng hướng với mã của bạn ở trên. Nhưng có một vài vấn đề.

@GM là chính xác về pointer('open')lệnh mở tất cả các con trỏ của bạn cùng một lúc. Hơn nữa, bạn không cung cấp một phương pháp để tiến qua con trỏ.

Tôi đã chiến đấu với vấn đề tương tự .. và đưa ra cách tiếp cận của riêng tôi. Tôi sử dụng một biến truy vấn trong url, tải lại trang vào trang quản trị nơi tôi muốn hiển thị con trỏ tiếp theo và để jQuery xử lý phần còn lại.

Lớp con trỏ WP

Tôi quyết định viết bài này như một lớp học. Nhưng tôi sẽ hiển thị nó theo từng bước đầu tiên để giúp bạn hiểu rõ hơn về những gì đang xảy ra.

Bắt đầu lớp học

// 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. Chúng tôi đã xác định lớp.
  2. Chúng tôi xây dựng lớp và thêm một hành động admin_enqueue_scripts.
  3. Chúng tôi xác định nếu con trỏ của chúng tôi đã bị loại bỏ.
  4. Nếu không, chúng tôi tiếp tục tranh thủ các kịch bản cần thiết.

Bạn KHÔNG cần phải thay đổi bất cứ điều gì trong các chức năng đầu tiên này.

Thiết lập mảng các mục con trỏ

Bước tiếp theo là xác định từng con trỏ. Có năm mục chúng ta cần xác định (trích cho con trỏ cuối cùng). Chúng tôi sẽ làm điều này bằng cách sử dụng mảng. Chúng ta hãy xem chức năng:

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

Được rồi .. chúng ta hãy xem một vài điều ở đây.

Đầu tiên, $tourmảng của chúng tôi . Đây là mảng chứa tất cả các con trỏ NGOẠI TRỪ con trỏ đầu tiên được hiển thị cho người dùng (nhiều hơn về điều này sau). Vì vậy, bạn sẽ muốn bắt đầu với con trỏ thứ hai mà bạn dự định hiển thị .. và tiếp tục đến con trỏ cuối cùng.

Tiếp theo, chúng tôi có một vài mục rất quan trọng.

  1. Các $tourkhóa mảng phải là duy nhất (quick_press, site_title, quick_press_last; như các ví dụ ở trên).
  2. Lệnh 'id' PHẢI khớp với id phần tử html của mục bạn muốn đính kèm vào con trỏ.
  3. Các functionlệnh sẽ được tải lại / di chuyển cửa sổ. Đây là những gì được sử dụng để hiển thị con trỏ tiếp theo. Chúng ta phải tải lại cửa sổ hoặc di chuyển nó đến trang quản trị tiếp theo nơi con trỏ sẽ được hiển thị.
  4. Chúng tôi chạy get_admin_url()hàm với hai biến; đầu tiên là trang quản trị nơi chúng tôi muốn đi tiếp theo; và thứ hai là khóa mảng duy nhất của con trỏ chúng tôi muốn hiển thị.

Xa hơn nữa, bạn sẽ thấy mã bắt đầu if (!array_key_exists($tab, $tour)) {. Đây là nơi chúng tôi xác định nếu một biến truy vấn url đã được đặt. Nếu nó KHÔNG, thì chúng ta cần xác định con trỏ đầu tiên để hiển thị.

Con trỏ này sử dụng các mục chính xác giống id, content, button2, and functionnhư được sử dụng trong $tourmảng của chúng tôi ở trên. Hãy nhớ rằng, đối số thứ hai của get_admin_url()hàm PHẢI chính xác giống như khóa mảng trong $tourbiến. Đây là những gì nói cho kịch bản đi đến con trỏ tiếp theo.

Phần còn lại của hàm được sử dụng nếu một biến truy vấn đã được đặt trong url. Không cần phải điều chỉnh thêm chức năng.

Lấy Url quản trị Chức năng tiếp theo thực sự là chức năng trợ giúp ... được sử dụng để lấy url quản trị và tiến con trỏ.

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

Hãy nhớ rằng, có hai đối số; trang quản trị chúng ta sẽ đến .. và tab. Tab này sẽ là $tourkhóa mảng chúng ta muốn tiếp theo. NHỮNG ĐIỀU NÀY PHẢI .

Vì vậy, khi chúng ta gọi hàm get_admin_url()và truyền hai biến; biến đầu tiên xác định trang quản trị tiếp theo .. và biến thứ hai xác định con trỏ nào sẽ hiển thị.

Cuối cùng ... cuối cùng chúng ta cũng có thể in tập lệnh quản trị viên đến chân trang.

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

Một lần nữa, không cần phải thay đổi bất cứ điều gì ở trên. Kịch bản lệnh này sẽ xác định và xuất hai nút trong cửa sổ lớp phủ con trỏ. Một sẽ luôn là nút "Đóng"; và sẽ cập nhật dismissed_pointerstùy chọn meta người dùng hiện tại .

Nút thứ hai (nút hành động) sẽ thực thi chức năng (phương pháp di chuyển cửa sổ của chúng tôi).

Và chúng tôi đóng cửa lớp học.

Đây là toàn bộ mã. Lớp con trỏ WP

Bạn có thể sao chép / dán nó vào trang dev của bạn và truy cập trang "Bảng điều khiển". Nó sẽ hướng dẫn bạn thông qua các tour du lịch.

Hãy nhớ rằng, hơi khó hiểu khi con trỏ đầu tiên được xác định cuối cùng trong mã. Đó là cách nó được cho là để làm việc. Mảng sẽ chứa tất cả phần còn lại của con trỏ bạn muốn sử dụng.

Hãy nhớ rằng, mục mảng 'id' PHẢI khớp với đối số thứ hai của get_admin_url()hàm từ lệnh 'function' của mục mảng trước đó. Đây là cách con trỏ 'nói chuyện' với nhau và biết cách tiến lên.

Thưởng thức!! :)


Thật đáng yêu Josh, cảm ơn bạn rất nhiều! Tôi sẽ thử nó và xem nó hoạt động tốt như thế nào. Tôi nên nhấn mạnh rằng mã của GM là mã mà tôi có thể sẽ trao giải thưởng này vì nó có một vài tính năng thiết yếu mà tôi yêu cầu và tôi tin rằng nó rất quan trọng để tạo một hướng dẫn, đặc biệt là cho nhiều trang trong wp-admin. Tuy nhiên, thật tuyệt khi thấy một cách tiếp cận khác về điều này và sẽ có ích cho những người dùng khác đang tìm kiếm một giải pháp tốt. Vì tò mò, bạn cho biết có khá nhiều cảm xúc lẫn lộn khi sử dụng con trỏ , quan tâm đến công phu?
Christine Cooper

2
Đừng lo lắng :) Vâng, con trỏ có thể 'cản trở' khi sử dụng vượt mức. Không ai muốn truy cập một trang và có ba hoặc bốn con trỏ hiển thị ... đặc biệt nếu chúng không liên quan. Giả sử hai plugin khác đang hiển thị con trỏ, sau đó chúng tôi thêm nhiều con trỏ .. nó có thể trở nên quá mức cần thiết. Hầu hết mọi người nói sử dụng chúng một cách tiết kiệm ... nhưng với mỗi người là của riêng họ :) Rất vui vì bạn đã làm cho nó hoạt động đúng.
Josh

1
Đây cũng là Josh tuyệt vời, tôi sẽ đưa ra 1 gợi ý, để làm cho nó linh hoạt hơn và có nó ở đó bạn có thể chỉ cần chuyển mảng vào một hàm công khai thay vì lưu trữ nó bên trong mã lớp. Thứ hai, con trỏ đầu tiên, cách tách nó ra, sửa đổi nó để nó có thể là khóa / giá trị mảng đầu tiên trong mảng con trỏ. Chỉ cần một vài ý tưởng để lớp này có thể được gọi từ một tập lệnh khác và chỉ cần truyền vào mảng con trỏ. Tôi vẫn thực sự thích nó, cảm ơn vì đã chia sẻ tôi sẽ sử dụng cái này!
JasonDavis

Cảm ơn @jasondavis. Tôi thực sự đã rút mã đó từ một plugin khác mà tôi đã phát triển cho một ai đó. Tôi chỉ quan tâm đến việc làm cho nó hoạt động đúng. Nhưng vâng, tôi hoàn toàn đồng ý với bạn ... nó cần được làm sạch. Có lẽ tôi sẽ dừng lại sau hôm nay và gây rối với nó một lần nữa :) Bạn rock, người anh em!
Josh

Thật tuyệt, tôi thực sự chưa bao giờ có ý định sử dụng Con trỏ quản trị, chủ yếu là vì chúng trông giống như một cơn ác mộng và thứ 2 vì tôi không có công dụng thực sự cho chúng, nhưng lớp của bạn khiến chúng trông rất dễ sử dụng mà tôi cảm thấy phải sử dụng chúng với nó thật dễ dàng với lớp đó! Tôi yêu các dự án / thư viện nhỏ như thế, những thứ tốt
JasonDavis
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.