805 lines
26 KiB
PHP
805 lines
26 KiB
PHP
|
<?php
|
||
|
/*
|
||
|
Plugin Name: Bluesky Publisher for WordPress
|
||
|
Description: A WordPress plugin for publishing posts to Bluesky with customizable formatting, image handling, and queue management.
|
||
|
Version: 1.0.0
|
||
|
Author: Eugene Web Doctor
|
||
|
Author URI: https://eugenewebdoctor.com
|
||
|
License: MIT
|
||
|
License URI: https://opensource.org/licenses/MIT
|
||
|
Text Domain: bluesky-connector
|
||
|
Domain Path: /languages
|
||
|
|
||
|
Copyright (c) 2024 Eugene Web Doctor
|
||
|
|
||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||
|
of this software and associated documentation files (the "Software"), to deal
|
||
|
in the Software without restriction, including without limitation the rights
|
||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||
|
copies of the Software, and to permit persons to whom the Software is
|
||
|
furnished to do so, subject to the following conditions:
|
||
|
|
||
|
The above copyright notice and this permission notice shall be included in all
|
||
|
copies or substantial portions of the Software.
|
||
|
|
||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||
|
SOFTWARE.
|
||
|
*/
|
||
|
|
||
|
// Prevent direct file access
|
||
|
if (!defined('ABSPATH')) {
|
||
|
exit;
|
||
|
}
|
||
|
|
||
|
// Define constants
|
||
|
define('BLUESKY_CONNECTOR_DIR', plugin_dir_path(__FILE__));
|
||
|
define('BLUESKY_CONNECTOR_URL', plugin_dir_url(__FILE__));
|
||
|
define('BLUESKY_CONNECTOR_VERSION', '1.0.0');
|
||
|
|
||
|
// Include necessary files
|
||
|
require_once BLUESKY_CONNECTOR_DIR . 'includes/bluesky-api.php';
|
||
|
require_once BLUESKY_CONNECTOR_DIR . 'includes/bluesky-auth.php';
|
||
|
require_once BLUESKY_CONNECTOR_DIR . 'includes/post-formatter.php';
|
||
|
require_once BLUESKY_CONNECTOR_DIR . 'includes/settings.php';
|
||
|
|
||
|
/**
|
||
|
* Main plugin class
|
||
|
*/
|
||
|
class Bluesky_Connector
|
||
|
{
|
||
|
/**
|
||
|
* Plugin instance
|
||
|
*
|
||
|
* @var Bluesky_Connector|null
|
||
|
*/
|
||
|
private static $instance = null;
|
||
|
|
||
|
/**
|
||
|
* Get plugin instance
|
||
|
*
|
||
|
* @return Bluesky_Connector
|
||
|
*/
|
||
|
public static function get_instance()
|
||
|
{
|
||
|
if (null === self::$instance) {
|
||
|
self::$instance = new self();
|
||
|
}
|
||
|
return self::$instance;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Constructor
|
||
|
*/
|
||
|
private function __construct()
|
||
|
{
|
||
|
// Initialize plugin
|
||
|
add_action('init', array($this, 'init'));
|
||
|
add_action('init', array($this, 'ajax_init'));
|
||
|
|
||
|
// Admin hooks
|
||
|
if (is_admin()) {
|
||
|
add_action('admin_init', array($this, 'admin_init'));
|
||
|
add_action('admin_menu', array($this, 'add_admin_menu'));
|
||
|
add_action('admin_notices', array($this, 'admin_notices'));
|
||
|
add_action('admin_enqueue_scripts', array($this, 'enqueue_admin_scripts'));
|
||
|
add_filter(
|
||
|
'plugin_action_links_' . plugin_basename(BLUESKY_CONNECTOR_DIR . 'bluesky-connector.php'),
|
||
|
array($this, 'add_settings_link')
|
||
|
);
|
||
|
}
|
||
|
|
||
|
// Post hooks
|
||
|
add_action('publish_post', array($this, 'handle_post_publish'), 10, 2);
|
||
|
add_action('process_bluesky_post_queue', array($this, 'process_post_queue'));
|
||
|
add_action('add_meta_boxes', array($this, 'add_post_meta_box'));
|
||
|
add_action('save_post', array($this, 'save_post_meta_box'));
|
||
|
|
||
|
// Plugin activation/deactivation
|
||
|
register_activation_hook(BLUESKY_CONNECTOR_DIR . 'bluesky-connector.php', array($this, 'activate'));
|
||
|
register_deactivation_hook(BLUESKY_CONNECTOR_DIR . 'bluesky-connector.php', array($this, 'deactivate'));
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Initialize plugin
|
||
|
*/
|
||
|
public function init()
|
||
|
{
|
||
|
load_plugin_textdomain('bluesky-connector', false, dirname(plugin_basename(__FILE__)) . '/languages');
|
||
|
|
||
|
if (!wp_next_scheduled('process_bluesky_post_queue')) {
|
||
|
wp_schedule_event(time(), 'hourly', 'process_bluesky_post_queue');
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Initialize AJAX handlers
|
||
|
*/
|
||
|
public function ajax_init()
|
||
|
{
|
||
|
add_action('wp_ajax_bluesky_retry_post', array($this, 'handle_retry_post'));
|
||
|
add_action('wp_ajax_bluesky_share_post', array($this, 'handle_share_post'));
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Initialize admin settings
|
||
|
*/
|
||
|
public function admin_init()
|
||
|
{
|
||
|
// Register settings
|
||
|
register_setting('bluesky_connector_settings', 'bluesky_domain', array(
|
||
|
'type' => 'string',
|
||
|
'default' => 'https://bsky.social',
|
||
|
'sanitize_callback' => function($input) {
|
||
|
// Force the default if empty
|
||
|
if (empty($input)) {
|
||
|
return 'https://bsky.social';
|
||
|
}
|
||
|
// Ensure https:// is present
|
||
|
if (strpos($input, 'https://') !== 0) {
|
||
|
$input = 'https://' . $input;
|
||
|
}
|
||
|
// Remove any trailing slashes
|
||
|
return rtrim($input, '/');
|
||
|
}
|
||
|
));
|
||
|
register_setting('bluesky_connector_settings', 'bluesky_identifier', array(
|
||
|
'type' => 'string',
|
||
|
'sanitize_callback' => 'sanitize_text_field'
|
||
|
));
|
||
|
|
||
|
register_setting('bluesky_connector_settings', 'bluesky_password', array(
|
||
|
'type' => 'string',
|
||
|
'sanitize_callback' => 'sanitize_text_field'
|
||
|
));
|
||
|
|
||
|
register_setting('bluesky_connector_settings', 'bluesky_post_format', array(
|
||
|
'type' => 'string',
|
||
|
'default' => 'title-excerpt-image-link',
|
||
|
'sanitize_callback' => 'sanitize_text_field'
|
||
|
));
|
||
|
register_setting('bluesky_connector_settings', 'bluesky_include_title', array(
|
||
|
'type' => 'boolean',
|
||
|
'default' => true
|
||
|
));
|
||
|
register_setting('bluesky_connector_settings', 'bluesky_title_separator', array(
|
||
|
'type' => 'string',
|
||
|
'default' => "\n\n",
|
||
|
'sanitize_callback' => 'sanitize_text_field'
|
||
|
));
|
||
|
|
||
|
// Add settings sections and fields
|
||
|
add_settings_section(
|
||
|
'bluesky_connector_main',
|
||
|
__('Bluesky Connection Settings', 'bluesky-connector'),
|
||
|
array($this, 'render_settings_section'),
|
||
|
'bluesky_connector_settings'
|
||
|
);
|
||
|
|
||
|
add_settings_section(
|
||
|
'bluesky_format_section',
|
||
|
__('Post Format Settings', 'bluesky-connector'),
|
||
|
array($this, 'render_format_section'),
|
||
|
'bluesky_connector_settings'
|
||
|
);
|
||
|
|
||
|
add_settings_field(
|
||
|
'bluesky_domain',
|
||
|
__('Bluesky Domain', 'bluesky-connector'),
|
||
|
array($this, 'render_domain_field'),
|
||
|
'bluesky_connector_settings',
|
||
|
'bluesky_connector_main'
|
||
|
);
|
||
|
|
||
|
add_settings_field(
|
||
|
'bluesky_identifier',
|
||
|
__('Bluesky Handle', 'bluesky-connector'),
|
||
|
array($this, 'render_identifier_field'),
|
||
|
'bluesky_connector_settings',
|
||
|
'bluesky_connector_main'
|
||
|
);
|
||
|
|
||
|
add_settings_field(
|
||
|
'bluesky_password',
|
||
|
__('App Password', 'bluesky-connector'),
|
||
|
array($this, 'render_password_field'),
|
||
|
'bluesky_connector_settings',
|
||
|
'bluesky_connector_main'
|
||
|
);
|
||
|
|
||
|
add_settings_field(
|
||
|
'bluesky_post_format',
|
||
|
__('Post Layout', 'bluesky-connector'),
|
||
|
array($this, 'render_post_format_field'),
|
||
|
'bluesky_connector_settings',
|
||
|
'bluesky_format_section'
|
||
|
);
|
||
|
|
||
|
add_settings_field(
|
||
|
'bluesky_include_title',
|
||
|
__('Include Post Title', 'bluesky-connector'),
|
||
|
array($this, 'render_include_title_field'),
|
||
|
'bluesky_connector_settings',
|
||
|
'bluesky_format_section'
|
||
|
);
|
||
|
|
||
|
add_settings_field(
|
||
|
'bluesky_title_separator',
|
||
|
__('Title Separator', 'bluesky-connector'),
|
||
|
array($this, 'render_title_separator_field'),
|
||
|
'bluesky_connector_settings',
|
||
|
'bluesky_format_section'
|
||
|
);
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
/**
|
||
|
* Add admin menu
|
||
|
*/
|
||
|
public function add_admin_menu()
|
||
|
{
|
||
|
add_options_page(
|
||
|
__('Bluesky Connector Settings', 'bluesky-connector'),
|
||
|
__('Bluesky Connector', 'bluesky-connector'),
|
||
|
'manage_options',
|
||
|
'bluesky-connector',
|
||
|
array($this, 'render_settings_page')
|
||
|
);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Render settings page
|
||
|
*/
|
||
|
public function render_settings_page()
|
||
|
{
|
||
|
if (!current_user_can('manage_options')) {
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
// Add process queue button
|
||
|
if (isset($_POST['process_queue_now']) && check_admin_referer('bluesky_process_queue')) {
|
||
|
$this->process_post_queue();
|
||
|
add_settings_error(
|
||
|
'bluesky_connector_settings',
|
||
|
'queue_processed',
|
||
|
__('Queue processed.', 'bluesky-connector'),
|
||
|
'success'
|
||
|
);
|
||
|
}
|
||
|
|
||
|
// Check for form submission
|
||
|
if (isset($_POST['action']) && $_POST['action'] === 'update_bluesky_settings') {
|
||
|
check_admin_referer('bluesky_connector_settings');
|
||
|
$this->handle_settings_update();
|
||
|
}
|
||
|
|
||
|
// Get current settings
|
||
|
$settings = array(
|
||
|
'domain' => get_option('bluesky_domain', 'https://bsky.social'),
|
||
|
'identifier' => get_option('bluesky_identifier', ''),
|
||
|
'connection_status' => get_option('bluesky_connection_status', ''),
|
||
|
'last_error' => get_option('bluesky_last_error', '')
|
||
|
);
|
||
|
|
||
|
include BLUESKY_CONNECTOR_DIR . 'templates/settings-page.php';
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Render settings section description
|
||
|
*/
|
||
|
public function render_settings_section()
|
||
|
{
|
||
|
echo '<p>' . esc_html__('Configure your Bluesky connection settings below.', 'bluesky-connector') . '</p>';
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Render domain field
|
||
|
*/
|
||
|
public function render_domain_field()
|
||
|
{
|
||
|
$value = get_option('bluesky_domain', 'https://bsky.social');
|
||
|
echo '<input type="url" name="bluesky_domain" value="' . esc_attr($value) . '" class="regular-text">';
|
||
|
echo '<p class="description">' . esc_html__('The Bluesky API domain (default: https://bsky.social)', 'bluesky-connector') . '</p>';
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Render identifier field
|
||
|
*/
|
||
|
public function render_identifier_field()
|
||
|
{
|
||
|
$value = get_option('bluesky_identifier', '');
|
||
|
echo '<input type="text" name="bluesky_identifier" value="' . esc_attr($value) . '" class="regular-text">';
|
||
|
echo '<p class="description">' . esc_html__('Your Bluesky handle (e.g., username.bsky.social)', 'bluesky-connector') . '</p>';
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Render password field
|
||
|
*/
|
||
|
public function render_password_field()
|
||
|
{
|
||
|
echo '<input type="password" name="bluesky_password" class="regular-text">';
|
||
|
echo '<p class="description">' . esc_html__('Your Bluesky app password (will not be stored)', 'bluesky-connector') . '</p>';
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Handle settings update
|
||
|
*/
|
||
|
private function handle_settings_update()
|
||
|
{
|
||
|
$identifier = sanitize_text_field($_POST['bluesky_identifier']);
|
||
|
$password = sanitize_text_field($_POST['bluesky_password']);
|
||
|
$domain = esc_url_raw($_POST['bluesky_domain']);
|
||
|
|
||
|
if (empty($identifier) || empty($password)) {
|
||
|
add_settings_error(
|
||
|
'bluesky_connector_settings',
|
||
|
'missing_credentials',
|
||
|
__('Both identifier and password are required.', 'bluesky-connector')
|
||
|
);
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
// Try to authenticate
|
||
|
$auth = new Bluesky_Auth($identifier, $password);
|
||
|
$result = $auth->get_access_token();
|
||
|
|
||
|
if (isset($result['error'])) {
|
||
|
update_option('bluesky_connection_status', 'error');
|
||
|
update_option('bluesky_last_error', $result['error']);
|
||
|
add_settings_error(
|
||
|
'bluesky_connector_settings',
|
||
|
'auth_failed',
|
||
|
sprintf(__('Authentication failed: %s', 'bluesky-connector'), $result['error'])
|
||
|
);
|
||
|
} else {
|
||
|
update_option('bluesky_connection_status', 'connected');
|
||
|
update_option('bluesky_last_error', '');
|
||
|
add_settings_error(
|
||
|
'bluesky_connector_settings',
|
||
|
'settings_updated',
|
||
|
__('Settings saved and connected successfully.', 'bluesky-connector'),
|
||
|
'success'
|
||
|
);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Handle post publish
|
||
|
*/
|
||
|
public function handle_post_publish($post_id, $post)
|
||
|
{
|
||
|
// Skip if not a public post
|
||
|
if ($post->post_status !== 'publish' || $post->post_type !== 'post') {
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
// Skip if already posted
|
||
|
if (get_post_meta($post_id, '_bluesky_posted', true)) {
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
// Add to queue
|
||
|
$queue = get_option('bluesky_post_queue', array());
|
||
|
$queue[] = $post_id;
|
||
|
update_option('bluesky_post_queue', array_unique($queue));
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Process post queue
|
||
|
*/
|
||
|
public function process_post_queue()
|
||
|
{
|
||
|
$queue = get_option('bluesky_post_queue', array());
|
||
|
if (empty($queue)) {
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
$access_token = get_option('bluesky_access_jwt');
|
||
|
$did = get_option('bluesky_did');
|
||
|
|
||
|
if (!$access_token || !$did) {
|
||
|
error_log('Bluesky Connector: Missing access token or DID');
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
foreach ($queue as $key => $post_id) {
|
||
|
$post = get_post($post_id);
|
||
|
if (!$post) {
|
||
|
unset($queue[$key]);
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
try {
|
||
|
$formatter = new Post_Formatter($access_token, $did);
|
||
|
$response = $formatter->format_and_post($post);
|
||
|
|
||
|
if (!isset($response['error'])) {
|
||
|
unset($queue[$key]);
|
||
|
update_post_meta($post_id, '_bluesky_post_id', $response['uri']);
|
||
|
update_post_meta($post_id, '_bluesky_posted', current_time('mysql'));
|
||
|
update_post_meta($post_id, '_bluesky_status', 'success');
|
||
|
} else {
|
||
|
update_post_meta($post_id, '_bluesky_status', 'error');
|
||
|
update_post_meta($post_id, '_bluesky_error', $response['error']);
|
||
|
error_log('Bluesky Post Error: ' . print_r($response['error'], true));
|
||
|
}
|
||
|
} catch (Exception $e) {
|
||
|
error_log('Bluesky Connector Exception: ' . $e->getMessage());
|
||
|
update_post_meta($post_id, '_bluesky_status', 'error');
|
||
|
update_post_meta($post_id, '_bluesky_error', $e->getMessage());
|
||
|
}
|
||
|
}
|
||
|
|
||
|
update_option('bluesky_post_queue', array_values($queue));
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Add post meta box
|
||
|
*/
|
||
|
public function add_post_meta_box()
|
||
|
{
|
||
|
add_meta_box(
|
||
|
'bluesky_post_status',
|
||
|
__('Bluesky Status', 'bluesky-connector'),
|
||
|
array($this, 'render_post_meta_box'),
|
||
|
'post',
|
||
|
'side',
|
||
|
'default'
|
||
|
);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Render post meta box
|
||
|
*/
|
||
|
public function render_post_meta_box($post)
|
||
|
{
|
||
|
$status = get_post_meta($post->ID, '_bluesky_status', true);
|
||
|
$posted_date = get_post_meta($post->ID, '_bluesky_posted', true);
|
||
|
$error = get_post_meta($post->ID, '_bluesky_error', true);
|
||
|
$post_id = get_post_meta($post->ID, '_bluesky_post_id', true);
|
||
|
|
||
|
include BLUESKY_CONNECTOR_DIR . 'templates/post-meta-box.php';
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Save post meta box
|
||
|
*/
|
||
|
public function save_post_meta_box($post_id)
|
||
|
{
|
||
|
if (!current_user_can('edit_post', $post_id)) {
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
if (defined('DOING_AUTOSAVE') && DOING_AUTOSAVE) {
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
// Add any custom meta box saving logic here
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Handle retry post AJAX request
|
||
|
*/
|
||
|
public function handle_retry_post()
|
||
|
{
|
||
|
check_ajax_referer('bluesky_admin', 'nonce');
|
||
|
|
||
|
if (!current_user_can('edit_posts')) {
|
||
|
wp_send_json_error(['message' => __('Permission denied.', 'bluesky-connector')]);
|
||
|
}
|
||
|
|
||
|
$post_id = intval($_POST['post_id']);
|
||
|
$post = get_post($post_id);
|
||
|
|
||
|
if (!$post) {
|
||
|
wp_send_json_error(['message' => __('Post not found.', 'bluesky-connector')]);
|
||
|
}
|
||
|
|
||
|
// Add to queue
|
||
|
$queue = get_option('bluesky_post_queue', array());
|
||
|
$queue[] = $post_id;
|
||
|
update_option('bluesky_post_queue', array_unique($queue));
|
||
|
|
||
|
// Update post meta
|
||
|
update_post_meta($post_id, '_bluesky_status', 'queued');
|
||
|
delete_post_meta($post_id, '_bluesky_error');
|
||
|
|
||
|
wp_send_json_success(['message' => __('Post queued for retry.', 'bluesky-connector')]);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Handle share post AJAX request
|
||
|
*/
|
||
|
public function handle_share_post()
|
||
|
{
|
||
|
check_ajax_referer('bluesky_admin', 'nonce');
|
||
|
|
||
|
if (!current_user_can('edit_posts')) {
|
||
|
wp_send_json_error(['message' => __('Permission denied.', 'bluesky-connector')]);
|
||
|
}
|
||
|
|
||
|
$post_id = intval($_POST['post_id']);
|
||
|
$post = get_post($post_id);
|
||
|
|
||
|
if (!$post) {
|
||
|
wp_send_json_error(['message' => __('Post not found.', 'bluesky-connector')]);
|
||
|
}
|
||
|
|
||
|
// Add to queue
|
||
|
$queue = get_option('bluesky_post_queue', array());
|
||
|
$queue[] = $post_id;
|
||
|
update_option('bluesky_post_queue', array_unique($queue));
|
||
|
|
||
|
// Update post meta
|
||
|
update_post_meta($post_id, '_bluesky_status', 'queued');
|
||
|
|
||
|
wp_send_json_success(['message' => __('Post queued for sharing.', 'bluesky-connector')]);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Enqueue admin scripts and styles
|
||
|
*/
|
||
|
public function enqueue_admin_scripts($hook)
|
||
|
{
|
||
|
if ('post.php' !== $hook && 'post-new.php' !== $hook) {
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
wp_enqueue_script(
|
||
|
'bluesky-admin',
|
||
|
BLUESKY_CONNECTOR_URL . 'assets/js/admin.js',
|
||
|
array('jquery'),
|
||
|
BLUESKY_CONNECTOR_VERSION,
|
||
|
true
|
||
|
);
|
||
|
|
||
|
wp_localize_script('bluesky-admin', 'blueskyAdmin', array(
|
||
|
'nonce' => wp_create_nonce('bluesky_admin'),
|
||
|
'ajaxUrl' => admin_url('admin-ajax.php'),
|
||
|
'strings' => array(
|
||
|
'error' => __('An error occurred. Please try again.', 'bluesky-connector'),
|
||
|
'success' => __('Operation completed successfully.', 'bluesky-connector')
|
||
|
)
|
||
|
));
|
||
|
|
||
|
wp_enqueue_style(
|
||
|
'bluesky-admin',
|
||
|
BLUESKY_CONNECTOR_URL . 'assets/css/admin.css',
|
||
|
array(),
|
||
|
BLUESKY_CONNECTOR_VERSION
|
||
|
);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Plugin activation
|
||
|
*/
|
||
|
public function activate()
|
||
|
{
|
||
|
// Create necessary options with default values
|
||
|
add_option('bluesky_domain', 'https://bsky.social');
|
||
|
add_option('bluesky_post_queue', array());
|
||
|
add_option('bluesky_connection_status', '');
|
||
|
|
||
|
// Schedule cron job
|
||
|
if (!wp_next_scheduled('process_bluesky_post_queue')) {
|
||
|
wp_schedule_event(time(), 'hourly', 'process_bluesky_post_queue');
|
||
|
}
|
||
|
|
||
|
// Create custom capabilities
|
||
|
$role = get_role('administrator');
|
||
|
if ($role) {
|
||
|
$role->add_cap('manage_bluesky_settings');
|
||
|
}
|
||
|
|
||
|
// Flush rewrite rules
|
||
|
flush_rewrite_rules();
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Plugin deactivation
|
||
|
*/
|
||
|
public function deactivate()
|
||
|
{
|
||
|
// Clear scheduled events
|
||
|
wp_clear_scheduled_hook('process_bluesky_post_queue');
|
||
|
|
||
|
// Remove custom capabilities
|
||
|
$role = get_role('administrator');
|
||
|
if ($role) {
|
||
|
$role->remove_cap('manage_bluesky_settings');
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Display admin notices
|
||
|
*/
|
||
|
public function admin_notices()
|
||
|
{
|
||
|
if (!current_user_can('manage_options')) {
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
$screen = get_current_screen();
|
||
|
if ($screen->id !== 'settings_page_bluesky-connector') {
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
settings_errors('bluesky_connector_settings');
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Add settings link to plugins page
|
||
|
*/
|
||
|
public function add_settings_link($links)
|
||
|
{
|
||
|
$settings_link = sprintf(
|
||
|
'<a href="%s">%s</a>',
|
||
|
admin_url('options-general.php?page=bluesky-connector'),
|
||
|
__('Settings', 'bluesky-connector')
|
||
|
);
|
||
|
array_unshift($links, $settings_link);
|
||
|
return $links;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Log plugin errors
|
||
|
*/
|
||
|
private function log_error($message, $data = array())
|
||
|
{
|
||
|
if (defined('WP_DEBUG') && WP_DEBUG) {
|
||
|
error_log(sprintf(
|
||
|
'[Bluesky Connector] %s | Data: %s',
|
||
|
$message,
|
||
|
print_r($data, true)
|
||
|
));
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Filter for customizing post content before sending to Bluesky
|
||
|
*/
|
||
|
public function filter_post_content($content, $post)
|
||
|
{
|
||
|
return apply_filters('bluesky_post_content', $content, $post);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Check if post should be shared to Bluesky
|
||
|
*/
|
||
|
private function should_share_post($post)
|
||
|
{
|
||
|
// Skip if post is not published
|
||
|
if ($post->post_status !== 'publish') {
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
// Skip if post type is not supported
|
||
|
$supported_post_types = apply_filters('bluesky_supported_post_types', array('post'));
|
||
|
if (!in_array($post->post_type, $supported_post_types)) {
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
// Skip if already shared
|
||
|
if (get_post_meta($post->ID, '_bluesky_posted', true)) {
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
// Allow custom filtering
|
||
|
return apply_filters('bluesky_should_share_post', true, $post);
|
||
|
}
|
||
|
|
||
|
public function render_format_section() {
|
||
|
echo '<p>' . esc_html__('Customize how your posts appear on Bluesky.', 'bluesky-connector') . '</p>';
|
||
|
}
|
||
|
|
||
|
public function render_post_format_field() {
|
||
|
$format = get_option('bluesky_post_format', 'title-excerpt-image-link');
|
||
|
$options = array(
|
||
|
'title-excerpt-image-link' => __('Title → Excerpt → Image → Link', 'bluesky-connector'),
|
||
|
'title-image-excerpt-link' => __('Title → Image → Excerpt → Link', 'bluesky-connector'),
|
||
|
'image-title-excerpt-link' => __('Image → Title → Excerpt → Link', 'bluesky-connector'),
|
||
|
'excerpt-image-link' => __('Excerpt → Image → Link', 'bluesky-connector'),
|
||
|
'title-excerpt-link' => __('Title → Excerpt → Link (No Image)', 'bluesky-connector'),
|
||
|
);
|
||
|
|
||
|
echo '<select name="bluesky_post_format" id="bluesky_post_format">';
|
||
|
foreach ($options as $value => $label) {
|
||
|
printf(
|
||
|
'<option value="%s" %s>%s</option>',
|
||
|
esc_attr($value),
|
||
|
selected($format, $value, false),
|
||
|
esc_html($label)
|
||
|
);
|
||
|
}
|
||
|
echo '</select>';
|
||
|
echo '<p class="description">' . esc_html__('Choose how you want your posts to be formatted on Bluesky.', 'bluesky-connector') . '</p>';
|
||
|
}
|
||
|
|
||
|
public function render_include_title_field() {
|
||
|
$include_title = get_option('bluesky_include_title', true);
|
||
|
printf(
|
||
|
'<label><input type="checkbox" name="bluesky_include_title" value="1" %s> %s</label>',
|
||
|
checked($include_title, true, false),
|
||
|
__('Include post title at the beginning', 'bluesky-connector')
|
||
|
);
|
||
|
echo '<p class="description">' . esc_html__('Add the post title to the beginning of each Bluesky post.', 'bluesky-connector') . '</p>';
|
||
|
}
|
||
|
|
||
|
public function render_title_separator_field() {
|
||
|
$separator = get_option('bluesky_title_separator', "\n\n");
|
||
|
$options = array(
|
||
|
"\n\n" => __('Double Line Break', 'bluesky-connector'),
|
||
|
"\n" => __('Single Line Break', 'bluesky-connector'),
|
||
|
" - " => __('Dash', 'bluesky-connector'),
|
||
|
": " => __('Colon', 'bluesky-connector'),
|
||
|
);
|
||
|
|
||
|
echo '<select name="bluesky_title_separator">';
|
||
|
foreach ($options as $value => $label) {
|
||
|
printf(
|
||
|
'<option value="%s" %s>%s</option>',
|
||
|
esc_attr($value),
|
||
|
selected($separator, $value, false),
|
||
|
esc_html($label)
|
||
|
);
|
||
|
}
|
||
|
echo '</select>';
|
||
|
echo '<p class="description">' . esc_html__('Choose how to separate the title from the excerpt.', 'bluesky-connector') . '</p>';
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Initialize the plugin
|
||
|
*/
|
||
|
function bluesky_connector_init()
|
||
|
{
|
||
|
return Bluesky_Connector::get_instance();
|
||
|
}
|
||
|
|
||
|
// Initialize the plugin
|
||
|
add_action('plugins_loaded', 'bluesky_connector_init');
|
||
|
|
||
|
/**
|
||
|
* Register uninstall hook
|
||
|
*/
|
||
|
register_uninstall_hook(__FILE__, 'bluesky_connector_uninstall');
|
||
|
|
||
|
/**
|
||
|
* Clean up plugin data on uninstall
|
||
|
*/
|
||
|
function bluesky_connector_uninstall()
|
||
|
{
|
||
|
// Only run if explicitly uninstalling
|
||
|
if (!defined('WP_UNINSTALL_PLUGIN')) {
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
// Remove options
|
||
|
delete_option('bluesky_domain');
|
||
|
delete_option('bluesky_identifier');
|
||
|
delete_option('bluesky_access_jwt');
|
||
|
delete_option('bluesky_refresh_jwt');
|
||
|
delete_option('bluesky_did');
|
||
|
delete_option('bluesky_post_queue');
|
||
|
delete_option('bluesky_connection_status');
|
||
|
delete_option('bluesky_last_error');
|
||
|
|
||
|
// Remove capabilities
|
||
|
$role = get_role('administrator');
|
||
|
if ($role) {
|
||
|
$role->remove_cap('manage_bluesky_settings');
|
||
|
}
|
||
|
|
||
|
// Clear scheduled hooks
|
||
|
wp_clear_scheduled_hook('process_bluesky_post_queue');
|
||
|
|
||
|
// Remove post meta
|
||
|
global $wpdb;
|
||
|
$wpdb->query("DELETE FROM {$wpdb->postmeta} WHERE meta_key LIKE '_bluesky_%'");
|
||
|
}
|