.pointer( 'open' );모든 포인터 객체에서 javascript 함수를 호출 하므로 모든 포인터가 동시에 표시되는 것은 놀라운 일이 아닙니다 ...
즉, 왜 모든 포인터 (비 활동적인 포인터조차)를 반환 custom_admin_pointers()한 다음 추가 함수를 추가하여 활성 포인터가 있는지 확인하고 포인터 루프 ( if ( $array['active'] ) {) 내부 에 Javascript 포인터를 추가하도록 선택하십시오. 또는 아닙니다. 단순히 활성 포인터 만 반환하는 것이 더 간단하지 않습니까?
또한 모든 관리자 페이지에 해당 자바 스크립트를 추가하고 있습니까? 또한 "# save-post"와 같은 일부 요소는 새 게시물 페이지에서만 사용할 수 있으므로 포인터를 새 냄비 페이지에만 추가하지 않는 것이 좋습니다.
마지막으로, 자바 스크립트가 PHP와 섞여 얼마나 지저분합니까? wp_localize_script데이터를 자바 스크립트로 전달하는 데 사용해야한다고 생각합니다 .
계획:
- PHP의 포인터 정의를 별도의 파일로 옮깁니다. 이러한 방식으로 PHP 코드에서 마크 업을 쉽게 편집하고 제거 할 수 있으므로 모든 것이 더 읽기 쉽고 유지 관리 가능합니다.
- 포인터에 그 팝업을 표시해야하는 관리자 페이지에서 설정하는 데 사용됩니다 ""속성을 추가 구성 :
post-new.php, index.php...
- 포인터 정보의 로딩, 파싱 및 필터링을 처리하는 클래스를 작성하십시오.
- 기본 "제거"버튼을 "다음"으로 변경하는 데 도움이되는 js 장점을 작성하십시오.
# 4 캔은 (아마도) 잘 플러그인 포인터를 알고 쉽게 할,하지만 내 경우이 아니다. 그래서 일반적인 jQuery 코드를 사용하여 결과를 얻습니다. 누군가 내 코드를 향상시킬 수 있다면 감사하겠습니다.
편집하다
내가 고려하지 않은 다른 것들이 있기 때문에 코드 (주로 js)를 편집했습니다. 일부 포인터는 동일한 앵커에 추가되거나 존재하지 않거나 보이지 않는 앵커에 동일한 포인터를 추가 할 수 있습니다. 모든 경우에 이전 코드가 작동하지 않았으므로 새 버전은 문제를 잘 해결하는 것으로 보입니다.
또한 테스트에 사용한 모든 코드 로 요점 을 설정했습니다 .
포인트 # 1 과 # 2로 시작해 봅시다 : 이름이 붙은 파일을 만들고 pointers.php거기에 쓰십시오 :
<?php
$pointers = array();
$pointers['new-items'] = array(
'title' => sprintf( '<h3>%s</h3>', esc_html__( 'Add New Item' ) ),
'content' => sprintf( '<p>%s</p>', esc_html__( 'Easily add a new post..' ) ),
'anchor_id' => '#wp-admin-bar-new-content',
'edge' => 'top',
'align' => 'left',
'where' => array( 'index.php', 'post-new.php' ) // <-- Please note this
);
$pointers['story_cover_help'] = array(
'title' => sprintf( '<h3>%s</h3>', esc_html__( 'Another info' ) ),
'content' => sprintf( '<p>%s</p>', esc_html__( 'Lore ipsum....' ) ),
'anchor_id' => '#save-post',
'edge' => 'top',
'align' => 'right',
'where' => array( 'post-new.php' ) // <-- Please note this
);
// more pointers here...
return $pointers;
모든 포인터 구성이 여기에 있습니다. 변경해야 할 경우이 파일을 열고 편집하십시오.
포인터를 사용할 수있는 페이지 배열 인 "where"특성에 유의하십시오.
플러그인으로 생성 된 페이지에 포인터를 표시하려면 아래에 설명 된이 줄을 찾아 그 바로 아래에 public function filter( $page ) {추가 die($page);하십시오. 그런 다음 해당 플러그인 페이지를 열고 where속성 에서 해당 문자열을 사용하십시오 .
자 이제 포인트 3 입니다.
클래스를 작성하기 전에 인터페이스를 코딩하고 싶습니다. 클래스가 무엇을 더 잘 이해할 수 있도록 주석을 달겠습니다.
<?php
interface PointersManagerInterface {
/**
* Load pointers from file and setup id with prefix and version.
* Cast pointers to objects.
*/
public function parse();
/**
* Remove from parse pointers dismissed ones and pointers
* that should not be shown on given page
*
* @param string $page Current admin page file
*/
public function filter( $page );
}
나는 분명해야한다고 생각합니다. 이제 클래스를 작성해 봅시다. 인터페이스와 생성자의 두 가지 메소드가 포함됩니다.
<?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;
}
}
코드는 매우 간단하며 인터페이스가 기대하는 것을 정확하게 수행합니다.
그러나 클래스 자체는 아무것도하지 않으므로 클래스를 인스턴스화 해야하는 적절한 후크를 전달하는 두 가지 메소드를 시작하는 후크가 필요합니다.
는 'admin_enqueue_scripts'우리의 범위에 적합 : 거기에 우리가 현재 관리자 페이지에 액세스 할 수 있습니다 우리는 또한 대기열 스크립트와 스타일이 필요 할 수 있습니다.
add_action( 'admin_enqueue_scripts', function( $page ) {
$file = plugin_dir_path( __FILE__ ) . 'pointers.php';
// Arguments: pointers php file, version (dots will be replaced), prefix
$manager = new PointersManager( $file, '5.0', 'custom_admin_pointers' );
$manager->parse();
$pointers = $manager->filter( $page );
if ( empty( $pointers ) ) { // nothing to do if no pointers pass the filter
return;
}
wp_enqueue_style( 'wp-pointer' );
$js_url = plugins_url( 'pointers.js', __FILE__ );
wp_enqueue_script( 'custom_admin_pointers', $js_url, array('wp-pointer'), NULL, TRUE );
// data to pass to javascript
$data = array(
'next_label' => __( 'Next' ),
'close_label' => __('Close'),
'pointers' => $pointers
);
wp_localize_script( 'custom_admin_pointers', 'MyAdminPointers', $data );
} );
특별한 것은 없습니다 : 클래스를 사용하여 포인터 데이터를 가져오고 일부 포인터가 필터를 전달하는 경우 스타일과 스크립트를 큐에 넣습니다. 그런 다음 포인터 데이터를 버튼의 현지화 된 "다음"레이블과 함께 스크립트로 전달하십시오.
자, "가장 어려운"부분 인 js입니다. 다시 한 번 말씀 드리지만 WordPress가 사용하는 포인터 플러그인을 모른다는 점을 강조하고 싶습니다. 누군가 알고 있으면 코드에서 수행하는 작업을 더 잘 수행 할 수 있지만 코드가 작동하고 일반적으로 말하면 그렇게 나쁘지 않습니다.
( function($, MAP) {
$(document).on( 'MyAdminPointers.setup_done', function( e, data ) {
e.stopImmediatePropagation();
MAP.setPlugin( data ); // open first popup
} );
$(document).on( 'MyAdminPointers.current_ready', function( e ) {
e.stopImmediatePropagation();
MAP.openPointer(); // open a popup
} );
MAP.js_pointers = {}; // contain js-parsed pointer objects
MAP.first_pointer = false; // contain first pointer anchor jQuery object
MAP.current_pointer = false; // contain current pointer jQuery object
MAP.last_pointer = false; // contain last pointer jQuery object
MAP.visible_pointers = []; // contain ids of pointers whose anchors are visible
MAP.hasNext = function( data ) { // check if a given pointer has valid next property
return typeof data.next === 'string'
&& data.next !== ''
&& typeof MAP.js_pointers[data.next].data !== 'undefined'
&& typeof MAP.js_pointers[data.next].data.id === 'string';
};
MAP.isVisible = function( data ) { // check if anchor for given pointer is visible
return $.inArray( data.id, MAP.visible_pointers ) !== -1;
};
// given a pointer object, return its the anchor jQuery object if available
// otherwise return first available, lookin at next property of subsequent pointers
MAP.getPointerData = function( data ) {
var $target = $( data.anchor_id );
if ( $.inArray(data.id, MAP.visible_pointers) !== -1 ) {
return { target: $target, data: data };
}
$target = false;
while( MAP.hasNext( data ) && ! MAP.isVisible( data ) ) {
data = MAP.js_pointers[data.next].data;
if ( MAP.isVisible( data ) ) {
$target = $(data.anchor_id);
}
}
return MAP.isVisible( data )
? { target: $target, data: data }
: { target: false, data: false };
};
// take pointer data and setup pointer plugin for anchor element
MAP.setPlugin = function( data ) {
if ( typeof MAP.last_pointer === 'object') {
MAP.last_pointer.pointer('destroy');
MAP.last_pointer = false;
}
MAP.current_pointer = false;
var pointer_data = MAP.getPointerData( data );
if ( ! pointer_data.target || ! pointer_data.data ) {
return;
}
$target = pointer_data.target;
data = pointer_data.data;
$pointer = $target.pointer({
content: data.title + data.content,
position: { edge: data.edge, align: data.align },
close: function() {
// open next pointer if it exists
if ( MAP.hasNext( data ) ) {
MAP.setPlugin( MAP.js_pointers[data.next].data );
}
$.post( ajaxurl, { pointer: data.id, action: 'dismiss-wp-pointer' } );
}
});
MAP.current_pointer = { pointer: $pointer, data: data };
$(document).trigger( 'MyAdminPointers.current_ready' );
};
// scroll the page to current pointer then open it
MAP.openPointer = function() {
var $pointer = MAP.current_pointer.pointer;
if ( ! typeof $pointer === 'object' ) {
return;
}
$('html, body').animate({ // scroll page to pointer
scrollTop: $pointer.offset().top - 30
}, 300, function() { // when scroll complete
MAP.last_pointer = $pointer;
var $widget = $pointer.pointer('widget');
MAP.setNext( $widget, MAP.current_pointer.data );
$pointer.pointer( 'open' ); // open
});
};
// if there is a next pointer set button label to "Next", to "Close" otherwise
MAP.setNext = function( $widget, data ) {
if ( typeof $widget === 'object' ) {
var $buttons = $widget.find('.wp-pointer-buttons').eq(0);
var $close = $buttons.find('a.close').eq(0);
$button = $close.clone(true, true).removeClass('close');
$buttons.find('a.close').remove();
$button.addClass('button').addClass('button-primary');
has_next = false;
if ( MAP.hasNext( data ) ) {
has_next_data = MAP.getPointerData(MAP.js_pointers[data.next].data);
has_next = has_next_data.target && has_next_data.data;
}
var label = has_next ? MAP.next_label : MAP.close_label;
$button.html(label).appendTo($buttons);
}
};
$(MAP.pointers).each(function(index, pointer) { // loop pointers data
if( ! $().pointer ) return; // do nothing if pointer plugin isn't available
MAP.js_pointers[pointer.id] = { data: pointer };
var $target = $(pointer.anchor_id);
if ( $target.length && $target.is(':visible') ) { // anchor exists and is visible?
MAP.visible_pointers.push(pointer.id);
if ( ! MAP.first_pointer ) {
MAP.first_pointer = pointer;
}
}
if ( index === ( MAP.pointers.length - 1 ) && MAP.first_pointer ) {
$(document).trigger( 'MyAdminPointers.setup_done', MAP.first_pointer );
}
});
} )(jQuery, MyAdminPointers); // MyAdminPointers is passed by `wp_localize_script`
주석의 도움으로 코드는 매우 분명해야합니다. 적어도 좋기를 바랍니다.
좋아, 끝났어 우리의 PHP는 더 단순하고 체계적이며, 자바 스크립트는 더 읽기 쉽고, 포인터는 더 쉽게 편집 할 수 있으며, 더 중요한 것은 모든 것이 작동합니다.