2016-01-10 20:29:41 +00:00
|
|
|
<?php
|
|
|
|
/*
|
|
|
|
Plugin Name: wp-webmention-again
|
|
|
|
Plugin URI: https://github.com/petermolnar/wp-webmention-again
|
|
|
|
Description:
|
2016-01-13 23:12:52 +00:00
|
|
|
Version: 0.2
|
2016-01-10 20:29:41 +00:00
|
|
|
Author: Peter Molnar <hello@petermolnar.eu>
|
|
|
|
Author URI: http://petermolnar.eu/
|
|
|
|
License: GPLv3
|
|
|
|
Required minimum PHP version: 5.3
|
|
|
|
*/
|
|
|
|
|
2016-01-12 22:36:38 +00:00
|
|
|
if ( ! class_exists( 'WP_Webmention_Again' ) ):
|
2016-01-10 20:29:41 +00:00
|
|
|
|
|
|
|
// global send_webmention function
|
2016-01-12 22:36:38 +00:00
|
|
|
if ( ! function_exists( 'send_webmention' ) ) {
|
2016-01-12 12:04:43 +00:00
|
|
|
function send_webmention( $source, $target ) {
|
2016-01-13 23:12:52 +00:00
|
|
|
return WP_Webmention_Again::queue_add ( 'out', $source, $target );
|
2016-01-12 12:04:43 +00:00
|
|
|
}
|
|
|
|
}
|
2016-01-10 20:29:41 +00:00
|
|
|
|
2016-01-13 23:12:52 +00:00
|
|
|
|
2016-01-12 12:04:43 +00:00
|
|
|
// something else might have loaded this already
|
2016-01-12 22:36:38 +00:00
|
|
|
if ( ! class_exists( 'Mf2\Parser' ) ) {
|
|
|
|
require ( __DIR__ . '/vendor/autoload.php' );
|
2016-01-10 20:29:41 +00:00
|
|
|
}
|
|
|
|
|
2016-01-12 12:04:43 +00:00
|
|
|
// if something has loaded Mf2 already, the autoload won't kick in,
|
|
|
|
// and we need this
|
2016-01-12 22:36:38 +00:00
|
|
|
if ( ! class_exists( 'EmojiRecognizer' ) ) {
|
|
|
|
require ( __DIR__ . '/vendor/dissolve/single-emoji-recognizer/src/emoji.php' );
|
2016-01-11 15:43:53 +00:00
|
|
|
}
|
|
|
|
|
2016-01-12 22:36:38 +00:00
|
|
|
class WP_Webmention_Again {
|
2016-01-10 20:29:41 +00:00
|
|
|
|
2016-01-12 12:04:43 +00:00
|
|
|
// post meta key for queued incoming mentions
|
2016-01-11 15:43:53 +00:00
|
|
|
const meta_received = '_webmention_received';
|
2016-01-12 12:04:43 +00:00
|
|
|
// cron handle for processing incoming
|
2016-01-11 15:43:53 +00:00
|
|
|
const cron_received = 'webmention_received';
|
2016-01-12 12:04:43 +00:00
|
|
|
// post meta key for posts marked as outgoing and in need of processing
|
2016-01-11 15:43:53 +00:00
|
|
|
const meta_send = '_webmention_send';
|
2016-01-12 12:04:43 +00:00
|
|
|
// cron handle for processing outgoing
|
2016-01-11 15:43:53 +00:00
|
|
|
const cron_send = 'webmention_send';
|
2016-01-12 12:04:43 +00:00
|
|
|
// WP cache expiration seconds
|
|
|
|
const expire = 10;
|
2016-01-13 23:12:52 +00:00
|
|
|
//
|
|
|
|
const tablename = 'webmentions';
|
2016-01-12 12:04:43 +00:00
|
|
|
|
|
|
|
/**
|
2016-01-12 22:36:38 +00:00
|
|
|
* regular cron interval for processing incoming
|
|
|
|
*
|
|
|
|
* use 'wp-webmention-again_interval_received' to filter this integer
|
|
|
|
*
|
|
|
|
* @return int cron interval in seconds
|
|
|
|
*
|
|
|
|
*/
|
2016-01-13 23:12:52 +00:00
|
|
|
protected static function known_reacji () {
|
|
|
|
return apply_filters( 'wp-webmention-again_known_reacji', 'reacji' );
|
2016-01-12 22:36:38 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* regular cron interval for processing incoming
|
2016-01-12 12:04:43 +00:00
|
|
|
*
|
|
|
|
* use 'wp-webmention-again_interval_received' to filter this integer
|
|
|
|
*
|
|
|
|
* @return int cron interval in seconds
|
|
|
|
*
|
|
|
|
*/
|
2016-01-13 23:12:52 +00:00
|
|
|
protected static function remote_timeout () {
|
|
|
|
return apply_filters( 'wp-webmention-again_remote_timeout', 100 );
|
2016-01-12 22:36:38 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2016-01-13 23:12:52 +00:00
|
|
|
* regular cron interval for processing incoming
|
2016-01-12 22:36:38 +00:00
|
|
|
*
|
|
|
|
* use 'wp-webmention-again_interval_received' to filter this integer
|
|
|
|
*
|
|
|
|
* @return int cron interval in seconds
|
|
|
|
*
|
|
|
|
*/
|
2016-01-13 23:12:52 +00:00
|
|
|
protected static function interval_received () {
|
|
|
|
return apply_filters( 'wp-webmention-again_interval_received', 90 );
|
2016-01-12 12:04:43 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2016-01-12 22:36:38 +00:00
|
|
|
* regular cron interval for processing outgoing
|
2016-01-12 12:04:43 +00:00
|
|
|
*
|
|
|
|
* use 'wp-webmention-again_interval_send' to filter this integer
|
|
|
|
*
|
|
|
|
* @return int cron interval in seconds
|
|
|
|
*
|
|
|
|
*/
|
|
|
|
protected static function interval_send () {
|
2016-01-13 23:12:52 +00:00
|
|
|
return apply_filters( 'wp-webmention-again_interval_send', 90 );
|
2016-01-12 12:04:43 +00:00
|
|
|
}
|
2016-01-11 15:43:53 +00:00
|
|
|
|
2016-01-12 12:04:43 +00:00
|
|
|
/**
|
|
|
|
* max number of retries ( both for outgoing and incoming )
|
|
|
|
*
|
|
|
|
* use 'wp-webmention-again_retry' to filter this integer
|
|
|
|
*
|
|
|
|
* @return int cron interval in seconds
|
|
|
|
*
|
|
|
|
*/
|
|
|
|
protected static function retry () {
|
2016-01-12 22:36:38 +00:00
|
|
|
return apply_filters( 'wp-webmention-again_retry', 5 );
|
2016-01-12 12:04:43 +00:00
|
|
|
}
|
2016-01-10 20:29:41 +00:00
|
|
|
|
2016-01-12 12:04:43 +00:00
|
|
|
/**
|
|
|
|
* endpoint for receiving webmentions
|
|
|
|
*
|
|
|
|
* use 'wp-webmention-again_endpoint' to filter this string
|
|
|
|
*
|
|
|
|
* @return string name of endpoint
|
|
|
|
*
|
|
|
|
*/
|
|
|
|
protected static function endpoint() {
|
2016-01-12 22:36:38 +00:00
|
|
|
return apply_filters( 'wp-webmention-again_endpoint', 'webmention' );
|
2016-01-12 12:04:43 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* maximum amount of posts per batch to be processed
|
|
|
|
*
|
|
|
|
* use 'wp-webmention-again_per_batch' to filter this int
|
|
|
|
*
|
|
|
|
* @return int posts per batch
|
|
|
|
*
|
|
|
|
*/
|
|
|
|
protected static function per_batch () {
|
2016-01-12 22:36:38 +00:00
|
|
|
return apply_filters( 'wp-webmention-again_per_batch', 10 );
|
2016-01-12 12:04:43 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* mapping for comment types -> mf2 names
|
|
|
|
*
|
|
|
|
* use 'wp-webmention-again_mftypes' to filter this array
|
|
|
|
*
|
|
|
|
* @return array array of comment_type => mf2_name entries
|
|
|
|
*
|
|
|
|
*/
|
|
|
|
protected static function mftypes () {
|
|
|
|
$map = array (
|
|
|
|
// http://indiewebcamp.com/reply
|
|
|
|
'reply' => 'in-reply-to',
|
|
|
|
// http://indiewebcamp.com/repost
|
|
|
|
'repost' => 'repost-of',
|
|
|
|
// http://indiewebcamp.com/like
|
|
|
|
'like' => 'like-of',
|
|
|
|
// http://indiewebcamp.com/favorite
|
|
|
|
'favorite' => 'favorite-of',
|
|
|
|
// http://indiewebcamp.com/bookmark
|
|
|
|
'bookmark' => 'bookmark-of',
|
|
|
|
// http://indiewebcamp.com/rsvp
|
|
|
|
'rsvp' => 'rsvp',
|
|
|
|
// http://indiewebcamp.com/tag
|
|
|
|
'tag' => 'tag-of',
|
|
|
|
);
|
|
|
|
|
2016-01-12 22:36:38 +00:00
|
|
|
return apply_filters( 'wp-webmention-again_mftypes', $map );
|
2016-01-12 12:04:43 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* runs on plugin load
|
|
|
|
*/
|
2016-01-10 20:29:41 +00:00
|
|
|
public function __construct() {
|
|
|
|
|
2016-01-12 22:36:38 +00:00
|
|
|
add_action( 'init', array( &$this, 'init' ) );
|
2016-01-12 19:13:09 +00:00
|
|
|
|
2016-01-12 12:04:43 +00:00
|
|
|
add_action( 'parse_query', array( &$this, 'receive' ) );
|
2016-01-10 20:29:41 +00:00
|
|
|
|
2016-01-12 12:04:43 +00:00
|
|
|
add_action( 'wp_head', array( &$this, 'html_header' ), 99 );
|
|
|
|
add_action( 'send_headers', array( &$this, 'http_header' ) );
|
2016-01-10 20:29:41 +00:00
|
|
|
|
|
|
|
// this is mostly for debugging reasons
|
2016-01-12 22:36:38 +00:00
|
|
|
register_activation_hook( __FILE__ , array( 'WP_Webmention_Again', 'plugin_activate' ) );
|
|
|
|
|
2016-01-10 20:29:41 +00:00
|
|
|
// clear schedules if there's any on deactivation
|
2016-01-12 22:36:38 +00:00
|
|
|
register_deactivation_hook( __FILE__ , array( 'WP_Webmention_Again', 'plugin_deactivate' ) );
|
2016-01-12 12:04:43 +00:00
|
|
|
|
|
|
|
// extend current cron schedules with our entry
|
2016-01-12 22:36:38 +00:00
|
|
|
add_filter( 'cron_schedules', array(&$this, 'add_cron_schedule' ) );
|
2016-01-11 15:43:53 +00:00
|
|
|
|
|
|
|
// register the action for processing received
|
|
|
|
add_action( static::cron_received, array( &$this, 'process_received' ) );
|
2016-01-10 20:29:41 +00:00
|
|
|
|
2016-01-12 12:04:43 +00:00
|
|
|
// register the action for processing received
|
|
|
|
add_action( static::cron_send, array( &$this, 'process_send' ) );
|
|
|
|
|
|
|
|
// additional comment types
|
2016-01-12 22:36:38 +00:00
|
|
|
add_action( 'admin_comment_types_dropdown', array( &$this, 'comment_types_dropdown' ) );
|
2016-01-10 20:29:41 +00:00
|
|
|
|
2016-01-12 12:04:43 +00:00
|
|
|
// register new posts
|
2016-01-12 22:36:38 +00:00
|
|
|
add_action( 'transition_post_status', array( &$this, 'queue_send' ), 12, 5 );
|
2016-01-10 20:29:41 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2016-01-12 12:04:43 +00:00
|
|
|
* runs at WordPress init hook
|
|
|
|
*
|
2016-01-10 20:29:41 +00:00
|
|
|
*/
|
|
|
|
public function init() {
|
|
|
|
|
2016-01-12 12:04:43 +00:00
|
|
|
// add webmention endpoint to query vars
|
|
|
|
add_filter( 'query_vars', array( &$this, 'add_query_var' ) );
|
2016-01-11 15:43:53 +00:00
|
|
|
|
|
|
|
// because this needs one more filter
|
2016-01-12 22:36:38 +00:00
|
|
|
add_filter( 'get_avatar_comment_types', array( &$this, 'add_comment_types' ) );
|
2016-01-11 15:43:53 +00:00
|
|
|
|
|
|
|
// additional avatar filter
|
2016-01-12 22:36:38 +00:00
|
|
|
add_filter( 'get_avatar' , array( &$this, 'get_avatar' ), 1, 5 );
|
2016-01-11 15:43:53 +00:00
|
|
|
|
2016-01-12 22:36:38 +00:00
|
|
|
// get_pung is not restrictive enough
|
|
|
|
add_filter ( 'get_pung', array( &$this, 'get_pung' ) );
|
|
|
|
|
2016-01-13 23:12:52 +00:00
|
|
|
if ( ! wp_get_schedule( static::cron_received ) )
|
2016-01-12 12:04:43 +00:00
|
|
|
wp_schedule_event( time(), static::cron_received, static::cron_received );
|
|
|
|
|
2016-01-13 23:12:52 +00:00
|
|
|
if ( ! wp_get_schedule( static::cron_send ) )
|
2016-01-12 12:04:43 +00:00
|
|
|
wp_schedule_event( time(), static::cron_send, static::cron_send );
|
|
|
|
|
2016-01-10 20:29:41 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2016-01-12 12:04:43 +00:00
|
|
|
* plugin activation hook
|
|
|
|
*
|
|
|
|
* dies if PHP version is too low
|
|
|
|
*
|
2016-01-10 20:29:41 +00:00
|
|
|
*/
|
2016-01-12 22:36:38 +00:00
|
|
|
public static function plugin_activate() {
|
2016-01-10 20:29:41 +00:00
|
|
|
if ( version_compare( phpversion(), 5.3, '<' ) ) {
|
|
|
|
die( 'The minimum PHP version required for this plugin is 5.3' );
|
|
|
|
}
|
2016-01-12 19:13:09 +00:00
|
|
|
|
2016-01-13 23:12:52 +00:00
|
|
|
global $wpdb;
|
|
|
|
$dbname = $wpdb->prefix . static::tablename;
|
|
|
|
|
|
|
|
//Use the character set and collation that's configured for WP tables
|
|
|
|
$charset_collate = '';
|
|
|
|
|
|
|
|
if ( !empty($wpdb->charset) ){
|
|
|
|
$charset = str_replace('-', '', $wpdb->charset);
|
|
|
|
$charset_collate = "DEFAULT CHARACTER SET {$charset}";
|
|
|
|
}
|
|
|
|
|
|
|
|
if ( !empty($wpdb->collate) ){
|
|
|
|
$charset_collate .= " COLLATE {$wpdb->collate}";
|
|
|
|
}
|
|
|
|
|
|
|
|
$db_command = "CREATE TABLE IF NOT EXISTS `{$dbname}` (
|
|
|
|
`id` char(160) CHARACTER SET ascii NOT NULL,
|
|
|
|
`date` datetime NOT NULL,
|
|
|
|
`direction` varchar(12) NOT NULL DEFAULT 'in',
|
|
|
|
`tries` int(4) NOT NULL DEFAULT '0',
|
|
|
|
`source` text NOT NULL,
|
|
|
|
`target` text NOT NULL,
|
|
|
|
`object_type` varchar(255) NOT NULL DEFAULT 'post',
|
|
|
|
`object_id` bigint(20) NOT NULL,
|
|
|
|
PRIMARY KEY (`id`),
|
|
|
|
KEY `time` (`date`),
|
|
|
|
KEY `key` (`direction`)
|
|
|
|
) {$charset_collate};";
|
|
|
|
|
|
|
|
static::debug("Initiating DB {$dbname}");
|
|
|
|
try {
|
|
|
|
$wpdb->query( $db_command );
|
|
|
|
}
|
|
|
|
catch (Exception $e) {
|
|
|
|
static::debug('Something went wrong: ' . $e->getMessage());
|
|
|
|
}
|
|
|
|
|
2016-01-10 20:29:41 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2016-01-12 12:04:43 +00:00
|
|
|
* plugin deactivation hook
|
|
|
|
*
|
|
|
|
* makes sure there are no scheduled cron hooks left
|
|
|
|
*
|
2016-01-10 20:29:41 +00:00
|
|
|
*/
|
2016-01-12 22:36:38 +00:00
|
|
|
public static function plugin_deactivate () {
|
2016-01-12 12:04:43 +00:00
|
|
|
wp_unschedule_event( time(), static::cron_received );
|
|
|
|
wp_clear_scheduled_hook( static::cron_received );
|
|
|
|
|
|
|
|
wp_unschedule_event( time(), static::cron_send );
|
|
|
|
wp_clear_scheduled_hook( static::cron_send );
|
2016-01-12 19:13:09 +00:00
|
|
|
|
2016-01-13 23:12:52 +00:00
|
|
|
global $wpdb;
|
|
|
|
$dbname = $wpdb->prefix . static::tablename;
|
|
|
|
|
|
|
|
$db_command = "DROP TABLE IF EXISTS `{$dbname}`;";
|
|
|
|
|
|
|
|
static::debug("Deleting DB {$dbname}");
|
|
|
|
try {
|
|
|
|
$wpdb->query( $db_command );
|
|
|
|
}
|
|
|
|
catch (Exception $e) {
|
|
|
|
static::debug('Something went wrong: ' . $e->getMessage());
|
|
|
|
}
|
2016-01-10 20:29:41 +00:00
|
|
|
}
|
|
|
|
|
2016-01-11 15:43:53 +00:00
|
|
|
|
|
|
|
/**
|
|
|
|
* add our own schedule
|
|
|
|
*
|
|
|
|
* @param array $schedules - current schedules list
|
|
|
|
*
|
|
|
|
* @return array $schedules - extended schedules list
|
|
|
|
*/
|
|
|
|
public function add_cron_schedule ( $schedules ) {
|
|
|
|
|
2016-01-12 22:36:38 +00:00
|
|
|
$schedules[ static::cron_received ] = array (
|
2016-01-12 12:04:43 +00:00
|
|
|
'interval' => static::interval_received(),
|
|
|
|
'display' => sprintf(__( 'every %d seconds' ), static::interval_received() )
|
2016-01-11 15:43:53 +00:00
|
|
|
);
|
|
|
|
|
2016-01-12 22:36:38 +00:00
|
|
|
$schedules[ static::cron_send ] = array (
|
2016-01-12 12:04:43 +00:00
|
|
|
'interval' => static::interval_send(),
|
|
|
|
'display' => sprintf(__( 'every %d seconds' ), static::interval_send() )
|
2016-01-11 15:43:53 +00:00
|
|
|
);
|
|
|
|
|
|
|
|
return $schedules;
|
|
|
|
}
|
|
|
|
|
2016-01-10 20:29:41 +00:00
|
|
|
/**
|
2016-01-12 12:04:43 +00:00
|
|
|
* extends HTML header with webmention endpoint
|
|
|
|
*
|
|
|
|
* @output string two lines of HTML <link>
|
2016-01-10 20:29:41 +00:00
|
|
|
*
|
|
|
|
*/
|
|
|
|
public function html_header () {
|
2016-01-12 12:04:43 +00:00
|
|
|
$endpoint = site_url( '?'. static::endpoint() .'=endpoint' );
|
2016-01-10 20:29:41 +00:00
|
|
|
|
|
|
|
// backwards compatibility with v0.1
|
|
|
|
echo '<link rel="http://webmention.org/" href="' . $endpoint . '" />' . "\n";
|
|
|
|
echo '<link rel="webmention" href="' . $endpoint . '" />' . "\n";
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2016-01-12 12:04:43 +00:00
|
|
|
* extends HTTP response header with webmention endpoints
|
|
|
|
*
|
|
|
|
* @output two lines of Link: in HTTP header
|
2016-01-11 15:43:53 +00:00
|
|
|
*
|
2016-01-10 20:29:41 +00:00
|
|
|
*/
|
|
|
|
public function http_header() {
|
2016-01-12 12:04:43 +00:00
|
|
|
$endpoint = site_url( '?'. static::endpoint() .'=endpoint' );
|
2016-01-10 20:29:41 +00:00
|
|
|
|
|
|
|
// backwards compatibility with v0.1
|
|
|
|
header( 'Link: <' . $endpoint . '>; rel="http://webmention.org/"', false );
|
|
|
|
header( 'Link: <' . $endpoint . '>; rel="webmention"', false );
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* add webmention to accepted query vars
|
|
|
|
*
|
2016-01-12 12:04:43 +00:00
|
|
|
* @param array $vars current query vars
|
2016-01-10 20:29:41 +00:00
|
|
|
*
|
|
|
|
* @return array extended vars
|
|
|
|
*/
|
2016-01-12 22:36:38 +00:00
|
|
|
public function add_query_var( $vars ) {
|
|
|
|
array_push( $vars, static::endpoint() );
|
2016-01-10 20:29:41 +00:00
|
|
|
return $vars;
|
|
|
|
}
|
|
|
|
|
2016-01-11 15:43:53 +00:00
|
|
|
/**
|
2016-01-12 12:04:43 +00:00
|
|
|
* get plugin options
|
|
|
|
*
|
|
|
|
* use 'wp-webmention-again_comment_types' to filter registered comment types
|
|
|
|
* before being applied
|
|
|
|
*
|
|
|
|
* @return array plugin options
|
2016-01-11 15:43:53 +00:00
|
|
|
*
|
|
|
|
*/
|
|
|
|
public static function get_options () {
|
2016-01-12 22:36:38 +00:00
|
|
|
$options = get_option( __CLASS__ );
|
2016-01-11 15:43:53 +00:00
|
|
|
|
2016-01-12 22:36:38 +00:00
|
|
|
if ( isset( $options['comment_types'] ) && is_array( $options['comment_types'] ) )
|
2016-01-11 15:43:53 +00:00
|
|
|
$options['comment_types'] = array_merge($options['comment_types'], static::mftypes());
|
|
|
|
else
|
|
|
|
$options['comment_types'] = static::mftypes();
|
|
|
|
|
2016-01-12 22:36:38 +00:00
|
|
|
$options['comment_types'] = apply_filters( 'wp-webmention-again_comment_types', $options['comment_types'] );
|
2016-01-12 12:04:43 +00:00
|
|
|
|
2016-01-11 15:43:53 +00:00
|
|
|
return $options;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2016-01-12 12:04:43 +00:00
|
|
|
* extends the current registered comment types in options to have
|
|
|
|
* single char emoji as comment type
|
2016-01-11 15:43:53 +00:00
|
|
|
*
|
2016-01-12 12:04:43 +00:00
|
|
|
* @param char $reacji single emoticon character to add as comment type
|
2016-01-11 15:43:53 +00:00
|
|
|
*
|
2016-01-13 23:12:52 +00:00
|
|
|
*
|
2016-01-12 22:36:38 +00:00
|
|
|
public static function register_reacji ( $reacji ) {
|
2016-01-13 23:12:52 +00:00
|
|
|
$known_reacji = get_option( static::known_reacji() );
|
|
|
|
|
|
|
|
if (!is_array($known_reacji))
|
|
|
|
$known_reacji = array();
|
2016-01-11 15:43:53 +00:00
|
|
|
|
2016-01-13 23:12:52 +00:00
|
|
|
if ( ! in_array( $reacji, $known_reacji ) ) {
|
|
|
|
array_push( $known_reacji, $reacji );
|
|
|
|
update_option( static::known_reacji() , $options );
|
2016-01-11 15:43:53 +00:00
|
|
|
}
|
2016-01-12 22:36:38 +00:00
|
|
|
|
2016-01-11 15:43:53 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Extend the "filter by comment type" of in the comments section
|
|
|
|
* of the admin interface with all of our methods
|
|
|
|
*
|
|
|
|
* @param array $types the different comment types
|
|
|
|
*
|
|
|
|
* @return array the filtered comment types
|
|
|
|
*/
|
2016-01-12 22:36:38 +00:00
|
|
|
public function comment_types_dropdown( $types ) {
|
2016-01-11 15:43:53 +00:00
|
|
|
$options = static::get_options();
|
|
|
|
|
2016-01-12 22:36:38 +00:00
|
|
|
foreach ( $options['comment_types'] as $type => $fancy )
|
|
|
|
if ( ! isset( $types[ $type ] ) )
|
2016-01-11 15:43:53 +00:00
|
|
|
$types[ $type ] = ucfirst( $type );
|
2016-01-12 22:36:38 +00:00
|
|
|
|
2016-01-11 15:43:53 +00:00
|
|
|
return $types;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2016-01-12 12:04:43 +00:00
|
|
|
* extend the abailable comment types in WordPress with the ones recognized
|
|
|
|
* by the plugin, including reacji
|
2016-01-11 15:43:53 +00:00
|
|
|
*
|
2016-01-12 12:04:43 +00:00
|
|
|
* @param array $types current types
|
|
|
|
*
|
|
|
|
* @return array extended types
|
2016-01-11 15:43:53 +00:00
|
|
|
*/
|
|
|
|
public function add_comment_types ( $types ) {
|
|
|
|
$options = static::get_options();
|
|
|
|
|
2016-01-12 22:36:38 +00:00
|
|
|
foreach ( $options['comment_types'] as $type => $fancy )
|
|
|
|
if ( ! in_array( $type, $types ) )
|
2016-01-11 15:43:53 +00:00
|
|
|
array_push( $types, $type );
|
|
|
|
|
|
|
|
return $types;
|
|
|
|
}
|
|
|
|
|
2016-01-13 23:12:52 +00:00
|
|
|
/**
|
|
|
|
* insert a webmention to the queue
|
|
|
|
*
|
|
|
|
* @param string $direction - 'in' or 'out'
|
|
|
|
* @param string $source - source URL
|
|
|
|
* @param string $target - target URL
|
|
|
|
* @param string $object - object type: post, comment, etc.
|
|
|
|
* @param int $object_id - ID of object
|
|
|
|
*
|
|
|
|
* @return false|string - false on failure, inserted ID on success
|
|
|
|
*
|
|
|
|
*/
|
|
|
|
public static function queue_add ( $direction, $source, $target, $object = '', $object_id = 0 ) {
|
|
|
|
global $wpdb;
|
|
|
|
$dbname = $wpdb->prefix . static::tablename;
|
|
|
|
|
|
|
|
$direction = strtolower($direction);
|
|
|
|
$valid_directions = array ( 'in', 'out' );
|
|
|
|
if ( ! in_array ( $direction, $valid_directions ) )
|
|
|
|
return false;
|
|
|
|
|
|
|
|
$id = sha1($source . $target);
|
|
|
|
|
|
|
|
if ( static::queue_exists ( $direction, $source, $target ) )
|
|
|
|
return true;
|
|
|
|
|
|
|
|
$q = $wpdb->prepare( "INSERT INTO `{$dbname}`
|
|
|
|
(`id`,`date`,`direction`, `tries`,`source`, `target`, `object_type`, `object_id`) VALUES
|
|
|
|
( '%s', NOW(), '%s', 0, '%s', '%s', '%s', %d );",
|
|
|
|
$id, $direction, $source, $target, $object, $object_id );
|
|
|
|
|
|
|
|
try {
|
|
|
|
$req = $wpdb->query( $q );
|
|
|
|
}
|
|
|
|
catch (Exception $e) {
|
|
|
|
static::debug('Something went wrong: ' . $e->getMessage());
|
|
|
|
}
|
|
|
|
|
|
|
|
return $id;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* increment tries counter for a queue element
|
|
|
|
*
|
|
|
|
* @param string $id - ID of queue element
|
|
|
|
*
|
|
|
|
* @return bool - query success/failure
|
|
|
|
*
|
|
|
|
*/
|
|
|
|
public static function queue_inc ( $id ) {
|
|
|
|
|
|
|
|
if ( empty( $id ) )
|
|
|
|
return false;
|
|
|
|
|
|
|
|
global $wpdb;
|
|
|
|
$dbname = $wpdb->prefix . static::tablename;
|
|
|
|
|
|
|
|
$q = $wpdb->prepare( "UPDATE `{$dbname}` SET `tries` = `tries` + 1 WHERE `id` = '%s'; ", $id );
|
|
|
|
|
|
|
|
try {
|
|
|
|
$req = $wpdb->query( $q );
|
|
|
|
}
|
|
|
|
catch (Exception $e) {
|
|
|
|
static::debug('Something went wrong: ' . $e->getMessage());
|
|
|
|
}
|
|
|
|
|
|
|
|
return $req;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* delete an entry from the webmentions queue
|
|
|
|
*
|
|
|
|
* @param string $id - ID of webmention queue element
|
|
|
|
*
|
|
|
|
* @return bool - query success/failure
|
|
|
|
*
|
|
|
|
*/
|
|
|
|
public static function queue_del ( $id ) {
|
|
|
|
|
|
|
|
if ( empty( $id ) )
|
|
|
|
return false;
|
|
|
|
|
|
|
|
global $wpdb;
|
|
|
|
$dbname = $wpdb->prefix . static::tablename;
|
|
|
|
|
|
|
|
$q = $wpdb->prepare( "DELETE FROM `{$dbname}` WHERE `id` = '%s' LIMIT 1;", $id );
|
|
|
|
|
|
|
|
try {
|
|
|
|
$req = $wpdb->query( $q );
|
|
|
|
}
|
|
|
|
catch (Exception $e) {
|
|
|
|
static::debug('Something went wrong: ' . $e->getMessage());
|
|
|
|
}
|
|
|
|
|
|
|
|
return $req;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* get a batch of elements according to direction
|
|
|
|
*
|
|
|
|
* @param string $direction - 'in' or 'out'
|
|
|
|
* @param int $limit - max number of items to get
|
|
|
|
*
|
|
|
|
* @return array of queue objects
|
|
|
|
*
|
|
|
|
*/
|
|
|
|
public static function queue_get ( $direction, $limit = 1 ) {
|
|
|
|
|
|
|
|
$direction = strtolower($direction);
|
|
|
|
$valid_directions = array ( 'in', 'out' );
|
|
|
|
if ( ! in_array ( $direction, $valid_directions ) )
|
|
|
|
return false;
|
|
|
|
|
|
|
|
global $wpdb;
|
|
|
|
$dbname = $wpdb->prefix . static::tablename;
|
|
|
|
|
|
|
|
$q = $wpdb->prepare( "SELECT * FROM `{$dbname}` WHERE `direction` = '%s' LIMIT %d;", $direction, $limit );
|
|
|
|
|
|
|
|
try {
|
|
|
|
$req = $wpdb->get_results( $q );
|
|
|
|
}
|
|
|
|
catch (Exception $e) {
|
|
|
|
static::debug('Something went wrong: ' . $e->getMessage());
|
|
|
|
}
|
|
|
|
|
|
|
|
if ( ! empty ( $req ) )
|
|
|
|
return $req;
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* checks existence of a queue element
|
|
|
|
*
|
|
|
|
* @param string $direction - 'in' or 'out'
|
|
|
|
* @param string $source - source URL
|
|
|
|
* @param string $target - target URL
|
|
|
|
*
|
|
|
|
* @return bool true on existing element, false on not found
|
|
|
|
*/
|
|
|
|
public static function queue_exists ( $direction, $source, $target ) {
|
|
|
|
|
|
|
|
global $wpdb;
|
|
|
|
$dbname = $wpdb->prefix . static::tablename;
|
|
|
|
|
|
|
|
$direction = strtolower($direction);
|
|
|
|
$valid_directions = array ( 'in', 'out' );
|
|
|
|
if ( ! in_array ( $direction, $valid_directions ) )
|
|
|
|
return false;
|
|
|
|
|
|
|
|
$id = sha1($source . $target);
|
|
|
|
|
|
|
|
$q = $wpdb->prepare( "SELECT date FROM `{$dbname}` WHERE `direction` = '%s' and `id` = '%s';", $direction, $id );
|
|
|
|
|
|
|
|
try {
|
|
|
|
$req = $wpdb->get_results( $q );
|
|
|
|
}
|
|
|
|
catch (Exception $e) {
|
|
|
|
static::debug('Something went wrong: ' . $e->getMessage());
|
|
|
|
}
|
|
|
|
|
|
|
|
if ( ! empty ( $req ) )
|
|
|
|
return true;
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
2016-01-11 15:43:53 +00:00
|
|
|
|
2016-01-10 20:29:41 +00:00
|
|
|
/**
|
2016-01-12 12:04:43 +00:00
|
|
|
* parse & queue incoming webmention endpoint requests
|
2016-01-10 20:29:41 +00:00
|
|
|
*
|
|
|
|
* @param mixed $wp WordPress Query
|
2016-01-12 12:04:43 +00:00
|
|
|
*
|
2016-01-10 20:29:41 +00:00
|
|
|
*/
|
2016-01-12 22:36:38 +00:00
|
|
|
public function receive ( $wp ) {
|
2016-01-10 20:29:41 +00:00
|
|
|
|
|
|
|
// check if it is a webmention request or not
|
2016-01-12 12:04:43 +00:00
|
|
|
if ( ! array_key_exists( static::endpoint(), $wp->query_vars ) )
|
2016-01-10 20:29:41 +00:00
|
|
|
return false;
|
|
|
|
|
|
|
|
// plain text header
|
|
|
|
header( 'Content-Type: text/plain; charset=' . get_option( 'blog_charset' ) );
|
|
|
|
|
|
|
|
// check if source url is transmitted
|
|
|
|
if ( ! isset( $_POST['source'] ) ) {
|
|
|
|
status_header( 400 );
|
|
|
|
echo '"source" is missing';
|
|
|
|
exit;
|
|
|
|
}
|
|
|
|
|
|
|
|
// check if target url is transmitted
|
|
|
|
if ( ! isset( $_POST['target'] ) ) {
|
|
|
|
status_header( 400 );
|
|
|
|
echo '"target" is missing';
|
|
|
|
exit;
|
|
|
|
}
|
|
|
|
|
2016-01-12 22:36:38 +00:00
|
|
|
$target = filter_var( $_POST['target'], FILTER_SANITIZE_URL );
|
|
|
|
$source = filter_var( $_POST['source'], FILTER_SANITIZE_URL );
|
2016-01-10 20:29:41 +00:00
|
|
|
|
2016-01-12 22:36:38 +00:00
|
|
|
if ( false === filter_var( $target, FILTER_VALIDATE_URL ) ) {
|
2016-01-10 20:29:41 +00:00
|
|
|
status_header( 400 );
|
|
|
|
echo '"target" is an invalid URL';
|
|
|
|
exit;
|
|
|
|
}
|
|
|
|
|
2016-01-12 22:36:38 +00:00
|
|
|
if ( false === filter_var( $source, FILTER_VALIDATE_URL ) ) {
|
2016-01-10 20:29:41 +00:00
|
|
|
status_header( 400 );
|
|
|
|
echo '"source" is an invalid URL';
|
|
|
|
exit;
|
|
|
|
}
|
|
|
|
|
2016-01-12 22:36:38 +00:00
|
|
|
$post_id = static::validate_local( $target );
|
|
|
|
|
|
|
|
if (! $post_id || 0 == $post_id ) {
|
2016-01-10 20:29:41 +00:00
|
|
|
status_header( 404 );
|
|
|
|
echo '"target" not found.';
|
|
|
|
exit;
|
|
|
|
}
|
|
|
|
|
2016-01-12 12:04:43 +00:00
|
|
|
// check if pings are allowed
|
|
|
|
if ( ! pings_open( $post_id ) ) {
|
|
|
|
status_header( 403 );
|
|
|
|
echo 'Pings are disabled for this post';
|
|
|
|
exit;
|
|
|
|
}
|
|
|
|
|
2016-01-10 20:29:41 +00:00
|
|
|
// queue here, the remote check will be async
|
2016-01-13 23:12:52 +00:00
|
|
|
//$r = static::queue_receive( $source, $target, $post_id );
|
|
|
|
$r = static::queue_add( 'in', $source, $target, 'post', $post_id );
|
2016-01-10 20:29:41 +00:00
|
|
|
|
2016-01-12 22:36:38 +00:00
|
|
|
if ( true == $r ) {
|
2016-01-10 20:29:41 +00:00
|
|
|
status_header( 202 );
|
|
|
|
echo 'Webmention accepted in the queue.';
|
|
|
|
}
|
|
|
|
else {
|
2016-01-12 19:13:09 +00:00
|
|
|
status_header( 500 );
|
2016-01-10 20:29:41 +00:00
|
|
|
echo 'Something went wrong; please try again later!';
|
|
|
|
}
|
|
|
|
|
|
|
|
exit;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* worker method for doing received webmentions
|
2016-01-12 12:04:43 +00:00
|
|
|
* triggered by cron
|
2016-01-10 20:29:41 +00:00
|
|
|
*
|
|
|
|
*/
|
2016-01-11 15:43:53 +00:00
|
|
|
public function process_received () {
|
2016-01-10 20:29:41 +00:00
|
|
|
|
2016-01-13 23:12:52 +00:00
|
|
|
$incoming = static::queue_get ( 'in', static::per_batch() );
|
2016-01-10 20:29:41 +00:00
|
|
|
|
2016-01-13 23:12:52 +00:00
|
|
|
if ( empty( $incoming ) )
|
2016-01-10 20:29:41 +00:00
|
|
|
return true;
|
|
|
|
|
2016-01-13 23:12:52 +00:00
|
|
|
foreach ( (array)$incoming as $received ) {
|
2016-01-12 22:36:38 +00:00
|
|
|
|
2016-01-13 23:12:52 +00:00
|
|
|
// this really should not happen, but if it does, get rid of this entry immediately
|
|
|
|
if (! isset( $received->target ) ||
|
|
|
|
empty( $received->target ) ||
|
|
|
|
! isset( $received->source ) ||
|
|
|
|
empty( $received->source )
|
|
|
|
) {
|
2016-01-12 22:36:38 +00:00
|
|
|
static::debug( " target or souce empty, aborting" );
|
2016-01-13 23:12:52 +00:00
|
|
|
static::queue_del ( $received->id );
|
2016-01-10 20:29:41 +00:00
|
|
|
continue;
|
|
|
|
}
|
2016-01-12 12:04:43 +00:00
|
|
|
|
2016-01-13 23:12:52 +00:00
|
|
|
static::debug( "processing webmention: target -> {$received->target}, source -> {$received->source}" );
|
2016-01-12 12:04:43 +00:00
|
|
|
|
2016-01-13 23:12:52 +00:00
|
|
|
if ( empty( $received->object_id ) || 0 == $received->object_id )
|
|
|
|
$post_id = url_to_postid ( $received->target );
|
|
|
|
else
|
|
|
|
$post_id = $received->object_id;
|
|
|
|
$post = get_post ( $post_id );
|
2016-01-10 20:29:41 +00:00
|
|
|
|
2016-01-13 23:12:52 +00:00
|
|
|
if ( ! static::is_post( $post ) ) {
|
|
|
|
static::debug( " no post found for this mention, try again later, who knows?" );
|
|
|
|
//static::queue_del ( $received->id );
|
|
|
|
continue;
|
|
|
|
}
|
2016-01-12 12:04:43 +00:00
|
|
|
|
2016-01-13 23:12:52 +00:00
|
|
|
// too many retries, drop this mention and walk away
|
|
|
|
if ( $received->tries >= static::retry() ) {
|
|
|
|
static::debug( " this mention was tried earlier and failed too many times, drop it" );
|
|
|
|
static::queue_del ( $received->id );
|
|
|
|
continue;
|
2016-01-12 12:04:43 +00:00
|
|
|
}
|
2016-01-10 20:29:41 +00:00
|
|
|
|
2016-01-13 23:12:52 +00:00
|
|
|
// increment retries
|
|
|
|
static::queue_inc ( $received->id );
|
2016-01-12 12:04:43 +00:00
|
|
|
|
2016-01-13 23:12:52 +00:00
|
|
|
// validate target
|
|
|
|
$remote = static::try_receive_remote( $post_id, $received->source, $received->target );
|
2016-01-12 12:04:43 +00:00
|
|
|
|
2016-01-13 23:12:52 +00:00
|
|
|
if ( false === $remote || empty( $remote ) ) {
|
|
|
|
static::debug( " parsing this mention failed, retrying next time" );
|
|
|
|
continue;
|
|
|
|
}
|
2016-01-12 12:04:43 +00:00
|
|
|
|
2016-01-13 23:12:52 +00:00
|
|
|
// we have remote data !
|
|
|
|
$c = static::try_parse_remote ( $post_id, $received->source, $received->target, $remote );
|
|
|
|
$ins = static::insert_comment ( $post_id, $received->source, $received->target, $remote, $c );
|
2016-01-12 12:04:43 +00:00
|
|
|
|
2016-01-13 23:12:52 +00:00
|
|
|
if ( true === $ins ) {
|
|
|
|
static::debug( " duplicate (or something similar): this queue element has to be ignored; deleting queue entry" );
|
|
|
|
static::queue_del ( $received->id );
|
|
|
|
}
|
|
|
|
elseif ( is_numeric( $ins ) ) {
|
|
|
|
static::debug( " all went well, we have a comment id: {$ins}, deleting queue entry" );
|
|
|
|
static::queue_del ( $received->id );
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
static::debug( "This is unexpected. Try again." );
|
2016-01-10 20:29:41 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2016-01-12 12:04:43 +00:00
|
|
|
* extended wp_remote_get with debugging
|
|
|
|
*
|
|
|
|
* @param string $source URL to pull
|
|
|
|
*
|
|
|
|
* @return array wp_remote_get array
|
2016-01-10 20:29:41 +00:00
|
|
|
*/
|
2016-01-12 22:36:38 +00:00
|
|
|
protected static function _wp_remote_get ( &$source ) {
|
2016-01-11 15:43:53 +00:00
|
|
|
$content = false;
|
2016-01-12 22:36:38 +00:00
|
|
|
static::debug( " fetching {$source}" );
|
|
|
|
$url = htmlspecialchars_decode( $source );
|
|
|
|
$q = wp_remote_get( $source );
|
2016-01-10 20:29:41 +00:00
|
|
|
|
2016-01-12 22:36:38 +00:00
|
|
|
if ( is_wp_error( $q ) ) {
|
|
|
|
static::debug( " something went wrong: " . $q->get_error_message() );
|
2016-01-10 20:29:41 +00:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2016-01-12 22:36:38 +00:00
|
|
|
if ( !is_array( $q ) ) {
|
|
|
|
static::debug( " $q is not an array. It should be one." );
|
2016-01-10 20:29:41 +00:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2016-01-12 22:36:38 +00:00
|
|
|
if ( ! isset( $q['headers'] ) || ! is_array( $q['headers'] ) ) {
|
|
|
|
static::debug( " missing response headers." );
|
2016-01-10 20:29:41 +00:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2016-01-12 22:36:38 +00:00
|
|
|
if ( ! isset( $q['body'] ) || empty( $q['body'] ) ) {
|
|
|
|
static::debug( " missing body" );
|
2016-01-10 20:29:41 +00:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2016-01-13 23:12:52 +00:00
|
|
|
static::debug('Headers: ' . json_encode($q['headers']));
|
|
|
|
|
2016-01-12 12:04:43 +00:00
|
|
|
return $q;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* try to get contents of webmention originator
|
|
|
|
*
|
|
|
|
* @param int $post_id ID of post
|
|
|
|
* @param string $source Originator URL
|
|
|
|
* @param string $target Target URL
|
|
|
|
*
|
|
|
|
* @return bool|array false on error; plain array or Mf2 parsed (and
|
|
|
|
* flattened ) array of remote content on success
|
|
|
|
*/
|
|
|
|
protected static function try_receive_remote ( &$post_id, &$source, &$target ) {
|
|
|
|
|
|
|
|
$content = false;
|
2016-01-12 22:36:38 +00:00
|
|
|
$q = static::_wp_remote_get( $source );
|
2016-01-12 12:04:43 +00:00
|
|
|
|
2016-01-12 22:36:38 +00:00
|
|
|
if ( false === $q )
|
2016-01-12 12:04:43 +00:00
|
|
|
return false;
|
|
|
|
|
2016-01-13 23:12:52 +00:00
|
|
|
$targets = array (
|
|
|
|
$target,
|
|
|
|
wp_get_shortlink( $post_id ),
|
|
|
|
get_permalink( $post_id )
|
|
|
|
);
|
|
|
|
|
|
|
|
$found = false;
|
|
|
|
|
|
|
|
foreach ( $targets as $k => $t ) {
|
|
|
|
$t = preg_replace( '/https?:\/\/(?:www.)?/', '', $t );
|
|
|
|
$t = preg_replace( '/#.*/', '', $t );
|
|
|
|
$t = untrailingslashit( $t );
|
|
|
|
//$targets[ $k ] = $t;
|
|
|
|
|
|
|
|
if ( ! stristr( $q['body'], $t ) )
|
|
|
|
$found = true;
|
|
|
|
|
|
|
|
}
|
2016-01-10 20:29:41 +00:00
|
|
|
|
|
|
|
// check if source really links to target
|
|
|
|
// this could be a temporary error, so we'll retry later this one as well
|
2016-01-13 23:12:52 +00:00
|
|
|
if ( false == $found ) {
|
2016-01-12 22:36:38 +00:00
|
|
|
static::debug( " missing link to {$t} in remote body" );
|
2016-01-10 20:29:41 +00:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2016-01-12 22:36:38 +00:00
|
|
|
$ctype = isset( $q['headers']['content-type'] ) ? $q['headers']['content-type'] : 'text/html';
|
2016-01-10 20:29:41 +00:00
|
|
|
|
2016-01-12 22:36:38 +00:00
|
|
|
if ( "text/plain" == $ctype ) {
|
|
|
|
static::debug( " interesting, plain text webmention. I'm not prepared for this yet" );
|
2016-01-10 20:29:41 +00:00
|
|
|
return false;
|
|
|
|
}
|
2016-01-12 22:36:38 +00:00
|
|
|
elseif ( $ctype == "application/json" ) {
|
|
|
|
static::debug( " content is JSON" );
|
|
|
|
$content = json_decode( $q['body'], true );
|
2016-01-10 20:29:41 +00:00
|
|
|
}
|
|
|
|
else {
|
2016-01-12 22:36:38 +00:00
|
|
|
static::debug( " content is (probably) html, trying to parse it with MF2" );
|
2016-01-11 15:43:53 +00:00
|
|
|
try {
|
2016-01-12 22:36:38 +00:00
|
|
|
$content = Mf2\parse( $q['body'], $source );
|
2016-01-11 15:43:53 +00:00
|
|
|
}
|
2016-01-12 22:36:38 +00:00
|
|
|
catch ( Exception $e ) {
|
|
|
|
static::debug( " parsing MF2 failed: " . $e->getMessage() );
|
2016-01-11 15:43:53 +00:00
|
|
|
return false;
|
|
|
|
}
|
2016-01-10 20:29:41 +00:00
|
|
|
|
2016-01-12 22:36:38 +00:00
|
|
|
$content = static::flatten_mf2_array( $content );
|
2016-01-11 16:08:41 +00:00
|
|
|
}
|
2016-01-11 15:43:53 +00:00
|
|
|
|
2016-01-12 12:04:43 +00:00
|
|
|
return $content;
|
2016-01-10 20:29:41 +00:00
|
|
|
}
|
|
|
|
|
2016-01-11 15:43:53 +00:00
|
|
|
/**
|
2016-01-12 12:04:43 +00:00
|
|
|
* try to convert remote content to comment
|
|
|
|
*
|
|
|
|
* @param int $post_id ID of post
|
|
|
|
* @param string $source Originator URL
|
|
|
|
* @param string $target Target URL
|
|
|
|
* @param array $content (Mf2) array of remote content
|
2016-01-11 15:43:53 +00:00
|
|
|
*
|
2016-01-12 12:04:43 +00:00
|
|
|
* @return bool|int false on error; insterted comment ID on success
|
2016-01-11 15:43:53 +00:00
|
|
|
*/
|
2016-01-12 12:04:43 +00:00
|
|
|
protected static function try_parse_remote ( &$post_id, &$source, &$target, &$content ) {
|
2016-01-11 15:43:53 +00:00
|
|
|
|
|
|
|
$item = false;
|
|
|
|
$p_authors = array();
|
2016-01-12 12:04:43 +00:00
|
|
|
|
2016-01-12 22:36:38 +00:00
|
|
|
if ( isset( $content['items']['properties'] ) && isset( $content['items']['type'] ) ) {
|
2016-01-11 15:43:53 +00:00
|
|
|
$item = $content['items'];
|
|
|
|
}
|
2016-01-12 22:36:38 +00:00
|
|
|
elseif ( is_array($content['items'] ) && ! empty( $content['items']['type'] ) ) {
|
|
|
|
foreach ( $content['items'] as $i ) {
|
|
|
|
if ( 'h-entry' == $i['type'] ) {
|
2016-01-13 23:12:52 +00:00
|
|
|
$items[] = $i;
|
2016-01-11 15:43:53 +00:00
|
|
|
}
|
2016-01-12 22:36:38 +00:00
|
|
|
elseif ( 'h-card' == $i['type'] ) {
|
2016-01-11 15:43:53 +00:00
|
|
|
$p_authors[] = $i;
|
|
|
|
}
|
2016-01-13 23:12:52 +00:00
|
|
|
elseif ( 'u-comment' == $i['type'] ) {
|
|
|
|
$comments[] = $i;
|
|
|
|
}
|
2016-01-11 15:43:53 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-01-13 23:12:52 +00:00
|
|
|
if ( ! empty ( $items ) )
|
|
|
|
$item = array_pop( $items );
|
|
|
|
elseif ( empty( $items ) && ! empty( $comments ) )
|
|
|
|
$item = array_pop( $comments );
|
|
|
|
|
2016-01-12 22:36:38 +00:00
|
|
|
if (! $item || empty( $item )) {
|
2016-01-11 15:43:53 +00:00
|
|
|
static::debug(' no parseable h-entry found, saving as standard mention comment');
|
2016-01-11 16:08:41 +00:00
|
|
|
$c = array (
|
|
|
|
'comment_author' => $source,
|
|
|
|
'comment_author_url' => $source,
|
|
|
|
'comment_author_email' => '',
|
|
|
|
'comment_post_ID' => $post_id,
|
|
|
|
'comment_type' => 'webmention',
|
|
|
|
'comment_date' => date("Y-m-d H:i:s"),
|
|
|
|
'comment_date_gmt' => date("Y-m-d H:i:s"),
|
|
|
|
'comment_agent' => __CLASS__,
|
|
|
|
'comment_approved' => 0,
|
2016-01-12 22:36:38 +00:00
|
|
|
'comment_content' => sprintf( __( 'This entry was webmentioned on <a href="%s">%s</a>.' ), $source, $source ),
|
2016-01-11 16:08:41 +00:00
|
|
|
);
|
2016-01-12 12:04:43 +00:00
|
|
|
return $c;
|
2016-01-11 15:43:53 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// process author
|
|
|
|
$author_name = $author_url = $avatar = $a = false;
|
|
|
|
|
2016-01-12 22:36:38 +00:00
|
|
|
if ( isset( $item['properties']['author'] ) ) {
|
2016-01-11 15:43:53 +00:00
|
|
|
$a = $item['properties']['author'];
|
|
|
|
}
|
2016-01-12 22:36:38 +00:00
|
|
|
elseif ( ! empty( $p_authors ) ) {
|
|
|
|
$a = array_pop( $p_authors );
|
2016-01-11 15:43:53 +00:00
|
|
|
}
|
|
|
|
|
2016-01-12 22:36:38 +00:00
|
|
|
if ( $a && isset( $a['properties'] ) ) {
|
2016-01-11 15:43:53 +00:00
|
|
|
$a = $a['properties'];
|
|
|
|
|
2016-01-12 22:36:38 +00:00
|
|
|
if ( isset($a['name']) && ! empty( $a['name'] ) )
|
2016-01-11 15:43:53 +00:00
|
|
|
$author_name = $a['name'];
|
|
|
|
|
|
|
|
$try_photos = array ('photo', 'avatar');
|
|
|
|
$p = false;
|
2016-01-12 22:36:38 +00:00
|
|
|
foreach ( $try_photos as $photo ) {
|
|
|
|
if (isset( $a[ $photo ]) && ! empty( $a[ $photo ] ) ) {
|
|
|
|
$p = $a[ $photo ];
|
|
|
|
if ( !empty( $p ) ) {
|
2016-01-11 15:43:53 +00:00
|
|
|
$avatar = $p;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// process type
|
2016-01-11 16:08:41 +00:00
|
|
|
$type = 'webmention';
|
2016-01-11 15:43:53 +00:00
|
|
|
|
2016-01-12 22:36:38 +00:00
|
|
|
foreach ( static::mftypes() as $k => $mapped ) {
|
|
|
|
if ( is_array( $item['properties'] ) && isset( $item['properties'][ $mapped ]) )
|
2016-01-11 15:43:53 +00:00
|
|
|
$type = $k;
|
|
|
|
}
|
|
|
|
|
|
|
|
//process content
|
|
|
|
$c = '';
|
2016-01-12 22:36:38 +00:00
|
|
|
if ( isset( $item['properties']['content'] ) && isset( $item['properties']['content']['html'] ) )
|
2016-01-11 15:43:53 +00:00
|
|
|
$c = $item['properties']['content']['html'];
|
2016-01-12 22:36:38 +00:00
|
|
|
if ( isset( $item['properties']['content'] ) && isset( $item['properties']['content']['value'] ) )
|
|
|
|
$c = wp_filter_kses( $item['properties']['content']['value'] );
|
2016-01-11 15:43:53 +00:00
|
|
|
|
|
|
|
// REACJI
|
2016-01-12 22:36:38 +00:00
|
|
|
$emoji = EmojiRecognizer::isSingleEmoji( $c );
|
2016-01-11 15:43:53 +00:00
|
|
|
|
2016-01-12 22:36:38 +00:00
|
|
|
if ( $emoji ) {
|
|
|
|
static::debug( "wheeeee, reacji!" );
|
2016-01-13 23:12:52 +00:00
|
|
|
$type = 'reacji';
|
|
|
|
//static::register_reacji( $type );
|
2016-01-11 15:43:53 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// process date
|
2016-01-12 22:36:38 +00:00
|
|
|
if ( isset( $item['properties']['modified'] ) )
|
|
|
|
$date = strtotime( $item['properties']['modified'] );
|
|
|
|
elseif ( isset( $item['properties']['published'] ) )
|
|
|
|
$date = strtotime( $item['properties']['published'] );
|
2016-01-11 15:43:53 +00:00
|
|
|
else
|
|
|
|
$date = time();
|
|
|
|
|
2016-01-12 22:36:38 +00:00
|
|
|
$name = empty( $author_name ) ? $source : $author_name;
|
2016-01-11 15:43:53 +00:00
|
|
|
|
|
|
|
$c = array (
|
|
|
|
'comment_author' => $name,
|
2016-01-11 16:08:41 +00:00
|
|
|
'comment_author_url' => $source,
|
2016-01-11 15:43:53 +00:00
|
|
|
'comment_author_email' => '',
|
|
|
|
'comment_post_ID' => $post_id,
|
|
|
|
'comment_type' => $type,
|
2016-01-12 22:36:38 +00:00
|
|
|
'comment_date' => date( "Y-m-d H:i:s", $date ),
|
|
|
|
'comment_date_gmt' => date( "Y-m-d H:i:s", $date ),
|
2016-01-11 15:43:53 +00:00
|
|
|
'comment_agent' => __CLASS__,
|
|
|
|
'comment_approved' => 0,
|
|
|
|
'comment_content' => $c,
|
2016-01-12 12:04:43 +00:00
|
|
|
'comment_avatar' => $avatar,
|
2016-01-11 15:43:53 +00:00
|
|
|
);
|
|
|
|
|
2016-01-12 12:04:43 +00:00
|
|
|
return $c;
|
2016-01-10 20:29:41 +00:00
|
|
|
}
|
|
|
|
|
2016-01-11 15:43:53 +00:00
|
|
|
/**
|
|
|
|
* Comment inserter
|
|
|
|
*
|
|
|
|
* @param string &$post_id post ID
|
2016-01-12 12:04:43 +00:00
|
|
|
* @param string &$source Originator URL
|
|
|
|
* @param string &$target Target URL
|
2016-01-11 15:43:53 +00:00
|
|
|
* @param mixed &$raw Raw format of the comment, like JSON response from the provider
|
2016-01-12 12:04:43 +00:00
|
|
|
* @param array &$comment array formatted to match a WP Comment requirement
|
2016-01-11 15:43:53 +00:00
|
|
|
*
|
|
|
|
*/
|
2016-01-12 12:04:43 +00:00
|
|
|
protected static function insert_comment ( &$post_id, &$source, &$target, &$raw, &$comment ) {
|
2016-01-11 15:43:53 +00:00
|
|
|
|
|
|
|
$comment_id = false;
|
|
|
|
|
2016-01-12 12:04:43 +00:00
|
|
|
$avatar = false;
|
2016-01-12 22:36:38 +00:00
|
|
|
if( isset( $comment['comment_avatar'] ) ) {
|
2016-01-12 12:04:43 +00:00
|
|
|
$avatar = $comment['comment_avatar'];
|
2016-01-12 22:36:38 +00:00
|
|
|
unset( $comment['comment_avatar'] );
|
2016-01-12 12:04:43 +00:00
|
|
|
}
|
|
|
|
|
2016-01-11 15:43:53 +00:00
|
|
|
// safety first
|
|
|
|
$comment['comment_author_email'] = filter_var ( $comment['comment_author_email'], FILTER_SANITIZE_EMAIL );
|
|
|
|
$comment['comment_author_url'] = filter_var ( $comment['comment_author_url'], FILTER_SANITIZE_URL );
|
|
|
|
$comment['comment_author'] = filter_var ( $comment['comment_author'], FILTER_SANITIZE_STRING);
|
|
|
|
|
|
|
|
//test if we already have this imported
|
|
|
|
$testargs = array(
|
|
|
|
'author_url' => $comment['comment_author_url'],
|
|
|
|
'post_id' => $post_id,
|
|
|
|
);
|
|
|
|
|
|
|
|
// so if the type is comment and you add type = 'comment', WP will not return the comments
|
|
|
|
// such logical!
|
2016-01-12 22:36:38 +00:00
|
|
|
if ( 'comment' != $comment['comment_type'] )
|
2016-01-11 15:43:53 +00:00
|
|
|
$testargs['type'] = $comment['comment_type'];
|
|
|
|
|
|
|
|
// in case it's a fav or a like, the date field is not always present
|
|
|
|
// but there should be only one of those, so the lack of a date field indicates
|
|
|
|
// that we should not look for a date when checking the existence of the
|
|
|
|
// comment
|
2016-01-12 22:36:38 +00:00
|
|
|
if ( isset( $comment['comment_date'] ) && ! empty( $comment['comment_date'] ) ) {
|
2016-01-11 15:43:53 +00:00
|
|
|
// in case you're aware of a nicer way of doing this, please tell me
|
|
|
|
// or commit a change...
|
|
|
|
|
|
|
|
$tmp = explode ( " ", $comment['comment_date'] );
|
2016-01-12 22:36:38 +00:00
|
|
|
$d = explode( "-", $tmp[0] );
|
|
|
|
$t = explode ( ':', $tmp[1] );
|
2016-01-11 15:43:53 +00:00
|
|
|
|
|
|
|
$testargs['date_query'] = array(
|
|
|
|
'year' => $d[0],
|
|
|
|
'monthnum' => $d[1],
|
|
|
|
'day' => $d[2],
|
|
|
|
'hour' => $t[0],
|
|
|
|
'minute' => $t[1],
|
|
|
|
'second' => $t[2],
|
|
|
|
);
|
|
|
|
|
|
|
|
//test if we already have this imported
|
2016-01-12 22:36:38 +00:00
|
|
|
static::debug( "checking comment existence (with date) for post #{$post_id}" );
|
2016-01-11 15:43:53 +00:00
|
|
|
}
|
|
|
|
else {
|
|
|
|
// we do need a date
|
2016-01-12 22:36:38 +00:00
|
|
|
$comment['comment_date'] = date( "Y-m-d H:i:s" );
|
|
|
|
$comment['comment_date_gmt'] = date( "Y-m-d H:i:s" );
|
2016-01-11 15:43:53 +00:00
|
|
|
|
2016-01-12 22:36:38 +00:00
|
|
|
static::debug( "checking comment existence (no date) for post #{$post_id}" );
|
2016-01-11 15:43:53 +00:00
|
|
|
}
|
|
|
|
|
2016-01-12 22:36:38 +00:00
|
|
|
$existing = get_comments( $testargs );
|
2016-01-11 15:43:53 +00:00
|
|
|
|
|
|
|
// no matching comment yet, insert it
|
2016-01-12 22:36:38 +00:00
|
|
|
if ( ! empty( $existing ) ) {
|
|
|
|
static::debug ( "comment already exists" );
|
2016-01-11 15:43:53 +00:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
// disable flood control, just in case
|
2016-01-12 22:36:38 +00:00
|
|
|
remove_filter( 'check_comment_flood', 'check_comment_flood_db', 10, 3 );
|
2016-01-11 15:43:53 +00:00
|
|
|
$comment = apply_filters( 'preprocess_comment', $comment );
|
|
|
|
|
2016-01-12 22:36:38 +00:00
|
|
|
if ( $comment_id = wp_new_comment( $comment ) ) {
|
2016-01-11 15:43:53 +00:00
|
|
|
|
|
|
|
// add avatar for later use if present
|
2016-01-12 22:36:38 +00:00
|
|
|
if ( ! empty( $avatar ) )
|
2016-01-11 15:43:53 +00:00
|
|
|
update_comment_meta( $comment_id, 'avatar', $avatar );
|
|
|
|
|
|
|
|
// full raw response for the vote, just in case
|
|
|
|
update_comment_meta( $comment_id, 'webmention_source_mf2', $raw );
|
|
|
|
|
2016-01-12 19:13:09 +00:00
|
|
|
// original request
|
|
|
|
update_comment_meta( $comment_id, 'webmention_original', array( 'target' => $target, 'source' => $source) );
|
|
|
|
|
2016-01-11 15:43:53 +00:00
|
|
|
// info
|
|
|
|
$r = "new comment inserted for {$post_id} as #{$comment_id}";
|
|
|
|
|
|
|
|
// notify author
|
2016-01-12 19:13:09 +00:00
|
|
|
// wp_notify_postauthor( $comment_id );
|
2016-01-11 15:43:53 +00:00
|
|
|
}
|
|
|
|
else {
|
|
|
|
$r = "something went wrong when trying to insert comment for post #{$post_id}";
|
|
|
|
}
|
|
|
|
|
|
|
|
// re-add flood control
|
2016-01-12 22:36:38 +00:00
|
|
|
add_filter( 'check_comment_flood', 'check_comment_flood_db', 10, 3 );
|
|
|
|
|
|
|
|
static::debug( $r );
|
2016-01-11 15:43:53 +00:00
|
|
|
|
|
|
|
return $comment_id;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2016-01-10 20:29:41 +00:00
|
|
|
/**
|
2016-01-12 12:04:43 +00:00
|
|
|
* triggered on post transition, applied when new status is publish, therefore
|
|
|
|
* applied on edit of published posts as well
|
|
|
|
* add a post meta to the post to be processed by the send processor
|
2016-01-10 20:29:41 +00:00
|
|
|
*
|
2016-01-12 12:04:43 +00:00
|
|
|
* @param string $new_status New post status
|
|
|
|
* @param string $old_status Previous post status
|
|
|
|
* @param object $post WP Post object
|
2016-01-13 23:12:52 +00:00
|
|
|
*
|
2016-01-12 22:36:38 +00:00
|
|
|
public function queue_send( $new_status, $old_status, $post ) {
|
|
|
|
if ( ! static::is_post( $post ) ) {
|
|
|
|
static::debug( "Whoops, this is not a post." );
|
2016-01-10 20:29:41 +00:00
|
|
|
return false;
|
2016-01-12 12:04:43 +00:00
|
|
|
}
|
2016-01-10 20:29:41 +00:00
|
|
|
|
2016-01-12 22:36:38 +00:00
|
|
|
if ( 'publish' != $new_status ) {
|
|
|
|
static::debug( "Not adding {$post->ID} to mention queue yet; not published" );
|
2016-01-12 12:04:43 +00:00
|
|
|
return false;
|
|
|
|
}
|
2016-01-10 20:29:41 +00:00
|
|
|
|
2016-01-12 22:36:38 +00:00
|
|
|
$r = add_post_meta( $post->ID, static::meta_send, 1, true );
|
2016-01-10 20:29:41 +00:00
|
|
|
|
2016-01-12 22:36:38 +00:00
|
|
|
if ( ! $r ) {
|
|
|
|
static::debug( "Tried adding post #{$post->ID} to mention queue, but it didn't go well" );
|
|
|
|
}
|
2016-01-13 23:12:52 +00:00
|
|
|
//else {
|
|
|
|
//// fire up a single cron event if the scheduled is too far in the future
|
|
|
|
//$next = wp_next_scheduled( static::cron_send ) - time ();
|
|
|
|
//if ( $next > static::interval_send_min() )
|
|
|
|
//wp_schedule_single_event( time() , static::cron_send );
|
|
|
|
//}
|
2016-01-10 20:29:41 +00:00
|
|
|
|
|
|
|
return $r;
|
|
|
|
}
|
2016-01-13 23:12:52 +00:00
|
|
|
*/
|
2016-01-10 20:29:41 +00:00
|
|
|
|
|
|
|
/**
|
2016-01-13 23:12:52 +00:00
|
|
|
* triggered on post transition, applied when new status is publish, therefore
|
|
|
|
* applied on edit of published posts as well
|
|
|
|
* add a post meta to the post to be processed by the send processor
|
2016-01-10 20:29:41 +00:00
|
|
|
*
|
2016-01-13 23:12:52 +00:00
|
|
|
* @param string $new_status New post status
|
|
|
|
* @param string $old_status Previous post status
|
|
|
|
* @param object $post WP Post object
|
2016-01-10 20:29:41 +00:00
|
|
|
*/
|
2016-01-13 23:12:52 +00:00
|
|
|
public function queue_send( $new_status, $old_status, $post ) {
|
|
|
|
|
|
|
|
if ( ! static::is_post( $post ) ) {
|
|
|
|
static::debug( "Whoops, this is not a post." );
|
|
|
|
return false;
|
|
|
|
}
|
2016-01-12 12:04:43 +00:00
|
|
|
|
2016-01-13 23:12:52 +00:00
|
|
|
if ( 'publish' != $new_status ) {
|
|
|
|
static::debug( "Not adding {$post->ID} to mention queue yet; not published" );
|
2016-01-12 12:04:43 +00:00
|
|
|
return false;
|
2016-01-13 23:12:52 +00:00
|
|
|
}
|
2016-01-12 12:04:43 +00:00
|
|
|
|
2016-01-13 23:12:52 +00:00
|
|
|
static::debug("Trying to get urls for #{$post->ID}");
|
2016-01-12 12:04:43 +00:00
|
|
|
|
2016-01-13 23:12:52 +00:00
|
|
|
// try to avoid redirects, so no shortlink is sent for now as source
|
|
|
|
$source = get_permalink( $post->ID );
|
2016-01-12 12:04:43 +00:00
|
|
|
|
2016-01-13 23:12:52 +00:00
|
|
|
// process the content as if it was the_content()
|
|
|
|
$content = static::get_the_content( $post );
|
2016-01-12 12:04:43 +00:00
|
|
|
|
2016-01-13 23:12:52 +00:00
|
|
|
// get all urls in content
|
|
|
|
$urls = static::extract_urls( $content );
|
2016-01-12 12:04:43 +00:00
|
|
|
|
2016-01-13 23:12:52 +00:00
|
|
|
// for special ocasions when someone wants to add to this list
|
|
|
|
$urls = apply_filters( 'webmention_links', $urls, $post->ID );
|
2016-01-12 22:36:38 +00:00
|
|
|
|
2016-01-13 23:12:52 +00:00
|
|
|
// lowercase url is good for your mental health
|
|
|
|
foreach ( $urls as $k => $url )
|
|
|
|
$urls[ $k ] = strtolower( $url );
|
2016-01-12 22:36:38 +00:00
|
|
|
|
2016-01-13 23:12:52 +00:00
|
|
|
// remove all already pinged urls
|
|
|
|
$pung = get_pung( $post->ID );
|
|
|
|
$urls = array_diff ( $urls, $pung );
|
2016-01-12 12:04:43 +00:00
|
|
|
|
2016-01-13 23:12:52 +00:00
|
|
|
foreach ( $urls as $target ) {
|
|
|
|
$r = static::queue_add ( 'out', $source, $target, $post->post_type, $post->ID );
|
2016-01-12 12:04:43 +00:00
|
|
|
|
2016-01-13 23:12:52 +00:00
|
|
|
if ( !$r )
|
|
|
|
static::debug( " tried adding post #{$post->ID}, url: {$target} to mention queue, but it didn't go well" );
|
|
|
|
}
|
2016-01-12 12:04:43 +00:00
|
|
|
|
2016-01-13 23:12:52 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* worker method for doing received webmentions
|
|
|
|
* triggered by cron
|
|
|
|
*
|
|
|
|
*/
|
|
|
|
public function process_send () {
|
2016-01-12 12:04:43 +00:00
|
|
|
|
2016-01-13 23:12:52 +00:00
|
|
|
$outgoing = static::queue_get ( 'out', static::per_batch() );
|
2016-01-12 22:36:38 +00:00
|
|
|
|
2016-01-13 23:12:52 +00:00
|
|
|
if ( empty( $outgoing ) )
|
|
|
|
return true;
|
2016-01-12 12:04:43 +00:00
|
|
|
|
2016-01-13 23:12:52 +00:00
|
|
|
foreach ( (array)$outgoing as $send ) {
|
2016-01-12 12:04:43 +00:00
|
|
|
|
2016-01-13 23:12:52 +00:00
|
|
|
// this really should not happen, but if it does, get rid of this entry immediately
|
|
|
|
if (! isset( $send->target ) ||
|
|
|
|
empty( $send->target ) ||
|
|
|
|
! isset( $send->source ) ||
|
|
|
|
empty( $send->source )
|
|
|
|
) {
|
|
|
|
static::debug( " target or souce empty, aborting" );
|
|
|
|
static::queue_del ( $send->id );
|
2016-01-12 12:04:43 +00:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2016-01-13 23:12:52 +00:00
|
|
|
static::debug( "processing webmention: target -> {$send->target}, source -> {$send->source}" );
|
2016-01-12 12:04:43 +00:00
|
|
|
|
2016-01-13 23:12:52 +00:00
|
|
|
// too many retries, drop this mention and walk away
|
|
|
|
if ( $send->tries >= static::retry() ) {
|
|
|
|
static::debug( " this mention was tried earlier and failed too many times, drop it" );
|
|
|
|
static::queue_del ( $send->id );
|
|
|
|
continue;
|
|
|
|
}
|
2016-01-12 19:13:09 +00:00
|
|
|
|
2016-01-13 23:12:52 +00:00
|
|
|
// increment retries
|
|
|
|
static::queue_inc ( $send->id );
|
2016-01-12 19:13:09 +00:00
|
|
|
|
2016-01-13 23:12:52 +00:00
|
|
|
// try sending
|
|
|
|
$s = static::send( $send->source, $send->target );
|
2016-01-12 19:13:09 +00:00
|
|
|
|
2016-01-13 23:12:52 +00:00
|
|
|
if ( !$s ) {
|
|
|
|
static::debug( " sending failed; retrying later ({$tries} time)" );
|
2016-01-12 12:04:43 +00:00
|
|
|
}
|
2016-01-13 23:12:52 +00:00
|
|
|
else {
|
|
|
|
static::debug( " sending succeeded!" );
|
2016-01-12 12:04:43 +00:00
|
|
|
|
2016-01-13 23:12:52 +00:00
|
|
|
$post_types = get_post_types( '', 'names' );
|
|
|
|
if ( in_array( $send->object_type, $post_types ) && 0 != $send->object_id )
|
|
|
|
add_ping( $send->object_id, $send->target );
|
|
|
|
|
|
|
|
static::queue_del ( $send->id );
|
|
|
|
}
|
2016-01-12 12:04:43 +00:00
|
|
|
}
|
2016-01-10 20:29:41 +00:00
|
|
|
}
|
|
|
|
|
2016-01-13 23:12:52 +00:00
|
|
|
|
2016-01-12 19:13:09 +00:00
|
|
|
/**
|
2016-01-12 22:36:38 +00:00
|
|
|
* make pung stricter
|
|
|
|
*
|
|
|
|
* @param array $pung array of pinged urls
|
|
|
|
*
|
|
|
|
* @return array a better array of pinged urls
|
2016-01-12 19:13:09 +00:00
|
|
|
*
|
|
|
|
*/
|
2016-01-12 22:36:38 +00:00
|
|
|
public function get_pung ( $pung ) {
|
|
|
|
|
|
|
|
foreach ($pung as $k => $e )
|
|
|
|
$pung[ $k ] = strtolower( $e );
|
|
|
|
|
2016-01-12 19:13:09 +00:00
|
|
|
$pung = array_unique($pung);
|
2016-01-12 22:36:38 +00:00
|
|
|
|
2016-01-12 19:13:09 +00:00
|
|
|
return $pung;
|
|
|
|
}
|
|
|
|
|
2016-01-10 20:29:41 +00:00
|
|
|
/**
|
2016-01-12 12:04:43 +00:00
|
|
|
* get all posts that have meta entry for sending
|
2016-01-10 20:29:41 +00:00
|
|
|
*
|
2016-01-12 12:04:43 +00:00
|
|
|
* @return array of WP Post objects
|
2016-01-10 20:29:41 +00:00
|
|
|
*/
|
2016-01-12 12:04:43 +00:00
|
|
|
protected static function get_send () {
|
|
|
|
global $wpdb;
|
2016-01-12 22:36:38 +00:00
|
|
|
|
2016-01-12 12:04:43 +00:00
|
|
|
$r = array();
|
2016-01-10 20:29:41 +00:00
|
|
|
|
2016-01-12 12:04:43 +00:00
|
|
|
$dbname = "{$wpdb->prefix}postmeta";
|
|
|
|
$key = static::meta_send;
|
|
|
|
$limit = static::per_batch();
|
|
|
|
$db_command = "SELECT DISTINCT `post_id` FROM `{$dbname}` WHERE `meta_key` = '{$key}' LIMIT {$limit}";
|
2016-01-10 20:29:41 +00:00
|
|
|
|
2016-01-12 12:04:43 +00:00
|
|
|
try {
|
2016-01-12 22:36:38 +00:00
|
|
|
$q = $wpdb->get_results( $db_command );
|
2016-01-12 12:04:43 +00:00
|
|
|
}
|
2016-01-12 22:36:38 +00:00
|
|
|
catch ( Exception $e ) {
|
|
|
|
static::debug( "Something went wrong: " . $e->getMessage() );
|
2016-01-10 20:29:41 +00:00
|
|
|
}
|
|
|
|
|
2016-01-12 22:36:38 +00:00
|
|
|
if ( ! empty( $q ) && is_array( $q ) ) {
|
|
|
|
foreach ( $q as $post ) {
|
|
|
|
array_push( $r, $post->post_id );
|
2016-01-12 12:04:43 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return $r;
|
2016-01-10 20:29:41 +00:00
|
|
|
}
|
|
|
|
|
2016-01-11 15:43:53 +00:00
|
|
|
/**
|
2016-01-12 12:04:43 +00:00
|
|
|
* send a single webmention
|
|
|
|
* based on [webmention](https://github.com/pfefferle/wordpress-webmention)
|
2016-01-11 15:43:53 +00:00
|
|
|
*
|
|
|
|
*/
|
2016-01-12 12:04:43 +00:00
|
|
|
public static function send ( $source, $target, $post_id = false ) {
|
|
|
|
$options = static::get_options();
|
|
|
|
|
|
|
|
// stop selfpings on the same URL
|
2016-01-12 22:36:38 +00:00
|
|
|
if ( isset( $options['disable_selfpings_same_url'] ) &&
|
2016-01-12 12:04:43 +00:00
|
|
|
$options['disable_selfpings_same_url'] == '1' &&
|
|
|
|
$source === $target
|
|
|
|
)
|
|
|
|
return false;
|
|
|
|
|
|
|
|
// stop selfpings on the same domain
|
2016-01-12 22:36:38 +00:00
|
|
|
if ( isset( $options['disable_selfpings_same_domain'] ) &&
|
2016-01-12 12:04:43 +00:00
|
|
|
$options['disable_selfpings_same_domain'] == '1' &&
|
|
|
|
parse_url( $source, PHP_URL_HOST ) == parse_url( $target, PHP_URL_HOST )
|
|
|
|
)
|
|
|
|
return false;
|
|
|
|
|
|
|
|
// discover the webmention endpoint
|
|
|
|
$webmention_server_url = static::discover_endpoint( $target );
|
|
|
|
|
|
|
|
// if I can't find an endpoint, perhaps you can!
|
|
|
|
$webmention_server_url = apply_filters( 'webmention_server_url', $webmention_server_url, $target );
|
|
|
|
|
|
|
|
if ( $webmention_server_url ) {
|
|
|
|
$args = array(
|
|
|
|
'body' => 'source=' . urlencode( $source ) . '&target=' . urlencode( $target ),
|
2016-01-12 22:36:38 +00:00
|
|
|
'timeout' => static::remote_timeout(),
|
2016-01-12 12:04:43 +00:00
|
|
|
);
|
|
|
|
|
2016-01-12 22:36:38 +00:00
|
|
|
static::debug( "Sending webmention to: " .$webmention_server_url . " as: " . $args['body'] );
|
2016-01-12 12:04:43 +00:00
|
|
|
$response = wp_remote_post( $webmention_server_url, $args );
|
|
|
|
|
|
|
|
// use the response to do something usefull
|
|
|
|
// do_action( 'webmention_post_send', $response, $source, $target, $post_ID );
|
|
|
|
|
|
|
|
return $response;
|
|
|
|
}
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Finds a WebMention server URI based on the given URL
|
|
|
|
*
|
|
|
|
* code from [webmention](https://github.com/pfefferle/wordpress-webmention)
|
|
|
|
*
|
|
|
|
* Checks the HTML for the rel="http://webmention.org/" link and http://webmention.org/ headers. It does
|
|
|
|
* a check for the http://webmention.org/ headers first and returns that, if available. The
|
|
|
|
* check for the rel="http://webmention.org/" has more overhead than just the header.
|
|
|
|
*
|
|
|
|
* @param string $url URL to ping
|
|
|
|
*
|
|
|
|
* @return bool|string False on failure, string containing URI on success
|
|
|
|
*/
|
|
|
|
protected static function discover_endpoint( $url ) {
|
|
|
|
|
2016-01-12 22:36:38 +00:00
|
|
|
// Not an URL. This should never happen.
|
|
|
|
if ( false === filter_var( $url, FILTER_VALIDATE_URL ) )
|
2016-01-12 12:04:43 +00:00
|
|
|
return false;
|
|
|
|
|
|
|
|
// do not search for a WebMention server on our own uploads
|
|
|
|
$uploads_dir = wp_upload_dir();
|
2016-01-12 22:36:38 +00:00
|
|
|
if ( 0 === strpos( $url, $uploads_dir['baseurl'] ) )
|
2016-01-12 12:04:43 +00:00
|
|
|
return false;
|
|
|
|
|
2016-01-12 22:36:38 +00:00
|
|
|
$response = wp_remote_head( $url, array( 'timeout' => static::remote_timeout(), 'httpversion' => '1.0' ) );
|
2016-01-12 12:04:43 +00:00
|
|
|
|
|
|
|
if ( is_wp_error( $response ) ) {
|
2016-01-12 22:36:38 +00:00
|
|
|
static::debug( "Something went wrong: " . $response->get_error_message() );
|
2016-01-12 12:04:43 +00:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
// check link header
|
|
|
|
if ( $links = wp_remote_retrieve_header( $response, 'link' ) ) {
|
|
|
|
if ( is_array( $links ) ) {
|
|
|
|
foreach ( $links as $link ) {
|
|
|
|
if ( preg_match( '/<(.[^>]+)>;\s+rel\s?=\s?[\"\']?(http:\/\/)?webmention(.org)?\/?[\"\']?/i', $link, $result ) ) {
|
|
|
|
return self::make_url_absolute( $url, $result[1] );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
if ( preg_match( '/<(.[^>]+)>;\s+rel\s?=\s?[\"\']?(http:\/\/)?webmention(.org)?\/?[\"\']?/i', $links, $result ) ) {
|
|
|
|
return self::make_url_absolute( $url, $result[1] );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// not an (x)html, sgml, or xml page, no use going further
|
|
|
|
if ( preg_match( '#(image|audio|video|model)/#is', wp_remote_retrieve_header( $response, 'content-type' ) ) ) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
// now do a GET since we're going to look in the html headers (and we're sure its not a binary file)
|
2016-01-12 22:36:38 +00:00
|
|
|
$response = wp_remote_get( $url, array( 'timeout' => static::remote_timeout(), 'httpversion' => '1.0' ) );
|
2016-01-12 12:04:43 +00:00
|
|
|
|
|
|
|
if ( is_wp_error( $response ) ) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
$contents = wp_remote_retrieve_body( $response );
|
|
|
|
|
|
|
|
// boost performance and use alreade the header
|
|
|
|
$header = substr( $contents, 0, stripos( $contents, '</head>' ) );
|
|
|
|
|
|
|
|
// unicode to HTML entities
|
|
|
|
$contents = mb_convert_encoding( $contents, 'HTML-ENTITIES', mb_detect_encoding( $contents ) );
|
|
|
|
|
|
|
|
libxml_use_internal_errors( true );
|
2016-01-11 15:43:53 +00:00
|
|
|
|
2016-01-12 12:04:43 +00:00
|
|
|
$doc = new DOMDocument();
|
|
|
|
$doc->loadHTML( $contents );
|
|
|
|
|
|
|
|
$xpath = new DOMXPath( $doc );
|
|
|
|
|
|
|
|
// check <link> elements
|
|
|
|
// checks only head-links
|
|
|
|
foreach ( $xpath->query( '//head/link[contains(concat(" ", @rel, " "), " webmention ") or contains(@rel, "webmention.org")]/@href' ) as $result ) {
|
|
|
|
return self::make_url_absolute( $url, $result->value );
|
|
|
|
}
|
|
|
|
|
|
|
|
// check <a> elements
|
|
|
|
// checks only body>a-links
|
|
|
|
foreach ( $xpath->query( '//body//a[contains(concat(" ", @rel, " "), " webmention ") or contains(@rel, "webmention.org")]/@href' ) as $result ) {
|
|
|
|
return self::make_url_absolute( $url, $result->value );
|
|
|
|
}
|
|
|
|
|
|
|
|
return false;
|
2016-01-11 15:43:53 +00:00
|
|
|
}
|
|
|
|
|
2016-01-12 12:04:43 +00:00
|
|
|
|
2016-01-11 15:43:53 +00:00
|
|
|
/**
|
2016-01-12 12:04:43 +00:00
|
|
|
* validated a URL that is supposed to be on our site
|
|
|
|
*
|
|
|
|
* @param string $url URL to check against
|
2016-01-11 15:43:53 +00:00
|
|
|
*
|
2016-01-12 12:04:43 +00:00
|
|
|
* @return bool|int false on not found, int post_id on found
|
2016-01-11 15:43:53 +00:00
|
|
|
*/
|
2016-01-12 12:04:43 +00:00
|
|
|
protected static function validate_local ( $url ) {
|
|
|
|
// normalize url scheme, url_to_postid will take care of it anyway
|
|
|
|
$url = preg_replace( '/^https?:\/\//i', 'http://', $url );
|
|
|
|
return url_to_postid( $url );
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* recursive walker for an mf2 array: if it find an element which has a value
|
|
|
|
* of a single element array, flatten the array to the element
|
|
|
|
*
|
|
|
|
* @param array $mf2 Mf2 array to flatten
|
|
|
|
*
|
|
|
|
* @return array flattened Mf2 array
|
|
|
|
*
|
|
|
|
*/
|
|
|
|
protected static function flatten_mf2_array ( $mf2 ) {
|
2016-01-11 15:43:53 +00:00
|
|
|
|
|
|
|
if (is_array($mf2) && count($mf2) == 1) {
|
|
|
|
$mf2 = array_pop($mf2);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (is_array($mf2)) {
|
|
|
|
foreach($mf2 as $key => $value) {
|
2016-01-12 12:04:43 +00:00
|
|
|
$mf2[$key] = static::flatten_mf2_array($value);
|
2016-01-11 15:43:53 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return $mf2;
|
|
|
|
}
|
2016-01-10 20:29:41 +00:00
|
|
|
|
2016-01-12 12:04:43 +00:00
|
|
|
/**
|
|
|
|
* find all urls in a text
|
|
|
|
*
|
|
|
|
* @param string $text - text to parse
|
|
|
|
*
|
|
|
|
* @return array array of URLS found in the test
|
|
|
|
*/
|
|
|
|
protected static function extract_urls( &$text ) {
|
|
|
|
$matches = array();
|
|
|
|
preg_match_all("/\b(?:http|https)\:\/\/?[a-zA-Z0-9\.\/\?\:@\-_=#]+\.[a-zA-Z0-9\.\/\?\:@\-_=#]*/i", $text, $matches);
|
|
|
|
|
|
|
|
$matches = $matches[0];
|
2016-01-12 22:36:38 +00:00
|
|
|
|
|
|
|
$matches = array_unique($matches);
|
|
|
|
|
2016-01-12 12:04:43 +00:00
|
|
|
return $matches;
|
|
|
|
}
|
|
|
|
|
2016-01-10 20:29:41 +00:00
|
|
|
/**
|
|
|
|
* validates a post object if it really is a post
|
|
|
|
*
|
2016-01-12 12:04:43 +00:00
|
|
|
* @param $post object Wordpress Post Object to check
|
2016-01-10 20:29:41 +00:00
|
|
|
*
|
|
|
|
* @return bool true if it's a post, false if not
|
|
|
|
*/
|
2016-01-12 12:04:43 +00:00
|
|
|
protected static function is_post ( &$post ) {
|
2016-01-10 20:29:41 +00:00
|
|
|
if ( !empty($post) && is_object($post) && isset($post->ID) && !empty($post->ID) )
|
|
|
|
return true;
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
2016-01-11 15:43:53 +00:00
|
|
|
|
2016-01-12 12:04:43 +00:00
|
|
|
|
2016-01-12 19:13:09 +00:00
|
|
|
|
2016-01-12 12:04:43 +00:00
|
|
|
/**
|
|
|
|
* Converts relative to absolute urls
|
|
|
|
*
|
|
|
|
* code from [webmention](https://github.com/pfefferle/wordpress-webmention)
|
|
|
|
* which is based on the code of 99webtools.com
|
|
|
|
*
|
|
|
|
* @link http://99webtools.com/relative-path-into-absolute-url.php
|
|
|
|
*
|
|
|
|
* @param string $base the base url
|
|
|
|
* @param string $rel the relative url
|
|
|
|
*
|
|
|
|
* @return string the absolute url
|
|
|
|
*/
|
|
|
|
protected static function make_url_absolute( $base, $rel ) {
|
|
|
|
if ( 0 === strpos( $rel, '//' ) ) {
|
|
|
|
return parse_url( $base, PHP_URL_SCHEME ) . ':' . $rel;
|
|
|
|
}
|
|
|
|
// return if already absolute URL
|
|
|
|
if ( parse_url( $rel, PHP_URL_SCHEME ) != '' ) {
|
|
|
|
return $rel;
|
|
|
|
}
|
|
|
|
// queries and anchors
|
|
|
|
if ( '#' == $rel[0] || '?' == $rel[0] ) {
|
|
|
|
return $base . $rel;
|
|
|
|
}
|
|
|
|
// parse base URL and convert to local variables:
|
|
|
|
// $scheme, $host, $path
|
|
|
|
extract( parse_url( $base ) );
|
|
|
|
// remove non-directory element from path
|
|
|
|
$path = preg_replace( '#/[^/]*$#', '', $path );
|
|
|
|
// destroy path if relative url points to root
|
|
|
|
if ( '/' == $rel[0] ) {
|
|
|
|
$path = '';
|
|
|
|
}
|
|
|
|
// dirty absolute URL
|
|
|
|
$abs = "$host";
|
|
|
|
// check port
|
|
|
|
if ( isset( $port ) && ! empty( $port ) ) {
|
|
|
|
$abs .= ":$port";
|
|
|
|
}
|
|
|
|
// add path + rel
|
|
|
|
$abs .= "$path/$rel";
|
|
|
|
// replace '//' or '/./' or '/foo/../' with '/'
|
|
|
|
$re = array( '#(/\.?/)#', '#/(?!\.\.)[^/]+/\.\./#' );
|
|
|
|
for ( $n = 1; $n > 0; $abs = preg_replace( $re, '/', $abs, -1, $n ) ) { }
|
|
|
|
// absolute URL is ready!
|
|
|
|
return $scheme . '://' . $abs;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2016-01-11 15:43:53 +00:00
|
|
|
/**
|
|
|
|
* of there is a comment meta 'avatar' field, use that as avatar for the commenter
|
|
|
|
*
|
|
|
|
* @param string $avatar the current avatar image string
|
|
|
|
* @param mixed $id_or_email this could be anything that triggered the avatar all
|
|
|
|
* @param string $size size for the image to display
|
|
|
|
* @param string $default optional fallback
|
|
|
|
* @param string $alt alt text for the avatar image
|
|
|
|
*/
|
2016-01-12 22:36:38 +00:00
|
|
|
public function get_avatar( $avatar, $id_or_email, $size, $default = '', $alt = '' ) {
|
|
|
|
if ( ! is_object( $id_or_email ) || ! isset( $id_or_email->comment_type ) )
|
2016-01-11 15:43:53 +00:00
|
|
|
return $avatar;
|
|
|
|
|
|
|
|
// check if comment has an avatar
|
2016-01-12 22:36:38 +00:00
|
|
|
$c_avatar = get_comment_meta( $id_or_email->comment_ID, 'avatar', true );
|
2016-01-11 15:43:53 +00:00
|
|
|
|
2016-01-12 22:36:38 +00:00
|
|
|
if ( ! $c_avatar )
|
2016-01-11 15:43:53 +00:00
|
|
|
return $avatar;
|
|
|
|
|
2016-01-12 22:36:38 +00:00
|
|
|
if ( false === $alt )
|
2016-01-11 15:43:53 +00:00
|
|
|
$safe_alt = '';
|
|
|
|
else
|
2016-01-12 22:36:38 +00:00
|
|
|
$safe_alt = esc_attr( $alt );
|
2016-01-11 15:43:53 +00:00
|
|
|
|
2016-01-12 22:36:38 +00:00
|
|
|
return sprintf( '<img alt="%s" src="%s" class="avatar photo u-photo" style="width: %dpx; height: %dpx;" />', $safe_alt, $c_avatar, $size, $size );
|
2016-01-11 15:43:53 +00:00
|
|
|
}
|
|
|
|
|
2016-01-12 12:04:43 +00:00
|
|
|
/**
|
|
|
|
* get content like the_content
|
|
|
|
*
|
|
|
|
* @param object $post - WP Post object
|
|
|
|
*
|
|
|
|
* @return string the_content
|
|
|
|
*/
|
|
|
|
protected static function get_the_content( &$post ){
|
|
|
|
|
2016-01-12 22:36:38 +00:00
|
|
|
if ( ! static::is_post( $post ) )
|
2016-01-12 12:04:43 +00:00
|
|
|
return false;
|
|
|
|
|
|
|
|
if ( $cached = wp_cache_get ( $post->ID, __CLASS__ . __FUNCTION__ ) )
|
|
|
|
return $cached;
|
|
|
|
|
2016-01-12 22:36:38 +00:00
|
|
|
$r = apply_filters( 'the_content', $post->post_content );
|
2016-01-12 12:04:43 +00:00
|
|
|
|
|
|
|
wp_cache_set ( $post->ID, $r, __CLASS__ . __FUNCTION__, static::expire );
|
|
|
|
|
|
|
|
return $r;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* debug messages; will only work if WP_DEBUG is on
|
|
|
|
* or if the level is LOG_ERR, but that will kill the process
|
|
|
|
*
|
|
|
|
* @param string $message
|
|
|
|
* @param int $level
|
|
|
|
*/
|
|
|
|
protected static function debug( $message, $level = LOG_NOTICE ) {
|
|
|
|
if ( @is_array( $message ) || @is_object ( $message ) )
|
2016-01-12 22:36:38 +00:00
|
|
|
$message = json_encode( $message );
|
2016-01-12 12:04:43 +00:00
|
|
|
|
|
|
|
|
|
|
|
switch ( $level ) {
|
|
|
|
case LOG_ERR :
|
|
|
|
wp_die( '<h1>Error:</h1>' . '<p>' . $message . '</p>' );
|
|
|
|
exit;
|
|
|
|
default:
|
2016-01-12 22:36:38 +00:00
|
|
|
if ( ! defined( 'WP_DEBUG' ) || true != WP_DEBUG )
|
2016-01-12 12:04:43 +00:00
|
|
|
return;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
error_log( __CLASS__ . ": " . $message );
|
|
|
|
}
|
|
|
|
|
2016-01-10 20:29:41 +00:00
|
|
|
}
|
2016-01-12 22:36:38 +00:00
|
|
|
$WP_Webmention_Again = new WP_Webmention_Again();
|
2016-01-10 20:29:41 +00:00
|
|
|
|
|
|
|
endif;
|