Skip to content

Instantly share code, notes, and snippets.

@hinaloe
Last active September 21, 2017 02:54
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save hinaloe/329fe6529bbca19f307c0b967210afce to your computer and use it in GitHub Desktop.
Save hinaloe/329fe6529bbca19f307c0b967210afce to your computer and use it in GitHub Desktop.
<?php
namespace NAE\ShowArticleMap\Admin;
use NAE\ShowArticleMap\ShowArticleMap;
use NAE\ShowArticleMap\SingleTon;
class OptionsPage extends SingleTon {
protected $options = [];
/**
* OptionsPage constructor.
*/
protected function __construct() {
add_action( 'admin_menu', [ $this, 'admin_menu' ], 4 );
add_action( 'admin_init', [ $this, 'admin_init' ] );
}
/**
* Add admin page
*/
public function admin_menu() {
add_submenu_page( 'tools.php', esc_html__( 'Article Map' ), esc_html__( 'Article Map' ), 'manage_options', 'article_map', [
$this,
'settings_page',
] );
}
/**
* Settings page callback
*/
public function settings_page() {
$this->options = get_option( 'nae_article_map_options', [] );
?>
<div class="wrap">
<h1><?= esc_html__( 'Article Map' ); ?></h1>
<form method="post" action="options.php">
<?php
// This prints out all hidden setting fields
settings_fields( 'nae_article_map_opts' );
do_settings_sections( 'nae_article_map_opts' );
submit_button();
?>
</form>
<?php
echo ShowArticleMap::activate()->nae_echo_article_map();
?>
</div>
<?php
}
public function admin_init() {
register_setting('nae_article_map_opts', 'nae_article_map_options', [
'sanitize_callback' => [ $this, 'sanitize' ],
] );
add_settings_section( 'nae_article_map_opts', __( 'Option' ), null, 'nae_article_map_opts' );
add_settings_field( 'enable_shortcode', esc_html__( 'Enable Shortcode' ), function () {
echo '<input type="hidden" name="nae_article_map_options[enable_shortcode]" value="0">';
echo '<label><input type="checkbox" name="nae_article_map_options[enable_shortcode]" value="1" ' . checked( isset( $this->options['enable_shortcode'] ) && $this->options['enable_shortcode'], true, false ) . '/> ' . esc_html__( 'Enable Shortcode.' ) . '</label>';
}, 'nae_article_map_opts', 'nae_article_map_opts' );
}
/**
* @param array $input
*
* @return array
*/
public function sanitize( $input ) {
$opt = [
'enable_shortcode' => filter_var( $input['enable_shortcode'], FILTER_VALIDATE_BOOLEAN ),
];
return $opt;
}
}
<?php
/*
Plugin Name: Show Article Map
Plugin URI: https://www.naenote.net/entry/show-article-map
Description: Visualize internal link between posts
Author: NAE
Version: 0.2
Author URI: https://www.naenote.net/entry/show-article-map
License: GPL2
*/
namespace NAE\ShowArticleMap;
use DOMDocument;
use DOMXPath;
use WP_Error;
use WP_Term;
if ( ! defined( 'ABSPATH' ) ) {
die();
}
require_once __DIR__ . '/SingleTon.php';
/*
Copyright 2017 NAE (email : @__NAE__)
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License, version 2, as
published by the Free Software Foundation.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
/**
* Class ShowArticleMap
*/
class ShowArticleMap extends SingleTon {
/**
* ShowArticleMap constructor.
*/
protected function __construct() {
if ( $this->isEnabledShortcode() ) {
add_shortcode( 'article-map', [ $this, 'nae_echo_article_map' ] );
}
if ( is_admin() ) {
require_once __DIR__ . '/OptionsPage.php';
Admin\OptionsPage::activate();
}
}
/**
* @param string $text
* @param string $insert
* @param int $num
*
* @return string
*/
public function nae_insert_str( $text, $insert, $num ) {
$return_text = $text;
$text_len = mb_strlen( $text, 'utf-8' );
$insert_len = mb_strlen( $insert, 'utf-8' );
for ( $i = 0; ( $i + 1 ) * $num < $text_len; $i ++ ) {
$current_num = $num + $i * ( $insert_len + $num );
$return_text = preg_replace( "/^.{0,$current_num}+\K/us", $insert, $return_text );
}
return $return_text;
}
/**
* @return false|string
*/
function nae_get_dataset() {
$args = [
'posts_per_page' => - 1,
'post_type' => [ 'post', 'page' ],
'post_status' => 'publish',
];
/**
* @var \WP_Post[] $posts
*/
$posts = get_posts( $args );
$nodes = [];
$edges = [];
$assets = [
'queue' => [
'style' => wp_styles()->queue,
'script' => wp_scripts()->queue,
],
'args' => [
'style' => wp_styles()->args,
'script' => wp_scripts()->args,
],
];
foreach ( $posts as $post ) {
if ( is_admin() ) {
$GLOBALS['post'] = $post;
}
/** @var WP_Term[] $categories */
$categories = get_the_category( $post->ID );
if ( empty( $categories ) ) {
$group = 'no_cat';
} else {
$cats = get_ancestors( $categories[0]->cat_ID, 'category' ) ?: $categories;
$root_category_ID = array_pop( $cats );
/** @var WP_Term|WP_Error $root_category */
$root_category = get_category( $root_category_ID );
$group = empty( $root_category ) || is_wp_error( $root_category ) ? 'no_cat' : $root_category->slug;
}
$nodes[] = [
'id' => $post->ID,
'label' => $this->nae_insert_str( urldecode( $post->post_name ), "\r\n", 20 ),
'group' => urldecode( $group ),
'title' => '<a href="' . esc_attr( get_permalink( $post ) ) . '" target="_blank">' . esc_html( $post->post_title ) . '</a>',
];
$html = preg_replace( '/\[show_article_map[^\]]*\]/u', '', $post->post_content );
$html = apply_filters( 'the_content', $html );
$dom = new DOMDocument;
@$dom->loadHTML( mb_convert_encoding( $html, 'HTML-ENTITIES', 'UTF-8' ) );
$xpath = new DOMXPath( $dom );
$query = "//a[@href != '' and not(starts-with(@href, '#')) and normalize-space() != '']";
foreach ( $xpath->query( $query ) as $node ) {
$href = $xpath->evaluate( 'string(@href)', $node );
$linked_post_id = url_to_postid( $href );
if ( 0 !== $linked_post_id && ! in_array( [ 'from' => $post->ID, 'to' => $linked_post_id ], $edges, true ) ) {
$edges[] = [
'from' => $post->ID,
'to' => $linked_post_id,
];
}
}
wp_reset_postdata();
}
// Restore assets.
wp_styles()->queue = $assets['queue']['style'];
wp_styles()->args = $assets['args']['style'];
wp_scripts()->queue = $assets['queue']['script'];
wp_scripts()->args = $assets['args']['script'];
return wp_json_encode( [ $nodes, $edges ] );
}
/**
* @return string
*/
public function nae_echo_article_map() {
$dataset = $this->nae_get_dataset();
$body = <<<EOD
<div id="manipulationspace">
<label for="searchnodequery">Search by node name</label>
<input id="searchnodequery" name="searchnodequery" size="30" style="display:inline;width:50% !important;" type="text">
<button id="searchnodebutton" type="submit">Search</button>
<button id="deletepagebutton" type="submit">Remove Pages</button>
</div>
<div id="mynetwork" style="width: 100%; height: 800px; border: 1px solid lightgray;"></div>
<div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/vis/4.20.0/vis.min.js"></script>
<link href="https://cdnjs.cloudflare.com/ajax/libs/vis/4.20.0/vis.min.css" rel="stylesheet">
<script type="text/javascript">
var dataset = $dataset;
var nodedata = dataset[0];
var edgedata = dataset[1];
// create an array with nodes
var nodes = new vis.DataSet(nodedata);
// create an array with edges
var edges = new vis.DataSet(edgedata);
// create a network
var container = document.getElementById('mynetwork');
// provide the data in the vis format
data = { nodes: nodes, edges: edges };
var options = {
nodes:{shape:"box"},
edges:{arrows: {to:{enabled: true, scaleFactor:1, type:'arrow'}}},
manipulation:{enabled:true},
};
// initialize your network!
var network = new vis.Network(container, data, options);
// double click node to open an article
network.on('doubleClick', function(e){
var nodeID = e.nodes.toString();
var url = jQuery(data.nodes.get(nodeID)['title']).attr('href');
//console.log(jQuery(data.nodes.get(nodeID)['title'])); console.log(url);
window.open(url,'_blank');
});
// search node label by query
jQuery('#searchnodebutton').click(function(){
var search = jQuery('#searchnodequery').val();
// serch nodes by node label
var hitNodes = nodes.get({
filter:function(item){
var label = item.label.replace("\\r\\n","");
return label.indexOf(search) !== -1;
}
});
var hitNodeIDs = [];
for (var i=0;i<hitNodes.length;i++) {
hitNodeIDs.push(hitNodes[i].id);
};
// select
network.selectNodes(hitNodeIDs);
});
jQuery('#searchnodequery').keypress(function(e){
if(e.which === 13){//Enter key pressed
jQuery('#searchnodebutton').click();//Trigger search button click event
}
});
jQuery('#deletepagebutton').click(function(){
// serch nodes by group ID
var hitNodes = nodes.get({
filter:function(item){
return item.group.indexOf("no_cat") !== -1;
}
});
nodes.remove(hitNodes);
});
</script>
</div>
EOD;
return $body;
}
protected function isEnabledShortcode() {
$option = get_option( 'nae_article_map_options', [] );
return isset( $option['enable_shortcode'] ) && (bool) $option['enable_shortcode'];
}
}
ShowArticleMap::activate();
<?php
namespace NAE\ShowArticleMap;
abstract class SingleTon {
private static $instances = [];
/**
* @return static
*/
static public function activate() {
if ( ! isset( self::$instances[ static::class ] ) ) {
self::$instances[ static::class ] = new static();
}
return self::$instances[ static::class ];
}
}
@hinaloe
Copy link
Author

hinaloe commented Sep 18, 2017

gistだとディレクトリ構造保てなくね

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment