Skip to content

Instantly share code, notes, and snippets.

@igorhrcek
Created February 16, 2023 20:30
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 igorhrcek/b90e3749ec6dd83781ca10ecb80b86f3 to your computer and use it in GitHub Desktop.
Save igorhrcek/b90e3749ec6dd83781ca10ecb80b86f3 to your computer and use it in GitHub Desktop.
<?php
namespace App\Libraries\Commands;
abstract class AbstractCommand implements CommandInterface
{
/**
* @return array
*/
public function getSynopsis(): array
{
return [];
}
/**
* @return string
*/
final public function getName(): string
{
return sprintf('benchmark %s', $this->getCommandName());
}
/**
* Get the full command name
*
* @return string
*/
abstract protected function getCommandName(): string;
}
<?php
namespace App\Libraries\Commands;
interface CommandInterface
{
/**
* Get command name
*
* @return string
*/
public function getName(): string;
/**
* Executes the command
*
* @param $arguments
* @param $options
*
*/
public function __invoke($arguments, $options);
/**
* Get the positional and associative arguments a command accepts.
*
* @return array
*/
public function getSynopsis(): array;
/**
* Get the command description.
*
* @return string
*/
public function getDescription(): string;
}
<?php
namespace App\Libraries\Commands;
use App\Libraries\Commands\Libraries\CommentsSync;
class CommentsCommand extends AbstractCommand {
public function __invoke($arguments, $options)
{
$sync = (new CommentsSync($options))->sync();
}
/**
* Define a WP CLI command name
*
* @return strirng
*/
protected function getCommandName() : string
{
return "comments-sync";
}
/**
* Returns a brief description of the WP CLI command
*
* @return string
*/
public function getDescription() : string
{
return "Fetch and store thread comments as WordPress comments in articles with connected XenForo Threads.";
}
/**
* Provides definition of the additional command arguments
*
* @return array
*/
public function getSynopsis(): array
{
return [
[
'type' => 'assoc',
'name' => 'published-since',
'description' => 'Defines a time frame in which published articled will be taken into account (now minus passed number). Defaults to `close_comments_days_old`.',
'optional' => true
],
[
'type' => 'assoc',
'name' => 'post_type',
'description' => 'Defines for which post type a comments will be syncronized. Defaults to `post`.',
'optional' => true,
'default' => 'post'
],
];
}
}
<?php
namespace App\Libraries\Commands\Libraries;
class CommentsSync {
/**
* Passed command options
*
* @var array
*/
public array $options;
/**
* WordPress `close_comments_days_old` option value
*
* @var integer
*/
public int $optionCloseCommentsOld;
/**
* XFWP_Requests class instance
*
* @var \XFWP_Requests
*/
private \XFWP_Requests $xf;
/**
* Defines a WordPress post type that will be used for comments sync
*
* @var string
*/
private string $postType;
/**
* Post meta keys which is used for storing XFWP data
*
* @var string
*/
private string $xfwpMetaKey = 'xfwp';
/**
* Default sleep time between XF API calls
*
* @var integer
*/
private int $sleep = 1;
public function __construct(array $options)
{
$this->options = $options;
$this->postType = $options['post_type'];
$this->sleep = (isset($options['sleep'])) ? (int)$options['sleep'] : $this->sleep;
$this->optionCloseCommentsOld = (isset($options['published-since'])) ? (int)$options['published-since'] : get_option('close_comments_days_old');
$this->xf = new \XFWP_Requests();
}
/**
* Girl for everyone and everything
*
* @return void
*/
public function sync() : void
{
$postIds = $this->getPostIdsByConnectedThreads();
$threadsIds = $this->getThreadIds($postIds);
foreach($threadsIds as $threadId) {
$response = $this->xf->thread_posts($threadId['threadId']);
if($response) {
\WP_CLI::log("Processing thread " . $threadId['threadId'] . ", post " . $threadId['postId']);
$this->processThreadPosts($response, $threadId);
}
\WP_CLI::log("Sleeping for " . $this->sleep . " second(s)...");
sleep($this->sleep);
}
\WP_CLI::success("All comments have been synced.");
}
/**
* Returns a list of Connected Threads Ids
*
* With WP_Query we fetch posts within $optionCloseCommentsOld days that have `xfwp` meta_value and then extract thread_ids from it.
*
* @return array
*/
private function getPostIdsByConnectedThreads() : array
{
$args = [
'post_type' => $this->postType,
'post_status' => 'publish',
'fields' => 'ids',
'posts_per_page' => -1,
'date_query' => [
[
'after' => $this->optionCloseCommentsOld . ' days ago'
]
],
'meta_query' => [
[
'key' => $this->xfwpMetaKey,
'compare' => 'EXISTS'
]
]
];
$posts = new \WP_Query($args);
return $posts->posts;
}
/**
* Returns an array of threadIds for posts
*
* @param array $postIds
*
* @return array
*/
private function getThreadIds(array $postIds) : array
{
$threadIds = [];
foreach($postIds as $postId) {
$metas = \get_post_meta($postId, $this->xfwpMetaKey, FALSE);
foreach($metas as $meta) {
if(isset($meta['comments']["thread_id"]) && (int)$meta['comments']["thread_id"] > 0) {
array_push($threadIds, [
"postId" => $postId,
"threadId" => (int)$meta['comments']["thread_id"]
]);
}
}
}
return $threadIds;
}
private function processThreadPosts(array $threadPosts, array $entity) : void
{
//Uhvati komentare za post
$args = [
'post_id' => $entity["postId"],
'count' => true
];
if(!isset($threadPosts["posts"]) || count($threadPosts["posts"]) == 0) {
return;
}
//If WordPress post has 0 comments, add them all
if(get_comments($args) == 0) {
\WP_CLI::log("Article has no comments, storing all comments.");
foreach($threadPosts["posts"] as $threadPost) {
$this->storePostComment($threadPost, $entity);
}
} else {
//This post already has comments in it so we need to inspect each one
//to make sure that we do not override those that are trashed by WordPress admins
global $wpdb;
$query = "SELECT comment_ID FROM {$wpdb->prefix}commentmeta WHERE meta_key = %s AND meta_value = %d";
foreach($threadPosts["posts"] as $threadPost) {
$commentMeta = $wpdb->get_row($wpdb->prepare($query, 'thread_post_id', (int)$threadPost['post_id']));
//We do not have a comment that corresponds to the thread post id
if(!isset($commentMeta->comment_ID)) {
\WP_CLI::log("Storing new thread posts as comments...");
$this->storePostComment($threadPost, $entity);
} else {
$comment = get_comment($commentMeta->comment_ID);
//If comment is approved, update it
if($comment->comment_approved !== "trash") {
\WP_CLI::log("Updating existing comment...");
$this->updatePostComment((int)$commentMeta->comment_ID, $threadPost, $entity);
}
}
}
}
}
/**
* Stores XF Thread post as a comment
*
* @param array $threadPost
* @param array $entity
*
* @return boolean
*/
private function storePostComment(array $threadPost, array $entity) : bool
{
$postDate = $threadPost['post_date'];
$dt = new \DateTime("@$postDate");
$args = [
'comment_post_ID' => $entity['postId'],
'comment_content' => $threadPost['message'],
'comment_author' => $threadPost['user']['username'],
'comment_date' => $dt->format("Y-m-d H:i:s"),
'comment_karma' => $threadPost['reaction_score'],
'comment_approved' => 1,
'comment_meta' => [
'thread_post_id' => $threadPost['post_id'],
'author_reaction_score' => $threadPost['user']['reaction_score'],
]
];
$comment = wp_insert_comment($args);
return $comment > 0;
}
/**
* Updated information of an existing comment in WordPress database
*
* @param integer $commentId
* @param array $threadPost
* @param array $entity
*
* @return boolean
*/
private function updatePostComment(int $commentId, array $threadPost, array $entity) : bool
{
$args = [
'comment_ID' => $commentId,
'comment_content' => $threadPost['message'],
'comment_karma' => $threadPost['reaction_score']
];
return wp_update_comment($args);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment