Compare commits
2 Commits
038d32386d
...
6b9d9d862e
Author | SHA1 | Date | |
---|---|---|---|
6b9d9d862e | |||
|
3bb8217690 |
0
.gitignore
vendored
Normal file
0
.gitignore
vendored
Normal file
3
.vscode/settings.json
vendored
Normal file
3
.vscode/settings.json
vendored
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
{
|
||||||
|
"direnv.path.executable": "/usr/bin/direnv"
|
||||||
|
}
|
132
README.md
132
README.md
@ -1,66 +1,98 @@
|
|||||||
# Bluesky Publisher for WordPress
|
=== Share On Bluesky ===
|
||||||
|
Contributors: eugenewebdoctor
|
||||||
|
Tags: bluesky, social media, cross-posting, atproto
|
||||||
|
Requires at least: 5.0
|
||||||
|
Tested up to: 6.7
|
||||||
|
Requires PHP: 7.4
|
||||||
|
Stable tag: 1.0.0
|
||||||
|
License: MIT
|
||||||
|
License URI: https://opensource.org/licenses/MIT
|
||||||
|
|
||||||
Automatically share your WordPress posts to Bluesky with customizable formatting, image handling, and queue management.
|
A simple WordPress plugin for automatically sharing your posts to Bluesky with support for featured images and customizable formatting.
|
||||||
|
|
||||||
## Description
|
== Description ==
|
||||||
|
|
||||||
Bluesky Publisher for WordPress enables automatic cross-posting of your WordPress content to Bluesky. With customizable post formatting, image support, and reliable queue management, you can ensure your content looks great on Bluesky.
|
Share On Bluesky is a lightweight WordPress plugin that enables automatic cross-posting to Bluesky. When you publish a post, it automatically shares it to your Bluesky account with proper formatting and image support.
|
||||||
|
|
||||||
### Features
|
= Key Features =
|
||||||
|
|
||||||
* Automatic post sharing to Bluesky
|
* One-click connection to Bluesky using your handle and app password
|
||||||
* Customizable post formatting
|
* Automatic post sharing when you publish
|
||||||
* Featured image support with automatic resizing
|
* Featured image support with auto-resizing
|
||||||
* Queue management for reliable posting
|
* Customizable post format with title and excerpt options
|
||||||
* Post meta box for post status and manual controls
|
* Manual post/repost controls from post editor
|
||||||
* Support for post titles, excerpts, and links
|
* Secure token management with automatic refresh
|
||||||
* Proper spacing and formatting of posts
|
|
||||||
* Automatic token refresh
|
|
||||||
|
|
||||||
## Requirements
|
== Installation ==
|
||||||
|
|
||||||
- WordPress 5.0 or higher
|
There are two ways to install the Share On Bluesky plugin:
|
||||||
- PHP 7.4 or higher
|
|
||||||
- Bluesky account
|
|
||||||
|
|
||||||
## Installation
|
= From WordPress Dashboard (Recommended) =
|
||||||
|
|
||||||
1. Upload 'bluesky-connector' folder to the '/wp-content/plugins/' directory
|
1. Go to your WordPress Dashboard > Plugins > Add New
|
||||||
2. Activate the plugin through the 'Plugins' menu in WordPress
|
2. Search for "Share On Bluesky"
|
||||||
3. Go to Settings > Bluesky Publisher to configure your connection
|
3. Click "Install Now" next to the Share On Bluesky plugin
|
||||||
|
4. After installation completes, click "Activate"
|
||||||
|
5. Go to Settings > Bluesky to configure your connection
|
||||||
|
|
||||||
## Configuration
|
= Manual Installation =
|
||||||
|
|
||||||
1. Get your Bluesky app password
|
1. Download the 'share-on-bluesky' plugin from WordPress.org
|
||||||
2. Enter your Bluesky handle and app password in the settings
|
2. Go to your WordPress Dashboard > Plugins > Add New > Upload Plugin
|
||||||
3. Choose your preferred post format
|
3. Choose the downloaded zip file and click "Install Now"
|
||||||
4. Start publishing!
|
4. After installation completes, click "Activate"
|
||||||
|
5. Go to Settings > Bluesky to configure your connection
|
||||||
|
|
||||||
## Frequently Asked Questions
|
= After Installation =
|
||||||
|
|
||||||
### Where do I find my Bluesky app password?
|
1. Enter your Bluesky handle (username.bsky.social)
|
||||||
|
2. Generate and enter an app password from your Bluesky account settings
|
||||||
|
3. Choose your preferred post format options
|
||||||
|
4. Test by publishing a new post
|
||||||
|
|
||||||
You can generate an app password in your Bluesky account settings under "App Passwords".
|
== Frequently Asked Questions ==
|
||||||
|
|
||||||
### What happens if an error occurs during posting?
|
= Where do I find my Bluesky app password? =
|
||||||
|
|
||||||
Posts are added to a queue and the plugin will automatically retry failed posts. You can also manually retry posts from the post editor.
|
You can generate an app password in your Bluesky account settings under "App Passwords". Never use your main account password.
|
||||||
|
|
||||||
## Changelog
|
= How are images handled? =
|
||||||
|
|
||||||
### 1.0.0
|
The plugin automatically uploads your post's featured image to Bluesky when sharing. Images are resized if needed to meet Bluesky's size limits.
|
||||||
|
|
||||||
|
= Can I manually control what gets posted? =
|
||||||
|
|
||||||
|
Yes! Each post has a Bluesky status box where you can manually share, retry, or repost content.
|
||||||
|
|
||||||
|
== Screenshots ==
|
||||||
|
|
||||||
|
1. Settings page showing connection and format options
|
||||||
|
2. Post editor integration with Bluesky status and controls
|
||||||
|
|
||||||
|
== Changelog ==
|
||||||
|
|
||||||
|
= 1.0.0 =
|
||||||
* Initial release
|
* Initial release
|
||||||
|
* Automatic post sharing with featured images
|
||||||
* Customizable post formatting
|
* Customizable post formatting
|
||||||
* Image support with automatic resizing
|
* Manual post controls
|
||||||
* Queue management system
|
* Secure token management
|
||||||
* Post meta box controls
|
|
||||||
* Token refresh functionality
|
|
||||||
|
|
||||||
## License
|
== Privacy Policy ==
|
||||||
|
|
||||||
This project is licensed under the MIT License - see below for details:
|
This plugin connects to Bluesky's servers (bsky.social) to share your posts. It stores:
|
||||||
|
* Your Bluesky handle
|
||||||
|
* Authentication tokens (securely encrypted)
|
||||||
|
* Post sharing status metadata
|
||||||
|
|
||||||
|
No other personal data is collected or shared.
|
||||||
|
|
||||||
|
== Credits ==
|
||||||
|
|
||||||
|
Developed by [Eugene Web Doctor](https://eugenewebdoctor.com)
|
||||||
|
|
||||||
|
== License ==
|
||||||
|
|
||||||
```
|
|
||||||
MIT License
|
MIT License
|
||||||
|
|
||||||
Copyright (c) 2024 Eugene Web Doctor
|
Copyright (c) 2024 Eugene Web Doctor
|
||||||
@ -82,23 +114,13 @@ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|||||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
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
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
SOFTWARE.
|
SOFTWARE.
|
||||||
```
|
|
||||||
|
|
||||||
## Credits
|
== Support Development ==
|
||||||
|
|
||||||
Developed by [Eugene Web Doctor](https://eugenewebdoctor.com)
|
|
||||||
|
|
||||||
## Support Development
|
|
||||||
|
|
||||||
If you find this plugin useful, consider supporting its development:
|
If you find this plugin useful, consider supporting its development:
|
||||||
|
|
||||||
### Lightning Network
|
Lightning Network:
|
||||||
```
|
`enki@zap.sovbit.host`
|
||||||
enki@zap.sovbit.host
|
|
||||||
```
|
|
||||||
|
|
||||||
### On-Chain Bitcoin
|
On-Chain Bitcoin:
|
||||||
```
|
`bc1pe60ykxhl6h8j6w7dpwrn7qzcyay6l52dkfeulkgg72eezgmms3wss3ul42`
|
||||||
bc1pe60ykxhl6h8j6w7dpwrn7qzcyay6l52dkfeulkgg72eezgmms3wss3ul42
|
|
||||||
|
|
||||||
```
|
|
@ -1,4 +1,3 @@
|
|||||||
|
|
||||||
.bluesky-post-status {
|
.bluesky-post-status {
|
||||||
padding: 10px;
|
padding: 10px;
|
||||||
}
|
}
|
||||||
@ -37,3 +36,21 @@
|
|||||||
width: 100%;
|
width: 100%;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Button States */
|
||||||
|
.updating-message {
|
||||||
|
position: relative;
|
||||||
|
padding-left: 24px !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.updating-message:before {
|
||||||
|
content: '';
|
||||||
|
position: absolute;
|
||||||
|
top: 50%;
|
||||||
|
left: 8px;
|
||||||
|
margin-top: -8px;
|
||||||
|
width: 16px;
|
||||||
|
height: 16px;
|
||||||
|
background: url(../images/spinner.gif) no-repeat center;
|
||||||
|
background-size: 16px 16px;
|
||||||
|
}
|
@ -1,62 +1,145 @@
|
|||||||
jQuery(document).ready(function($) {
|
jQuery(document).ready(function($) {
|
||||||
// Retry posting to Bluesky
|
// Post status container element
|
||||||
$('.bluesky-retry-post').on('click', function(e) {
|
const $statusContainer = $('.bluesky-post-status');
|
||||||
e.preventDefault();
|
|
||||||
var button = $(this);
|
|
||||||
var postId = button.data('post-id');
|
|
||||||
|
|
||||||
button.prop('disabled', true);
|
// Helper function to display status messages
|
||||||
|
function updateStatus(message, type = 'info') {
|
||||||
|
const $status = $(`<div class="notice notice-${type} is-dismissible"><p>${message}</p></div>`);
|
||||||
|
$statusContainer.prepend($status);
|
||||||
|
|
||||||
|
// Add dismiss button functionality
|
||||||
|
$status.find('.notice-dismiss').on('click', function() {
|
||||||
|
$status.fadeOut(300, function() { $(this).remove(); });
|
||||||
|
});
|
||||||
|
|
||||||
|
// Auto dismiss after 5 seconds for success messages
|
||||||
|
if (type === 'success') {
|
||||||
|
setTimeout(() => {
|
||||||
|
$status.fadeOut(300, function() { $(this).remove(); });
|
||||||
|
}, 5000);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handle immediate posting
|
||||||
|
$('.bluesky-share-post').on('click', function(e) {
|
||||||
|
e.preventDefault();
|
||||||
|
const $button = $(this);
|
||||||
|
const postId = $button.data('post-id');
|
||||||
|
const nonce = $button.data('nonce');
|
||||||
|
|
||||||
|
// Disable button and show loading state
|
||||||
|
$button.prop('disabled', true)
|
||||||
|
.addClass('updating-message')
|
||||||
|
.text(blueskyAdmin.strings.publishing);
|
||||||
|
|
||||||
$.ajax({
|
$.ajax({
|
||||||
url: ajaxurl,
|
url: blueskyAdmin.ajaxUrl,
|
||||||
type: 'POST',
|
type: 'POST',
|
||||||
data: {
|
data: {
|
||||||
action: 'bluesky_retry_post',
|
action: 'bluesky_post_now',
|
||||||
post_id: postId,
|
post_id: postId,
|
||||||
nonce: blueskyAdmin.nonce
|
nonce: nonce
|
||||||
},
|
},
|
||||||
success: function(response) {
|
success: function(response) {
|
||||||
if (response.success) {
|
if (response.success) {
|
||||||
|
updateStatus(response.data.message, 'success');
|
||||||
location.reload();
|
location.reload();
|
||||||
} else {
|
} else {
|
||||||
alert(response.data.message || 'Error retrying post');
|
updateStatus(response.data.message, 'error');
|
||||||
button.prop('disabled', false);
|
$button.prop('disabled', false)
|
||||||
|
.removeClass('updating-message')
|
||||||
|
.text(blueskyAdmin.strings.retry);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
error: function() {
|
error: function(xhr, status, error) {
|
||||||
alert('Network error. Please try again.');
|
updateStatus(blueskyAdmin.strings.error, 'error');
|
||||||
button.prop('disabled', false);
|
$button.prop('disabled', false)
|
||||||
|
.removeClass('updating-message')
|
||||||
|
.text(blueskyAdmin.strings.retry);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
// Share post to Bluesky
|
// Handle retry posting
|
||||||
$('.bluesky-share-post').on('click', function(e) {
|
$('.bluesky-retry-post').on('click', function(e) {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
var button = $(this);
|
const $button = $(this);
|
||||||
var postId = button.data('post-id');
|
const postId = $button.data('post-id');
|
||||||
|
const nonce = $button.data('nonce');
|
||||||
|
|
||||||
button.prop('disabled', true);
|
// Disable button and show loading state
|
||||||
|
$button.prop('disabled', true)
|
||||||
|
.addClass('updating-message')
|
||||||
|
.text(blueskyAdmin.strings.retrying);
|
||||||
|
|
||||||
$.ajax({
|
$.ajax({
|
||||||
url: ajaxurl,
|
url: blueskyAdmin.ajaxUrl,
|
||||||
type: 'POST',
|
type: 'POST',
|
||||||
data: {
|
data: {
|
||||||
action: 'bluesky_share_post',
|
action: 'bluesky_post_now',
|
||||||
post_id: postId,
|
post_id: postId,
|
||||||
nonce: blueskyAdmin.nonce
|
nonce: nonce
|
||||||
},
|
},
|
||||||
success: function(response) {
|
success: function(response) {
|
||||||
if (response.success) {
|
if (response.success) {
|
||||||
|
updateStatus(response.data.message, 'success');
|
||||||
location.reload();
|
location.reload();
|
||||||
} else {
|
} else {
|
||||||
alert(response.data.message || 'Error sharing post');
|
updateStatus(response.data.message, 'error');
|
||||||
button.prop('disabled', false);
|
$button.prop('disabled', false)
|
||||||
|
.removeClass('updating-message')
|
||||||
|
.text(blueskyAdmin.strings.retry);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
error: function() {
|
error: function(xhr, status, error) {
|
||||||
alert('Network error. Please try again.');
|
updateStatus(blueskyAdmin.strings.error, 'error');
|
||||||
button.prop('disabled', false);
|
$button.prop('disabled', false)
|
||||||
|
.removeClass('updating-message')
|
||||||
|
.text(blueskyAdmin.strings.retry);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// Handle reposting
|
||||||
|
$('.bluesky-repost').on('click', function(e) {
|
||||||
|
e.preventDefault();
|
||||||
|
const $button = $(this);
|
||||||
|
const postId = $button.data('post-id');
|
||||||
|
const nonce = $button.data('nonce');
|
||||||
|
|
||||||
|
if (!confirm(blueskyAdmin.strings.confirmRepost)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$button.prop('disabled', true)
|
||||||
|
.addClass('updating-message')
|
||||||
|
.text(blueskyAdmin.strings.reposting);
|
||||||
|
|
||||||
|
$.ajax({
|
||||||
|
url: blueskyAdmin.ajaxUrl,
|
||||||
|
type: 'POST',
|
||||||
|
data: {
|
||||||
|
action: 'bluesky_post_now',
|
||||||
|
post_id: postId,
|
||||||
|
nonce: nonce,
|
||||||
|
repost: true
|
||||||
|
},
|
||||||
|
success: function(response) {
|
||||||
|
if (response.success) {
|
||||||
|
updateStatus(response.data.message, 'success');
|
||||||
|
location.reload();
|
||||||
|
} else {
|
||||||
|
updateStatus(response.data.message, 'error');
|
||||||
|
$button.prop('disabled', false)
|
||||||
|
.removeClass('updating-message')
|
||||||
|
.text(blueskyAdmin.strings.repost);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
error: function(xhr, status, error) {
|
||||||
|
updateStatus(blueskyAdmin.strings.error, 'error');
|
||||||
|
$button.prop('disabled', false)
|
||||||
|
.removeClass('updating-message')
|
||||||
|
.text(blueskyAdmin.strings.repost);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
Binary file not shown.
File diff suppressed because it is too large
Load Diff
@ -1,15 +1,18 @@
|
|||||||
<?php
|
<?php
|
||||||
class Bluesky_API {
|
class Bluesky_API {
|
||||||
private $api_url = 'https://bsky.social/xrpc'; // Replace with the actual API endpoint
|
private $api_url = 'https://bsky.social/xrpc';
|
||||||
private $api_key;
|
private $api_key;
|
||||||
private $did;
|
private $did;
|
||||||
|
|
||||||
public function __construct($api_key, $did) {
|
public function __construct($api_key, $did) {
|
||||||
$this->api_key = $api_key;
|
$this->api_key = $api_key;
|
||||||
$this->did = $did;
|
$this->did = $did;
|
||||||
|
error_log('[Bluesky Connector] Initializing API with DID: ' . $did);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function create_post($post_data) {
|
public function create_post($post_data) {
|
||||||
|
error_log('[Bluesky Connector] Creating post with data: ' . wp_json_encode($post_data));
|
||||||
|
|
||||||
$headers = array(
|
$headers = array(
|
||||||
'Authorization' => 'Bearer ' . $this->api_key,
|
'Authorization' => 'Bearer ' . $this->api_key,
|
||||||
'Content-Type' => 'application/json',
|
'Content-Type' => 'application/json',
|
||||||
@ -17,54 +20,96 @@ class Bluesky_API {
|
|||||||
|
|
||||||
$response = wp_remote_post($this->api_url . '/com.atproto.repo.createRecord', array(
|
$response = wp_remote_post($this->api_url . '/com.atproto.repo.createRecord', array(
|
||||||
'headers' => $headers,
|
'headers' => $headers,
|
||||||
'body' => json_encode(array(
|
'body' => wp_json_encode(array(
|
||||||
'repo' => $this->did,
|
'repo' => $this->did,
|
||||||
'collection' => 'app.bsky.feed.post',
|
'collection' => 'app.bsky.feed.post',
|
||||||
'record' => $post_data,
|
'record' => $post_data,
|
||||||
)),
|
)),
|
||||||
|
'timeout' => 30,
|
||||||
));
|
));
|
||||||
|
|
||||||
if (is_wp_error($response)) {
|
if (is_wp_error($response)) {
|
||||||
|
error_log('[Bluesky Connector] Create post error: ' . $response->get_error_message());
|
||||||
return array('error' => $response->get_error_message());
|
return array('error' => $response->get_error_message());
|
||||||
}
|
}
|
||||||
|
|
||||||
return json_decode(wp_remote_retrieve_body($response), true);
|
$response_code = wp_remote_retrieve_response_code($response);
|
||||||
|
$response_body = wp_remote_retrieve_body($response);
|
||||||
|
error_log('[Bluesky Connector] Create post response code: ' . $response_code);
|
||||||
|
error_log('[Bluesky Connector] Create post response: ' . $response_body);
|
||||||
|
|
||||||
|
return json_decode($response_body, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function resolve_handle($handle) {
|
public function resolve_handle($handle) {
|
||||||
|
error_log('[Bluesky Connector] Resolving handle: ' . $handle);
|
||||||
|
|
||||||
$response = wp_remote_get($this->api_url . '/com.atproto.identity.resolveHandle', array(
|
$response = wp_remote_get($this->api_url . '/com.atproto.identity.resolveHandle', array(
|
||||||
'query' => array(
|
'query' => array(
|
||||||
'handle' => $handle,
|
'handle' => $handle,
|
||||||
),
|
),
|
||||||
|
'timeout' => 30,
|
||||||
));
|
));
|
||||||
|
|
||||||
if (is_wp_error($response)) {
|
if (is_wp_error($response)) {
|
||||||
|
error_log('[Bluesky Connector] Resolve handle error: ' . $response->get_error_message());
|
||||||
return array('error' => $response->get_error_message());
|
return array('error' => $response->get_error_message());
|
||||||
}
|
}
|
||||||
|
|
||||||
return json_decode(wp_remote_retrieve_body($response), true);
|
return json_decode(wp_remote_retrieve_body($response), true);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function upload_blob($file_path, $mime_type) {
|
public function upload_blob($file_path_or_data, $mime_type) {
|
||||||
|
error_log('[Bluesky Connector] Starting blob upload');
|
||||||
|
error_log('[Bluesky Connector] Mime type: ' . $mime_type);
|
||||||
|
|
||||||
$headers = array(
|
$headers = array(
|
||||||
'Authorization' => 'Bearer ' . $this->api_key,
|
'Authorization' => 'Bearer ' . $this->api_key,
|
||||||
'Content-Type' => $mime_type,
|
'Content-Type' => $mime_type,
|
||||||
);
|
);
|
||||||
|
|
||||||
$file_contents = file_get_contents($file_path);
|
// Determine if input is a file path or raw data
|
||||||
if ($file_contents === false) {
|
if (is_string($file_path_or_data) && file_exists($file_path_or_data)) {
|
||||||
return array('error' => 'Failed to read file.');
|
error_log('[Bluesky Connector] Reading from file path: ' . $file_path_or_data);
|
||||||
|
$file_contents = file_get_contents($file_path_or_data);
|
||||||
|
if ($file_contents === false) {
|
||||||
|
error_log('[Bluesky Connector] Failed to read file');
|
||||||
|
return array('error' => 'Failed to read file');
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
error_log('[Bluesky Connector] Using provided data directly');
|
||||||
|
$file_contents = $file_path_or_data;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check content size
|
||||||
|
$content_size = strlen($file_contents);
|
||||||
|
error_log('[Bluesky Connector] Content size: ' . $content_size . ' bytes');
|
||||||
|
|
||||||
|
if ($content_size > 1000000) {
|
||||||
|
error_log('[Bluesky Connector] Content size exceeds 1MB limit');
|
||||||
|
return array('error' => 'File size exceeds 1MB limit');
|
||||||
}
|
}
|
||||||
|
|
||||||
$response = wp_remote_post($this->api_url . '/com.atproto.repo.uploadBlob', array(
|
$response = wp_remote_post($this->api_url . '/com.atproto.repo.uploadBlob', array(
|
||||||
'headers' => $headers,
|
'headers' => $headers,
|
||||||
'body' => $file_contents,
|
'body' => $file_contents,
|
||||||
|
'timeout' => 30,
|
||||||
));
|
));
|
||||||
|
|
||||||
if (is_wp_error($response)) {
|
if (is_wp_error($response)) {
|
||||||
|
error_log('[Bluesky Connector] Upload blob error: ' . $response->get_error_message());
|
||||||
return array('error' => $response->get_error_message());
|
return array('error' => $response->get_error_message());
|
||||||
}
|
}
|
||||||
|
|
||||||
return json_decode(wp_remote_retrieve_body($response), true);
|
$response_code = wp_remote_retrieve_response_code($response);
|
||||||
|
$response_body = wp_remote_retrieve_body($response);
|
||||||
|
error_log('[Bluesky Connector] Upload blob response code: ' . $response_code);
|
||||||
|
error_log('[Bluesky Connector] Upload blob response: ' . $response_body);
|
||||||
|
|
||||||
|
if ($response_code !== 200) {
|
||||||
|
return array('error' => 'Upload failed with status ' . $response_code);
|
||||||
|
}
|
||||||
|
|
||||||
|
return json_decode($response_body, true);
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -20,33 +20,74 @@ class Bluesky_Auth {
|
|||||||
// Ensure proper URL format
|
// Ensure proper URL format
|
||||||
$this->api_domain = rtrim($domain, '/') . '/';
|
$this->api_domain = rtrim($domain, '/') . '/';
|
||||||
|
|
||||||
// Debug logs
|
// Log configuration details if debugging is enabled
|
||||||
error_log('Bluesky Auth - Using domain setting: ' . get_option('bluesky_domain'));
|
$this->log_debug('Auth Configuration', array(
|
||||||
error_log('Bluesky Auth - Processed domain: ' . $this->api_domain);
|
'domain_setting' => get_option('bluesky_domain'),
|
||||||
error_log('Bluesky Auth - Identifier: ' . $this->identifier);
|
'processed_domain' => $this->api_domain,
|
||||||
|
'identifier' => $this->identifier
|
||||||
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
public function get_access_token() {
|
public function get_access_token() {
|
||||||
|
// First check if we have a valid access token
|
||||||
|
$access_token = get_option('bluesky_access_jwt');
|
||||||
|
$token_created = get_option('bluesky_token_created');
|
||||||
|
|
||||||
|
if (!empty($access_token) && $token_created > (time() - 7200)) {
|
||||||
|
$this->log_debug('Using existing valid token');
|
||||||
|
return $access_token;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If we have a refresh token, try to use it
|
||||||
if ($this->should_refresh_token()) {
|
if ($this->should_refresh_token()) {
|
||||||
return $this->refresh_access_token();
|
$refresh_result = $this->refresh_access_token();
|
||||||
|
if (!isset($refresh_result['error'])) {
|
||||||
|
return $refresh_result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// If we got here, we need to create a new session
|
||||||
|
if (empty($this->identifier) || empty($this->password)) {
|
||||||
|
// Try to get credentials from settings if not provided
|
||||||
|
$settings = get_option('bluesky_connector_settings', array());
|
||||||
|
if (!empty($settings['identifier'])) {
|
||||||
|
$this->identifier = $settings['identifier'];
|
||||||
|
}
|
||||||
|
if (!empty($settings['password'])) {
|
||||||
|
$this->password = $settings['password'];
|
||||||
|
}
|
||||||
|
|
||||||
|
// If still empty, try individual options
|
||||||
|
if (empty($this->identifier)) {
|
||||||
|
$this->identifier = get_option('bluesky_identifier');
|
||||||
|
}
|
||||||
|
if (empty($this->password)) {
|
||||||
|
$this->password = get_option('bluesky_password');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (empty($this->identifier) || empty($this->password)) {
|
||||||
|
$this->log_debug('Missing credentials', array(
|
||||||
|
'has_identifier' => !empty($this->identifier),
|
||||||
|
'has_password' => !empty($this->password)
|
||||||
|
));
|
||||||
|
return array('error' => 'Missing credentials - please check settings');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
$token_url = $this->api_domain . 'xrpc/com.atproto.server.createSession';
|
$token_url = $this->api_domain . 'xrpc/com.atproto.server.createSession';
|
||||||
|
|
||||||
// Debug log
|
$this->log_debug('Attempting connection', array('url' => $token_url));
|
||||||
error_log('Bluesky Auth - Attempting connection to: ' . $token_url);
|
|
||||||
|
|
||||||
$headers = array(
|
$headers = array(
|
||||||
'Content-Type' => 'application/json',
|
'Content-Type' => 'application/json',
|
||||||
);
|
);
|
||||||
|
|
||||||
$body = json_encode(array(
|
$body = wp_json_encode(array(
|
||||||
'identifier' => $this->identifier,
|
'identifier' => $this->identifier,
|
||||||
'password' => $this->password,
|
'password' => $this->password,
|
||||||
));
|
));
|
||||||
|
|
||||||
// Debug log
|
$this->log_debug('Request prepared', array('body' => str_replace($this->password, '[REDACTED]', $body)));
|
||||||
error_log('Bluesky Auth - Request body: ' . $body);
|
|
||||||
|
|
||||||
$wp_version = get_bloginfo('version');
|
$wp_version = get_bloginfo('version');
|
||||||
$user_agent = apply_filters('http_headers_useragent', 'WordPress/' . $wp_version . '; ' . get_bloginfo('url'));
|
$user_agent = apply_filters('http_headers_useragent', 'WordPress/' . $wp_version . '; ' . get_bloginfo('url'));
|
||||||
@ -55,38 +96,50 @@ class Bluesky_Auth {
|
|||||||
'headers' => $headers,
|
'headers' => $headers,
|
||||||
'user-agent' => "$user_agent; Bluesky Connector",
|
'user-agent' => "$user_agent; Bluesky Connector",
|
||||||
'body' => $body,
|
'body' => $body,
|
||||||
'timeout' => 30, // Increase timeout
|
'timeout' => 30,
|
||||||
));
|
));
|
||||||
|
|
||||||
if (is_wp_error($response)) {
|
if (is_wp_error($response)) {
|
||||||
$error_message = $response->get_error_message();
|
$error_message = $response->get_error_message();
|
||||||
error_log('Bluesky Auth Error: ' . $error_message);
|
$this->log_debug('Auth Error', array('error' => $error_message));
|
||||||
return array('error' => $error_message);
|
return array('error' => $error_message);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Debug response
|
// Log response details
|
||||||
$status_code = wp_remote_retrieve_response_code($response);
|
$status_code = wp_remote_retrieve_response_code($response);
|
||||||
$response_body = wp_remote_retrieve_body($response);
|
$response_body = wp_remote_retrieve_body($response);
|
||||||
error_log('Bluesky Auth - Response status: ' . $status_code);
|
$this->log_debug('Response received', array(
|
||||||
error_log('Bluesky Auth - Response body: ' . $response_body);
|
'status' => $status_code,
|
||||||
|
'body' => $response_body
|
||||||
|
));
|
||||||
|
|
||||||
$data = json_decode($response_body, true);
|
$data = json_decode($response_body, true);
|
||||||
|
|
||||||
if (!empty($data['accessJwt']) && !empty($data['refreshJwt']) && !empty($data['did'])) {
|
if (!empty($data['accessJwt']) && !empty($data['refreshJwt']) && !empty($data['did'])) {
|
||||||
|
// Save credentials in both locations
|
||||||
|
$settings = get_option('bluesky_connector_settings', array());
|
||||||
|
$settings['identifier'] = $this->identifier;
|
||||||
|
update_option('bluesky_connector_settings', $settings);
|
||||||
|
|
||||||
|
// Save all necessary tokens and details
|
||||||
|
update_option('bluesky_identifier', sanitize_text_field($this->identifier));
|
||||||
update_option('bluesky_access_jwt', sanitize_text_field($data['accessJwt']));
|
update_option('bluesky_access_jwt', sanitize_text_field($data['accessJwt']));
|
||||||
update_option('bluesky_refresh_jwt', sanitize_text_field($data['refreshJwt']));
|
update_option('bluesky_refresh_jwt', sanitize_text_field($data['refreshJwt']));
|
||||||
update_option('bluesky_did', sanitize_text_field($data['did']));
|
update_option('bluesky_did', sanitize_text_field($data['did']));
|
||||||
update_option('bluesky_token_created', time());
|
update_option('bluesky_token_created', time());
|
||||||
delete_option('bluesky_password'); // Don't store password
|
|
||||||
|
// Store password for reauth
|
||||||
|
update_option('bluesky_password', $this->password);
|
||||||
|
|
||||||
|
$this->log_debug('Authentication successful', array('did' => $data['did']));
|
||||||
return $data['accessJwt'];
|
return $data['accessJwt'];
|
||||||
}
|
}
|
||||||
|
|
||||||
// More detailed error reporting
|
|
||||||
$error_message = isset($data['error']) ? $data['error'] : 'Failed to get access token';
|
$error_message = isset($data['error']) ? $data['error'] : 'Failed to get access token';
|
||||||
if (isset($data['message'])) {
|
if (isset($data['message'])) {
|
||||||
$error_message .= ' - ' . $data['message'];
|
$error_message .= ' - ' . $data['message'];
|
||||||
}
|
}
|
||||||
error_log('Bluesky Auth - Error: ' . $error_message);
|
$this->log_debug('Auth Failed', array('error' => $error_message));
|
||||||
return array('error' => $error_message);
|
return array('error' => $error_message);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -94,7 +147,6 @@ class Bluesky_Auth {
|
|||||||
$token_created = get_option('bluesky_token_created');
|
$token_created = get_option('bluesky_token_created');
|
||||||
$refresh_token = get_option('bluesky_refresh_jwt');
|
$refresh_token = get_option('bluesky_refresh_jwt');
|
||||||
|
|
||||||
// Refresh if token is older than 23 hours or doesn't exist
|
|
||||||
return !empty($refresh_token) && ($token_created < (time() - 82800));
|
return !empty($refresh_token) && ($token_created < (time() - 82800));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -105,9 +157,7 @@ class Bluesky_Auth {
|
|||||||
}
|
}
|
||||||
|
|
||||||
$refresh_url = $this->api_domain . 'xrpc/com.atproto.server.refreshSession';
|
$refresh_url = $this->api_domain . 'xrpc/com.atproto.server.refreshSession';
|
||||||
|
$this->log_debug('Token refresh attempt', array('url' => $refresh_url));
|
||||||
// Debug log
|
|
||||||
error_log('Bluesky Auth - Attempting token refresh at: ' . $refresh_url);
|
|
||||||
|
|
||||||
$wp_version = get_bloginfo('version');
|
$wp_version = get_bloginfo('version');
|
||||||
$user_agent = apply_filters('http_headers_useragent', 'WordPress/' . $wp_version . '; ' . get_bloginfo('url'));
|
$user_agent = apply_filters('http_headers_useragent', 'WordPress/' . $wp_version . '; ' . get_bloginfo('url'));
|
||||||
@ -123,22 +173,18 @@ class Bluesky_Auth {
|
|||||||
|
|
||||||
if (is_wp_error($response)) {
|
if (is_wp_error($response)) {
|
||||||
$error_message = $response->get_error_message();
|
$error_message = $response->get_error_message();
|
||||||
error_log('Bluesky Token Refresh Error: ' . $error_message);
|
$this->log_debug('Token Refresh Error', array('error' => $error_message));
|
||||||
return array('error' => $error_message);
|
return array('error' => $error_message);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Debug response
|
$data = json_decode(wp_remote_retrieve_body($response), true);
|
||||||
$status_code = wp_remote_retrieve_response_code($response);
|
|
||||||
$response_body = wp_remote_retrieve_body($response);
|
|
||||||
error_log('Bluesky Auth Refresh - Response status: ' . $status_code);
|
|
||||||
error_log('Bluesky Auth Refresh - Response body: ' . $response_body);
|
|
||||||
|
|
||||||
$data = json_decode($response_body, true);
|
|
||||||
|
|
||||||
if (!empty($data['accessJwt']) && !empty($data['refreshJwt'])) {
|
if (!empty($data['accessJwt']) && !empty($data['refreshJwt'])) {
|
||||||
update_option('bluesky_access_jwt', sanitize_text_field($data['accessJwt']));
|
update_option('bluesky_access_jwt', sanitize_text_field($data['accessJwt']));
|
||||||
update_option('bluesky_refresh_jwt', sanitize_text_field($data['refreshJwt']));
|
update_option('bluesky_refresh_jwt', sanitize_text_field($data['refreshJwt']));
|
||||||
update_option('bluesky_token_created', time());
|
update_option('bluesky_token_created', time());
|
||||||
|
|
||||||
|
$this->log_debug('Token refresh successful');
|
||||||
return $data['accessJwt'];
|
return $data['accessJwt'];
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -146,7 +192,23 @@ class Bluesky_Auth {
|
|||||||
if (isset($data['message'])) {
|
if (isset($data['message'])) {
|
||||||
$error_message .= ' - ' . $data['message'];
|
$error_message .= ' - ' . $data['message'];
|
||||||
}
|
}
|
||||||
error_log('Bluesky Auth Refresh - Error: ' . $error_message);
|
$this->log_debug('Token Refresh Failed', array('error' => $error_message));
|
||||||
return array('error' => $error_message);
|
return array('error' => $error_message);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private function log_debug($message, $data = array()) {
|
||||||
|
if (defined('WP_DEBUG') && WP_DEBUG) {
|
||||||
|
$log_message = sprintf(
|
||||||
|
'[Bluesky Connector] %s | Data: %s',
|
||||||
|
$message,
|
||||||
|
wp_json_encode($data, JSON_PRETTY_PRINT)
|
||||||
|
);
|
||||||
|
|
||||||
|
if (defined('WP_DEBUG_LOG') && WP_DEBUG_LOG) {
|
||||||
|
error_log($log_message);
|
||||||
|
}
|
||||||
|
|
||||||
|
do_action('bluesky_connector_debug', $message, $data);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
@ -1,243 +1,457 @@
|
|||||||
<?php
|
<?php
|
||||||
class Post_Formatter {
|
class Post_Formatter
|
||||||
|
{
|
||||||
private $api;
|
private $api;
|
||||||
private $max_length = 300;
|
private $max_length = 300;
|
||||||
|
|
||||||
public function __construct($access_token, $did) {
|
private function diagnose_thumbnail_support($post_id) {
|
||||||
$this->api = new Bluesky_API($access_token, $did);
|
error_log('[Bluesky Connector] Starting thumbnail support diagnosis');
|
||||||
}
|
|
||||||
|
|
||||||
public function format_and_post($post) {
|
// Check WordPress version
|
||||||
$content = $this->get_formatted_content($post);
|
error_log('[Bluesky Connector] WordPress Version: ' . get_bloginfo('version'));
|
||||||
|
|
||||||
$post_data = array(
|
// Check if theme supports post thumbnails
|
||||||
'$type' => 'app.bsky.feed.post',
|
$theme_support = current_theme_supports('post-thumbnails');
|
||||||
'text' => $content,
|
error_log('[Bluesky Connector] Theme supports post thumbnails: ' . ($theme_support ? 'yes' : 'no'));
|
||||||
'createdAt' => gmdate('c', strtotime($post->post_date_gmt)),
|
|
||||||
'langs' => array('en')
|
|
||||||
);
|
|
||||||
|
|
||||||
// Add facets for the URL
|
// If theme doesn't support thumbnails, check if we can add support
|
||||||
$post_data['facets'] = $this->parse_facets($post);
|
if (!$theme_support) {
|
||||||
|
error_log('[Bluesky Connector] Attempting to add post thumbnail support');
|
||||||
|
add_theme_support('post-thumbnails');
|
||||||
|
$theme_support = current_theme_supports('post-thumbnails');
|
||||||
|
error_log('[Bluesky Connector] Post thumbnail support after attempt: ' . ($theme_support ? 'yes' : 'no'));
|
||||||
|
}
|
||||||
|
|
||||||
// Handle image embed separately from text content
|
// Check post type support
|
||||||
if (has_post_thumbnail($post->ID)) {
|
$post_type = get_post_type($post_id);
|
||||||
$image_data = $this->handle_featured_image($post->ID);
|
$post_type_support = post_type_supports($post_type, 'thumbnail');
|
||||||
if (!empty($image_data) && !isset($image_data['error'])) {
|
error_log('[Bluesky Connector] Post type ' . $post_type . ' supports thumbnails: ' . ($post_type_support ? 'yes' : 'no'));
|
||||||
$post_data['embed'] = $image_data;
|
|
||||||
|
// Check post meta directly
|
||||||
|
$post_meta = get_post_meta($post_id);
|
||||||
|
error_log('[Bluesky Connector] All post meta for debugging: ' . print_r($post_meta, true));
|
||||||
|
|
||||||
|
// Check attachment status
|
||||||
|
$thumbnail_id = get_post_thumbnail_id($post_id);
|
||||||
|
if ($thumbnail_id) {
|
||||||
|
$attachment = get_post($thumbnail_id);
|
||||||
|
if ($attachment) {
|
||||||
|
error_log('[Bluesky Connector] Attachment details: ' . print_r([
|
||||||
|
'ID' => $attachment->ID,
|
||||||
|
'post_type' => $attachment->post_type,
|
||||||
|
'status' => $attachment->post_status,
|
||||||
|
'mime_type' => $attachment->post_mime_type,
|
||||||
|
'attached_file' => get_attached_file($thumbnail_id),
|
||||||
|
'exists' => file_exists(get_attached_file($thumbnail_id))
|
||||||
|
], true));
|
||||||
|
} else {
|
||||||
|
error_log('[Bluesky Connector] Attachment post not found for ID: ' . $thumbnail_id);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return $this->api->create_post($post_data);
|
return $theme_support && $post_type_support;
|
||||||
}
|
}
|
||||||
|
|
||||||
private function get_formatted_content($post) {
|
public function __construct($access_token, $did)
|
||||||
$format = get_option('bluesky_post_format', 'image-title-excerpt-link');
|
{
|
||||||
$include_title = get_option('bluesky_include_title', true);
|
error_log('[Bluesky Connector] Initializing Post_Formatter with DID: ' . $did);
|
||||||
|
$this->api = new Bluesky_API($access_token, $did);
|
||||||
// Get individual components
|
|
||||||
$title = $include_title ? $post->post_title : '';
|
|
||||||
$excerpt = $this->get_excerpt($post);
|
|
||||||
$url = wp_get_shortlink($post->ID);
|
|
||||||
|
|
||||||
// Start building content with explicit line breaks
|
|
||||||
$content = '';
|
|
||||||
|
|
||||||
// Add title with line break if it exists
|
|
||||||
if (!empty($title)) {
|
|
||||||
$content .= $title . "\n\n";
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add excerpt if it exists
|
|
||||||
if (!empty($excerpt)) {
|
|
||||||
$content .= $excerpt . "\n\n"; // Add double line break after excerpt
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add URL on its own line
|
|
||||||
if (!empty($url)) {
|
|
||||||
$content .= $url; // URL starts on new line due to previous \n\n
|
|
||||||
}
|
|
||||||
|
|
||||||
return $content;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private function get_excerpt($post) {
|
public function format_and_post($post, $formatted_content = null)
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
error_log('[Bluesky Connector] Starting format_and_post for post ' . $post->ID);
|
||||||
|
|
||||||
|
// Use provided content if available, otherwise format it
|
||||||
|
$content = $formatted_content ?: $this->get_formatted_content($post);
|
||||||
|
error_log('[Bluesky Connector] Formatted content: ' . $content);
|
||||||
|
|
||||||
|
$post_data = array(
|
||||||
|
'$type' => 'app.bsky.feed.post',
|
||||||
|
'text' => $content,
|
||||||
|
'createdAt' => gmdate('c', strtotime($post->post_date_gmt)),
|
||||||
|
'langs' => array('en')
|
||||||
|
);
|
||||||
|
|
||||||
|
// Add facets for the URL
|
||||||
|
$post_data['facets'] = $this->parse_facets($post);
|
||||||
|
error_log('[Bluesky Connector] Added facets: ' . wp_json_encode($post_data['facets']));
|
||||||
|
|
||||||
|
// Debug thumbnail support
|
||||||
|
error_log('[Bluesky Connector] Checking for featured image...');
|
||||||
|
error_log('[Bluesky Connector] Post type: ' . $post->post_type);
|
||||||
|
error_log('[Bluesky Connector] has_post_thumbnail result: ' . (has_post_thumbnail($post->ID) ? 'true' : 'false'));
|
||||||
|
error_log('[Bluesky Connector] Theme supports thumbnails: ' . (current_theme_supports('post-thumbnails') ? 'true' : 'false'));
|
||||||
|
error_log('[Bluesky Connector] Post thumbnail meta check:');
|
||||||
|
error_log('[Bluesky Connector] _thumbnail_id: ' . get_post_meta($post->ID, '_thumbnail_id', true));
|
||||||
|
error_log('[Bluesky Connector] Post status: ' . get_post_status($post->ID));
|
||||||
|
|
||||||
|
// Handle image embed
|
||||||
|
if (has_post_thumbnail($post->ID)) {
|
||||||
|
error_log('[Bluesky Connector] Processing featured image for post ' . $post->ID);
|
||||||
|
$image_data = $this->handle_featured_image($post->ID);
|
||||||
|
if (!empty($image_data) && !isset($image_data['error'])) {
|
||||||
|
$post_data['embed'] = $image_data;
|
||||||
|
error_log('[Bluesky Connector] Image data added to post: ' . wp_json_encode($image_data));
|
||||||
|
} else {
|
||||||
|
error_log('[Bluesky Connector] Image processing error: ' . ($image_data['error'] ?? 'Unknown error'));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
error_log('[Bluesky Connector] Sending post data to API: ' . wp_json_encode($post_data));
|
||||||
|
$response = $this->api->create_post($post_data);
|
||||||
|
error_log('[Bluesky Connector] API Response: ' . wp_json_encode($response));
|
||||||
|
|
||||||
|
return $response;
|
||||||
|
|
||||||
|
} catch (Exception $e) {
|
||||||
|
error_log('[Bluesky Connector] Error in format_and_post: ' . $e->getMessage());
|
||||||
|
return array('error' => $e->getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private function get_formatted_content($post)
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
error_log('[Bluesky Connector] Starting content formatting for post ' . $post->ID);
|
||||||
|
|
||||||
|
$format = get_option('bluesky_post_format', 'image-title-excerpt-link');
|
||||||
|
$include_title = get_option('bluesky_include_title', true);
|
||||||
|
|
||||||
|
error_log('[Bluesky Connector] Using format: ' . $format . ', Include title: ' . ($include_title ? 'yes' : 'no'));
|
||||||
|
|
||||||
|
// Get individual components
|
||||||
|
$title = $include_title ? $post->post_title : '';
|
||||||
|
$excerpt = $this->get_excerpt($post);
|
||||||
|
$url = wp_get_shortlink($post->ID);
|
||||||
|
|
||||||
|
error_log('[Bluesky Connector] Content components:');
|
||||||
|
error_log('[Bluesky Connector] - Title: ' . $title);
|
||||||
|
error_log('[Bluesky Connector] - Excerpt length: ' . strlen($excerpt));
|
||||||
|
error_log('[Bluesky Connector] - URL: ' . $url);
|
||||||
|
|
||||||
|
// Start building content
|
||||||
|
$content = '';
|
||||||
|
|
||||||
|
// Add title with line break if it exists
|
||||||
|
if (!empty($title)) {
|
||||||
|
$content .= $title . "\n\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add excerpt if it exists
|
||||||
|
if (!empty($excerpt)) {
|
||||||
|
$content .= $excerpt . "\n\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add URL on its own line
|
||||||
|
if (!empty($url)) {
|
||||||
|
$content .= "Continue Reading: " . $url;
|
||||||
|
}
|
||||||
|
|
||||||
|
error_log('[Bluesky Connector] Final formatted content: ' . $content);
|
||||||
|
return $content;
|
||||||
|
|
||||||
|
} catch (Exception $e) {
|
||||||
|
error_log('[Bluesky Connector] Error in get_formatted_content: ' . $e->getMessage());
|
||||||
|
throw $e;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private function get_excerpt($post)
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
error_log('[Bluesky Connector] Getting excerpt for post ' . $post->ID);
|
||||||
|
|
||||||
$text = wp_strip_all_tags($post->post_excerpt);
|
$text = wp_strip_all_tags($post->post_excerpt);
|
||||||
if (empty($text)) {
|
if (empty($text)) {
|
||||||
$text = wp_strip_all_tags($post->post_content);
|
$text = wp_strip_all_tags($post->post_content);
|
||||||
|
error_log('[Bluesky Connector] Using post content for excerpt (no excerpt found)');
|
||||||
}
|
}
|
||||||
|
|
||||||
$url = wp_get_shortlink($post->ID);
|
$url = wp_get_shortlink($post->ID);
|
||||||
$include_title = get_option('bluesky_include_title', true);
|
$include_title = get_option('bluesky_include_title', true);
|
||||||
|
|
||||||
// Calculate available length accounting for spacing and new lines
|
// Calculate available length
|
||||||
$available_length = $this->max_length;
|
$available_length = $this->max_length;
|
||||||
$available_length -= strlen($url);
|
$available_length -= strlen($url);
|
||||||
$available_length -= 2; // Account for \n\n after excerpt
|
$available_length -= strlen("Continue Reading: "); // Account for the prefix
|
||||||
|
$available_length -= 4; // Account for \n\n before and after the URL
|
||||||
|
|
||||||
if ($include_title) {
|
if ($include_title) {
|
||||||
$available_length -= strlen($post->post_title);
|
$available_length -= strlen($post->post_title);
|
||||||
$available_length -= 2; // Account for \n\n after title
|
$available_length -= 2; // Account for \n\n after title
|
||||||
}
|
}
|
||||||
|
|
||||||
|
error_log('[Bluesky Connector] Available length for excerpt: ' . $available_length);
|
||||||
|
|
||||||
if (mb_strlen($text) > $available_length) {
|
if (mb_strlen($text) > $available_length) {
|
||||||
$text = mb_substr($text, 0, $available_length - 3) . '...';
|
$text = mb_substr($text, 0, $available_length - 3) . '...';
|
||||||
|
error_log('[Bluesky Connector] Excerpt truncated to fit length limit');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
error_log('[Bluesky Connector] Final excerpt length: ' . mb_strlen($text));
|
||||||
return $text;
|
return $text;
|
||||||
|
|
||||||
|
} catch (Exception $e) {
|
||||||
|
error_log('[Bluesky Connector] Error in get_excerpt: ' . $e->getMessage());
|
||||||
|
throw $e;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private function parse_facets($post) {
|
private function parse_facets($post)
|
||||||
$facets = array();
|
{
|
||||||
$content = $this->get_formatted_content($post);
|
try {
|
||||||
|
error_log('[Bluesky Connector] Parsing facets for post ' . $post->ID);
|
||||||
|
|
||||||
// Add link facet for the post URL
|
$facets = array();
|
||||||
$url = wp_get_shortlink($post->ID);
|
$content = $this->get_formatted_content($post);
|
||||||
$text_bytes = mb_convert_encoding($content, 'UTF-8');
|
$url = wp_get_shortlink($post->ID);
|
||||||
$url_position = mb_strrpos($text_bytes, $url);
|
$text_bytes = mb_convert_encoding($content, 'UTF-8');
|
||||||
|
$url_position = mb_strrpos($text_bytes, $url);
|
||||||
|
|
||||||
if ($url_position !== false) {
|
if ($url_position !== false) {
|
||||||
$facets[] = array(
|
$facets[] = array(
|
||||||
'index' => array(
|
'index' => array(
|
||||||
'byteStart' => $url_position,
|
'byteStart' => $url_position,
|
||||||
'byteEnd' => $url_position + strlen($url),
|
'byteEnd' => $url_position + strlen($url),
|
||||||
),
|
|
||||||
'features' => array(
|
|
||||||
array(
|
|
||||||
'$type' => 'app.bsky.richtext.facet#link',
|
|
||||||
'uri' => $url,
|
|
||||||
),
|
),
|
||||||
),
|
'features' => array(
|
||||||
);
|
array(
|
||||||
}
|
'$type' => 'app.bsky.richtext.facet#link',
|
||||||
|
'uri' => $url,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
error_log('[Bluesky Connector] Added URL facet for: ' . $url);
|
||||||
|
}
|
||||||
|
|
||||||
return $facets;
|
error_log('[Bluesky Connector] Generated facets: ' . wp_json_encode($facets));
|
||||||
|
return $facets;
|
||||||
|
|
||||||
|
} catch (Exception $e) {
|
||||||
|
error_log('[Bluesky Connector] Error in parse_facets: ' . $e->getMessage());
|
||||||
|
throw $e;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private function handle_featured_image($post_id) {
|
private function handle_featured_image($post_id) {
|
||||||
$image_id = get_post_thumbnail_id($post_id);
|
try {
|
||||||
$image_path = get_attached_file($image_id);
|
error_log('[Bluesky Connector] Starting featured image process for post ' . $post_id);
|
||||||
|
|
||||||
if (!$image_path) {
|
// Run diagnostics first
|
||||||
return array('error' => 'Image file not found');
|
$thumbnail_support = $this->diagnose_thumbnail_support($post_id);
|
||||||
}
|
if (!$thumbnail_support) {
|
||||||
|
error_log('[Bluesky Connector] Thumbnail support is not properly configured');
|
||||||
$mime_type = get_post_mime_type($image_id);
|
|
||||||
|
|
||||||
// Get image data
|
|
||||||
$image_data = file_get_contents($image_path);
|
|
||||||
if ($image_data === false) {
|
|
||||||
return array('error' => 'Failed to read image file');
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check file size (1MB limit for Bluesky)
|
|
||||||
if (strlen($image_data) > 1000000) {
|
|
||||||
// If image is too large, attempt to resize it
|
|
||||||
$resized = $this->resize_image($image_path);
|
|
||||||
if ($resized) {
|
|
||||||
$image_data = file_get_contents($resized);
|
|
||||||
unlink($resized); // Clean up temporary file
|
|
||||||
} else {
|
|
||||||
return array('error' => 'Image file size exceeds 1MB limit and resize failed');
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
// Upload image blob
|
// Get image ID with multiple fallbacks
|
||||||
$response = $this->api->upload_blob($image_path, $mime_type);
|
$image_id = get_post_thumbnail_id($post_id);
|
||||||
|
error_log('[Bluesky Connector] Initial image ID from get_post_thumbnail_id: ' . $image_id);
|
||||||
|
|
||||||
if (isset($response['error'])) {
|
if (!$image_id) {
|
||||||
return $response;
|
// Try direct meta approach
|
||||||
}
|
$image_id = get_post_meta($post_id, '_thumbnail_id', true);
|
||||||
|
error_log('[Bluesky Connector] Image ID from direct meta: ' . $image_id);
|
||||||
|
|
||||||
// Get the alt text
|
// If still no image, check for fallback
|
||||||
$alt_text = get_post_meta($image_id, '_wp_attachment_image_alt', true) ?: '';
|
if (!$image_id) {
|
||||||
|
$image_id = get_option('bluesky_fallback_image');
|
||||||
|
error_log('[Bluesky Connector] Using fallback image ID: ' . $image_id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return array(
|
if (!$image_id) {
|
||||||
'$type' => 'app.bsky.embed.images',
|
return array('error' => 'No valid image ID found');
|
||||||
'images' => array(
|
}
|
||||||
array(
|
|
||||||
'alt' => $alt_text,
|
// Get the image file path
|
||||||
'image' => $response['blob'],
|
$image_path = get_attached_file($image_id);
|
||||||
|
error_log('[Bluesky Connector] Image path: ' . ($image_path ?: 'not found'));
|
||||||
|
|
||||||
|
// Verify file exists and is accessible
|
||||||
|
if (!$image_path || !file_exists($image_path)) {
|
||||||
|
$upload_dir = wp_upload_dir();
|
||||||
|
error_log('[Bluesky Connector] Upload directory information: ' . print_r($upload_dir, true));
|
||||||
|
return array('error' => 'Image file not found or inaccessible');
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check file permissions
|
||||||
|
error_log('[Bluesky Connector] File permissions: ' . decoct(fileperms($image_path) & 0777));
|
||||||
|
|
||||||
|
// Verify mime type
|
||||||
|
$mime_type = get_post_mime_type($image_id);
|
||||||
|
error_log('[Bluesky Connector] Mime type: ' . $mime_type);
|
||||||
|
|
||||||
|
// Validate mime type
|
||||||
|
$allowed_types = array('image/jpeg', 'image/png', 'image/gif');
|
||||||
|
if (!in_array($mime_type, $allowed_types)) {
|
||||||
|
return array('error' => 'Unsupported image type: ' . $mime_type);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get image data
|
||||||
|
$image_data = file_get_contents($image_path);
|
||||||
|
if ($image_data === false) {
|
||||||
|
return array('error' => 'Failed to read image file');
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check and handle file size
|
||||||
|
$size = strlen($image_data);
|
||||||
|
error_log('[Bluesky Connector] Original image size: ' . $size . ' bytes');
|
||||||
|
|
||||||
|
if ($size > 1000000) {
|
||||||
|
error_log('[Bluesky Connector] Image exceeds size limit, attempting resize');
|
||||||
|
$resized = $this->resize_image($image_path);
|
||||||
|
|
||||||
|
if ($resized) {
|
||||||
|
error_log('[Bluesky Connector] Image resized successfully');
|
||||||
|
$image_path = $resized;
|
||||||
|
|
||||||
|
// Verify new size
|
||||||
|
$new_size = filesize($resized);
|
||||||
|
error_log('[Bluesky Connector] New image size: ' . $new_size . ' bytes');
|
||||||
|
|
||||||
|
if ($new_size > 1000000) {
|
||||||
|
error_log('[Bluesky Connector] Resized image still too large');
|
||||||
|
return array('error' => 'Unable to reduce image size below 1MB');
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
error_log('[Bluesky Connector] Image resize failed');
|
||||||
|
return array('error' => 'Image resize failed');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Upload to Bluesky
|
||||||
|
error_log('[Bluesky Connector] Uploading image to Bluesky');
|
||||||
|
$response = $this->api->upload_blob($image_path, $mime_type);
|
||||||
|
|
||||||
|
if (isset($response['error'])) {
|
||||||
|
error_log('[Bluesky Connector] Upload failed: ' . $response['error']);
|
||||||
|
return $response;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Clean up temporary file if it exists
|
||||||
|
if (isset($resized) && file_exists($resized)) {
|
||||||
|
unlink($resized);
|
||||||
|
error_log('[Bluesky Connector] Cleaned up temporary resized file');
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get alt text
|
||||||
|
$alt_text = get_post_meta($image_id, '_wp_attachment_image_alt', true) ?: '';
|
||||||
|
error_log('[Bluesky Connector] Using alt text: ' . $alt_text);
|
||||||
|
|
||||||
|
// Prepare final image embed
|
||||||
|
$image_embed = array(
|
||||||
|
'$type' => 'app.bsky.embed.images',
|
||||||
|
'images' => array(
|
||||||
|
array(
|
||||||
|
'alt' => $alt_text,
|
||||||
|
'image' => $response['blob'],
|
||||||
|
),
|
||||||
),
|
),
|
||||||
),
|
);
|
||||||
);
|
|
||||||
|
error_log('[Bluesky Connector] Image embed prepared successfully');
|
||||||
|
return $image_embed;
|
||||||
|
|
||||||
|
} catch (Exception $e) {
|
||||||
|
error_log('[Bluesky Connector] Error in handle_featured_image: ' . $e->getMessage());
|
||||||
|
error_log('[Bluesky Connector] Stack trace: ' . $e->getTraceAsString());
|
||||||
|
return array('error' => $e->getMessage());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private function resize_image($image_path) {
|
private function resize_image($image_path)
|
||||||
// Only proceed if GD is available
|
{
|
||||||
if (!function_exists('imagecreatefrompng')) {
|
try {
|
||||||
return false;
|
error_log('[Bluesky Connector] Starting image resize for: ' . $image_path);
|
||||||
}
|
|
||||||
|
|
||||||
$mime_type = mime_content_type($image_path);
|
if (!function_exists('imagecreatefrompng')) {
|
||||||
list($width, $height) = getimagesize($image_path);
|
error_log('[Bluesky Connector] GD library not available');
|
||||||
|
|
||||||
// Calculate new dimensions while maintaining aspect ratio
|
|
||||||
$max_dimension = 1000; // Reasonable size that should result in < 1MB file
|
|
||||||
if ($width > $height) {
|
|
||||||
$new_width = $max_dimension;
|
|
||||||
$new_height = floor($height * ($max_dimension / $width));
|
|
||||||
} else {
|
|
||||||
$new_height = $max_dimension;
|
|
||||||
$new_width = floor($width * ($max_dimension / $height));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create new image
|
|
||||||
$new_image = imagecreatetruecolor($new_width, $new_height);
|
|
||||||
|
|
||||||
// Handle different image types
|
|
||||||
switch ($mime_type) {
|
|
||||||
case 'image/jpeg':
|
|
||||||
$source = imagecreatefromjpeg($image_path);
|
|
||||||
break;
|
|
||||||
case 'image/png':
|
|
||||||
$source = imagecreatefrompng($image_path);
|
|
||||||
// Preserve transparency
|
|
||||||
imagealphablending($new_image, false);
|
|
||||||
imagesavealpha($new_image, true);
|
|
||||||
break;
|
|
||||||
case 'image/gif':
|
|
||||||
$source = imagecreatefromgif($image_path);
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!$source) {
|
$mime_type = mime_content_type($image_path);
|
||||||
|
list($width, $height) = getimagesize($image_path);
|
||||||
|
error_log('[Bluesky Connector] Original dimensions: ' . $width . 'x' . $height);
|
||||||
|
|
||||||
|
// Calculate new dimensions
|
||||||
|
$max_dimension = 1000;
|
||||||
|
if ($width > $height) {
|
||||||
|
$new_width = $max_dimension;
|
||||||
|
$new_height = floor($height * ($max_dimension / $width));
|
||||||
|
} else {
|
||||||
|
$new_height = $max_dimension;
|
||||||
|
$new_width = floor($width * ($max_dimension / $height));
|
||||||
|
}
|
||||||
|
|
||||||
|
error_log('[Bluesky Connector] New dimensions: ' . $new_width . 'x' . $new_height);
|
||||||
|
|
||||||
|
$new_image = imagecreatetruecolor($new_width, $new_height);
|
||||||
|
|
||||||
|
switch ($mime_type) {
|
||||||
|
case 'image/jpeg':
|
||||||
|
$source = imagecreatefromjpeg($image_path);
|
||||||
|
break;
|
||||||
|
case 'image/png':
|
||||||
|
$source = imagecreatefrompng($image_path);
|
||||||
|
imagealphablending($new_image, false);
|
||||||
|
imagesavealpha($new_image, true);
|
||||||
|
break;
|
||||||
|
case 'image/gif':
|
||||||
|
$source = imagecreatefromgif($image_path);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
error_log('[Bluesky Connector] Unsupported image type: ' . $mime_type);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!$source) {
|
||||||
|
error_log('[Bluesky Connector] Failed to create image resource');
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
imagecopyresampled(
|
||||||
|
$new_image,
|
||||||
|
$source,
|
||||||
|
0, 0, 0, 0,
|
||||||
|
$new_width,
|
||||||
|
$new_height,
|
||||||
|
$width,
|
||||||
|
$height
|
||||||
|
);
|
||||||
|
|
||||||
|
// Use PHP's tempnam
|
||||||
|
$temp_file = tempnam(sys_get_temp_dir(), 'bluesky_img_');
|
||||||
|
error_log('[Bluesky Connector] Created temp file: ' . $temp_file);
|
||||||
|
|
||||||
|
$success = false;
|
||||||
|
switch ($mime_type) {
|
||||||
|
case 'image/jpeg':
|
||||||
|
$success = imagejpeg($new_image, $temp_file, 85);
|
||||||
|
break;
|
||||||
|
case 'image/png':
|
||||||
|
$success = imagepng($new_image, $temp_file, 8);
|
||||||
|
break;
|
||||||
|
case 'image/gif':
|
||||||
|
$success = imagegif($new_image, $temp_file);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
imagedestroy($source);
|
||||||
|
imagedestroy($new_image);
|
||||||
|
|
||||||
|
if ($success) {
|
||||||
|
error_log('[Bluesky Connector] Image resized successfully');
|
||||||
|
return $temp_file;
|
||||||
|
} else {
|
||||||
|
error_log('[Bluesky Connector] Failed to save resized image');
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
} catch (Exception $e) {
|
||||||
|
error_log('[Bluesky Connector] Error in resize_image: ' . $e->getMessage());
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Resize
|
|
||||||
imagecopyresampled(
|
|
||||||
$new_image,
|
|
||||||
$source,
|
|
||||||
0, 0, 0, 0,
|
|
||||||
$new_width,
|
|
||||||
$new_height,
|
|
||||||
$width,
|
|
||||||
$height
|
|
||||||
);
|
|
||||||
|
|
||||||
// Create temporary file
|
|
||||||
$temp_file = tempnam(sys_get_temp_dir(), 'bluesky_img_');
|
|
||||||
|
|
||||||
// Save resized image
|
|
||||||
switch ($mime_type) {
|
|
||||||
case 'image/jpeg':
|
|
||||||
imagejpeg($new_image, $temp_file, 85);
|
|
||||||
break;
|
|
||||||
case 'image/png':
|
|
||||||
imagepng($new_image, $temp_file, 8);
|
|
||||||
break;
|
|
||||||
case 'image/gif':
|
|
||||||
imagegif($new_image, $temp_file);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Clean up
|
|
||||||
imagedestroy($source);
|
|
||||||
imagedestroy($new_image);
|
|
||||||
|
|
||||||
return $temp_file;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -1,58 +1,64 @@
|
|||||||
<div class="bluesky-post-status">
|
<div class="bluesky-post-status">
|
||||||
<?php wp_nonce_field('bluesky_post_meta_box', 'bluesky_post_meta_box_nonce'); ?>
|
<?php wp_nonce_field('bluesky_post_meta_box', 'bluesky_post_meta_box_nonce'); ?>
|
||||||
|
<?php if (!empty($status)): ?>
|
||||||
<?php if (!empty($status)) : ?>
|
|
||||||
<p>
|
<p>
|
||||||
<strong><?php _e('Status:', 'bluesky-connector'); ?></strong>
|
<strong><?php esc_html_e('Status:', 'bluesky-connctor'); ?></strong>
|
||||||
<?php
|
<?php
|
||||||
switch ($status) {
|
switch ($status) {
|
||||||
case 'success':
|
case 'success':
|
||||||
echo '<span class="bluesky-status-success">' . esc_html__('Posted', 'bluesky-connector') . '</span>';
|
echo '<span class="bluesky-status-success">' . esc_html__('Posted', 'bluesky-connctor') . '</span>';
|
||||||
break;
|
break;
|
||||||
case 'error':
|
case 'error':
|
||||||
echo '<span class="bluesky-status-error">' . esc_html__('Error', 'bluesky-connector') . '</span>';
|
echo '<span class="bluesky-status-error">' . esc_html__('Error', 'bluesky-connctor') . '</span>';
|
||||||
break;
|
break;
|
||||||
case 'queued':
|
case 'pending':
|
||||||
echo '<span class="bluesky-status-pending">' . esc_html__('Queued', 'bluesky-connector') . '</span>';
|
echo '<span class="bluesky-status-pending">' . esc_html__('Publishing...', 'bluesky-connctor') . '</span>';
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
echo '<span class="bluesky-status-unknown">' . esc_html__('Unknown', 'bluesky-connector') . '</span>';
|
echo '<span class="bluesky-status-unknown">' . esc_html__('Not Posted', 'bluesky-connctor') . '</span>';
|
||||||
}
|
}
|
||||||
?>
|
?>
|
||||||
</p>
|
</p>
|
||||||
|
<?php if ($posted_date): ?>
|
||||||
<?php if ($posted_date) : ?>
|
|
||||||
<p>
|
<p>
|
||||||
<strong><?php _e('Posted:', 'bluesky-connector'); ?></strong>
|
<strong><?php esc_html_e('Posted:', 'bluesky-connctor'); ?></strong>
|
||||||
<?php echo esc_html(date_i18n(get_option('date_format') . ' ' . get_option('time_format'), strtotime($posted_date))); ?>
|
<?php echo esc_html(date_i18n(get_option('date_format') . ' ' . get_option('time_format'), strtotime($posted_date))); ?>
|
||||||
</p>
|
</p>
|
||||||
<?php endif; ?>
|
<?php endif; ?>
|
||||||
|
<?php if ($post_id): ?>
|
||||||
<?php if ($post_id) : ?>
|
|
||||||
<p>
|
<p>
|
||||||
<strong><?php _e('Bluesky Post ID:', 'bluesky-connector'); ?></strong>
|
<strong><?php esc_html_e('Bluesky Post:', 'bluesky-connctor'); ?></strong>
|
||||||
<code><?php echo esc_html($post_id); ?></code>
|
<a href="https://bsky.app/profile/<?php echo esc_attr(get_option('bluesky_identifier')); ?>/post/<?php echo esc_attr($post_id); ?>"
|
||||||
|
target="_blank" rel="noopener noreferrer">
|
||||||
|
<?php esc_html_e('View Post', 'bluesky-connctor'); ?> ↗
|
||||||
|
</a>
|
||||||
</p>
|
</p>
|
||||||
<?php endif; ?>
|
<?php endif; ?>
|
||||||
|
<?php if ($error): ?>
|
||||||
<?php if ($error) : ?>
|
|
||||||
<p class="bluesky-error-message">
|
<p class="bluesky-error-message">
|
||||||
<strong><?php _e('Error:', 'bluesky-connector'); ?></strong>
|
<strong><?php esc_html_e('Error:', 'bluesky-connctor'); ?></strong>
|
||||||
<?php echo esc_html($error); ?>
|
<?php echo esc_html($error); ?>
|
||||||
</p>
|
</p>
|
||||||
<?php endif; ?>
|
<?php endif; ?>
|
||||||
|
|
||||||
<div class="bluesky-actions">
|
<div class="bluesky-actions">
|
||||||
<?php if ($status === 'error' || empty($post_id)) : ?>
|
<?php if ($status === 'error' || !$post_id): ?>
|
||||||
<button type="button" class="button bluesky-retry-post" data-post-id="<?php echo esc_attr($post->ID); ?>">
|
<button type="button" class="button bluesky-retry-post" data-post-id="<?php echo esc_attr($post->ID); ?>"
|
||||||
<?php _e('Retry Post', 'bluesky-connector'); ?>
|
data-nonce="<?php echo esc_attr(wp_create_nonce('bluesky_retry_post')); ?>">
|
||||||
|
<?php esc_html_e('Retry Post', 'bluesky-connctor'); ?>
|
||||||
|
</button>
|
||||||
|
<?php else: ?>
|
||||||
|
<button type="button" class="button bluesky-repost" data-post-id="<?php echo esc_attr($post->ID); ?>"
|
||||||
|
data-nonce="<?php echo esc_attr(wp_create_nonce('bluesky_repost')); ?>">
|
||||||
|
<?php esc_html_e('Post Again', 'bluesky-connctor'); ?>
|
||||||
</button>
|
</button>
|
||||||
<?php endif; ?>
|
<?php endif; ?>
|
||||||
</div>
|
</div>
|
||||||
<?php else : ?>
|
<?php else: ?>
|
||||||
<p><?php _e('This post has not been shared to Bluesky yet.', 'bluesky-connector'); ?></p>
|
<p><?php esc_html_e('This post has not been shared to Bluesky.', 'bluesky-connctor'); ?></p>
|
||||||
<button type="button" class="button bluesky-share-post" data-post-id="<?php echo esc_attr($post->ID); ?>">
|
<button type="button" class="button button-primary bluesky-share-post"
|
||||||
<?php _e('Share to Bluesky', 'bluesky-connector'); ?>
|
data-post-id="<?php echo esc_attr($post->ID); ?>"
|
||||||
|
data-nonce="<?php echo esc_attr(wp_create_nonce('bluesky_share_post')); ?>">
|
||||||
|
<?php esc_html_e('Share to Bluesky', 'bluesky-connctor'); ?>
|
||||||
</button>
|
</button>
|
||||||
<?php endif; ?>
|
<?php endif; ?>
|
||||||
</div>
|
</div>
|
@ -1,20 +1,22 @@
|
|||||||
<div class="wrap">
|
<div class="wrap">
|
||||||
<h1><?php echo esc_html(get_admin_page_title()); ?></h1>
|
<h1><?php echo esc_html(get_admin_page_title()); ?></h1>
|
||||||
|
|
||||||
<?php if (!empty($settings['connection_status'])) : ?>
|
<?php if ($settings['connection_status'] === 'connected'): ?>
|
||||||
<?php if ($settings['connection_status'] === 'connected') : ?>
|
<div class="notice notice-success">
|
||||||
<div class="notice notice-success">
|
<p><?php _e('Successfully connected to Bluesky!', 'bluesky-connctor'); ?></p>
|
||||||
<p><?php _e('Successfully connected to Bluesky!', 'bluesky-connector'); ?></p>
|
</div>
|
||||||
</div>
|
<?php else: ?>
|
||||||
<?php else : ?>
|
<div class="notice notice-error">
|
||||||
<div class="notice notice-error">
|
<!-- translators: %s: Error message -->
|
||||||
<p><?php printf(__('Connection error: %s', 'bluesky-connector'), esc_html($settings['last_error'])); ?></p>
|
<p><?php printf(
|
||||||
</div>
|
esc_html__('Connection error: %s', 'bluesky-connctor'),
|
||||||
<?php endif; ?>
|
esc_html($settings['last_error'])
|
||||||
|
); ?></p>
|
||||||
|
</div>
|
||||||
<?php endif; ?>
|
<?php endif; ?>
|
||||||
|
|
||||||
<div class="card">
|
<div class="card">
|
||||||
<h2><?php _e('Connection Settings', 'bluesky-connector'); ?></h2>
|
<h2><?php esc_html_e('Connection Settings', 'bluesky-connctor'); ?></h2>
|
||||||
<form method="post" action="">
|
<form method="post" action="">
|
||||||
<?php wp_nonce_field('bluesky_connector_settings'); ?>
|
<?php wp_nonce_field('bluesky_connector_settings'); ?>
|
||||||
<input type="hidden" name="action" value="update_bluesky_settings">
|
<input type="hidden" name="action" value="update_bluesky_settings">
|
||||||
@ -22,146 +24,122 @@
|
|||||||
<table class="form-table" role="presentation">
|
<table class="form-table" role="presentation">
|
||||||
<tr>
|
<tr>
|
||||||
<th scope="row">
|
<th scope="row">
|
||||||
<label for="bluesky_domain"><?php _e('Bluesky Domain', 'bluesky-connector'); ?></label>
|
<label for="bluesky_domain"><?php esc_html_e('Bluesky Domain', 'bluesky-connctor'); ?></label>
|
||||||
</th>
|
</th>
|
||||||
<td>
|
<td>
|
||||||
<input name="bluesky_domain"
|
<input name="bluesky_connector_settings[domain]" type="url" id="bluesky_domain"
|
||||||
type="url"
|
value="https://bsky.social" class="regular-text" readonly>
|
||||||
id="bluesky_domain"
|
|
||||||
value="https://bsky.social"
|
|
||||||
class="regular-text"
|
|
||||||
readonly>
|
|
||||||
<p class="description">
|
<p class="description">
|
||||||
<?php _e('The Bluesky API domain (fixed to https://bsky.social)', 'bluesky-connector'); ?>
|
<?php esc_html_e('The Bluesky API domain (fixed to https://bsky.social)', 'bluesky-connctor'); ?>
|
||||||
</p>
|
</p>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<th scope="row">
|
<th scope="row">
|
||||||
<label for="bluesky_identifier"><?php _e('Bluesky Handle', 'bluesky-connector'); ?></label>
|
<label for="bluesky_identifier"><?php esc_html_e('Bluesky Handle', 'bluesky-connctor'); ?></label>
|
||||||
</th>
|
</th>
|
||||||
<td>
|
<td>
|
||||||
<input name="bluesky_identifier"
|
<input name="bluesky_connector_settings[identifier]" type="text" id="bluesky_identifier"
|
||||||
type="text"
|
value="<?php echo esc_attr($settings['identifier']); ?>" class="regular-text"
|
||||||
id="bluesky_identifier"
|
placeholder="username.bsky.social" required>
|
||||||
value="<?php echo esc_attr($settings['identifier']); ?>"
|
|
||||||
class="regular-text"
|
|
||||||
placeholder="username.bsky.social"
|
|
||||||
required>
|
|
||||||
<p class="description">
|
<p class="description">
|
||||||
<?php _e('Your full Bluesky handle (e.g., username.bsky.social)', 'bluesky-connector'); ?>
|
<?php esc_html_e('Your full Bluesky handle (e.g., username.bsky.social)', 'bluesky-connctor'); ?>
|
||||||
</p>
|
</p>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<th scope="row">
|
<th scope="row">
|
||||||
<label for="bluesky_password"><?php _e('App Password', 'bluesky-connector'); ?></label>
|
<label for="bluesky_password"><?php esc_html_e('App Password', 'bluesky-connctor'); ?></label>
|
||||||
</th>
|
</th>
|
||||||
<td>
|
<td>
|
||||||
<input name="bluesky_password"
|
<input name="bluesky_connector_settings[password]" type="password" id="bluesky_password"
|
||||||
type="password"
|
class="regular-text" <?php echo empty($settings['identifier']) ? 'required' : ''; ?>>
|
||||||
id="bluesky_password"
|
|
||||||
class="regular-text"
|
|
||||||
<?php echo empty($settings['identifier']) ? 'required' : ''; ?>>
|
|
||||||
<p class="description">
|
<p class="description">
|
||||||
<?php _e('Your Bluesky app password (will not be stored)', 'bluesky-connector'); ?>
|
<?php esc_html_e('Your Bluesky app password (will not be stored)', 'bluesky-connctor'); ?>
|
||||||
</p>
|
</p>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
</table>
|
</table>
|
||||||
|
|
||||||
<?php submit_button(__('Save Connection Settings', 'bluesky-connector')); ?>
|
<?php submit_button(esc_html__('Save Connection Settings', 'bluesky-connctor')); ?>
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<?php if (!empty($settings['connection_status']) && $settings['connection_status'] === 'connected') : ?>
|
<?php if (!empty($settings['connection_status']) && $settings['connection_status'] === 'connected'): ?>
|
||||||
<div class="card" style="margin-top: 20px;">
|
<div class="card" style="margin-top: 20px;">
|
||||||
<h2><?php _e('Post Format Settings', 'bluesky-connector'); ?></h2>
|
<h2><?php esc_html_e('Post Format Settings', 'bluesky-connctor'); ?></h2>
|
||||||
<form method="post" action="options.php">
|
<form method="post" action="options.php">
|
||||||
<?php settings_fields('bluesky_connector_settings'); ?>
|
<?php
|
||||||
|
settings_fields('bluesky-connector');
|
||||||
|
do_settings_sections('bluesky-connector');
|
||||||
|
?>
|
||||||
<table class="form-table" role="presentation">
|
<table class="form-table" role="presentation">
|
||||||
<tr>
|
<tr>
|
||||||
<th scope="row">
|
<th scope="row">
|
||||||
<label for="bluesky_post_format"><?php _e('Post Layout', 'bluesky-connector'); ?></label>
|
<label for="bluesky_post_format"><?php esc_html_e('Post Layout', 'bluesky-connctor'); ?></label>
|
||||||
</th>
|
</th>
|
||||||
<td>
|
<td>
|
||||||
<select name="bluesky_post_format" id="bluesky_post_format">
|
<select name="bluesky_connector_settings[post_format]" id="bluesky_post_format">
|
||||||
<option value="title-excerpt-link" <?php selected(get_option('bluesky_post_format'), 'title-excerpt-link'); ?>>
|
<option value="title-excerpt-link" <?php selected($settings['post_format'] ?? 'title-excerpt-link', 'title-excerpt-link'); ?>>
|
||||||
<?php _e('Title + Excerpt + Link (No Image)', 'bluesky-connector'); ?>
|
<?php esc_html_e('Title + Excerpt + Link (No Image)', 'bluesky-connctor'); ?>
|
||||||
</option>
|
</option>
|
||||||
<option value="image-title-excerpt-link" <?php selected(get_option('bluesky_post_format'), 'image-title-excerpt-link'); ?>>
|
<option value="image-title-excerpt-link" <?php selected($settings['post_format'] ?? 'title-excerpt-link', 'image-title-excerpt-link'); ?>>
|
||||||
<?php _e('Image + Title + Excerpt + Link (Image will appear at top)', 'bluesky-connector'); ?>
|
<?php esc_html_e('Image + Title + Excerpt + Link', 'bluesky-connctor'); ?>
|
||||||
</option>
|
</option>
|
||||||
</select>
|
</select>
|
||||||
<p class="description">
|
<p class="description">
|
||||||
<?php _e('Note: When including images, Bluesky will always display them at the top of the post regardless of format selection.', 'bluesky-connector'); ?>
|
<?php esc_html_e('bluesky-connctor'); ?>
|
||||||
</p>
|
</p>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<th scope="row">
|
<th scope="row">
|
||||||
<label for="bluesky_include_title"><?php _e('Title Options', 'bluesky-connector'); ?></label>
|
<label for="bluesky_include_title"><?php esc_html_e('Include Title', 'bluesky-connctor'); ?></label>
|
||||||
</th>
|
</th>
|
||||||
<td>
|
<td>
|
||||||
<label>
|
<input type="checkbox" name="bluesky_connector_settings[include_title]"
|
||||||
<input type="checkbox"
|
id="bluesky_include_title" value="1" <?php checked($settings['include_title'] ?? true); ?>>
|
||||||
name="bluesky_include_title"
|
|
||||||
id="bluesky_include_title"
|
|
||||||
value="1"
|
|
||||||
<?php checked(get_option('bluesky_include_title', true)); ?>>
|
|
||||||
<?php _e('Include post title when format includes title', 'bluesky-connector'); ?>
|
|
||||||
</label>
|
|
||||||
<p class="description">
|
<p class="description">
|
||||||
<?php _e('When enabled, the post title will be included at the beginning of the post text.', 'bluesky-connector'); ?>
|
<?php esc_html_e('Include post title in the Bluesky post', 'bluesky-connctor'); ?>
|
||||||
</p>
|
</p>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
</table>
|
</table>
|
||||||
<?php submit_button(__('Save Format Settings', 'bluesky-connector')); ?>
|
<?php submit_button(esc_html__('Save Format Settings', 'bluesky-connctor')); ?>
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="card" style="margin-top: 20px;">
|
<div class="card" style="margin-top: 20px;">
|
||||||
<h2><?php _e('Queue Management', 'bluesky-connector'); ?></h2>
|
<h2><?php esc_html_e('Debug Information', 'bluesky-connctor'); ?></h2>
|
||||||
<?php
|
|
||||||
$queue = get_option('bluesky_post_queue', array());
|
|
||||||
$queue_count = count($queue);
|
|
||||||
?>
|
|
||||||
<p>
|
|
||||||
<?php printf(
|
|
||||||
_n(
|
|
||||||
'There is %s post in the queue.',
|
|
||||||
'There are %s posts in the queue.',
|
|
||||||
$queue_count,
|
|
||||||
'bluesky-connector'
|
|
||||||
),
|
|
||||||
number_format_i18n($queue_count)
|
|
||||||
); ?>
|
|
||||||
</p>
|
|
||||||
|
|
||||||
<?php if ($queue_count > 0) : ?>
|
|
||||||
<form method="post" style="margin-top: 10px;">
|
|
||||||
<?php wp_nonce_field('bluesky_process_queue'); ?>
|
|
||||||
<input type="submit" name="process_queue_now" class="button button-primary"
|
|
||||||
value="<?php esc_attr_e('Process Queue Now', 'bluesky-connector'); ?>">
|
|
||||||
</form>
|
|
||||||
<?php endif; ?>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<?php if (WP_DEBUG) : ?>
|
|
||||||
<div class="card" style="margin-top: 20px;">
|
|
||||||
<h2><?php _e('Connection Status', 'bluesky-connector'); ?></h2>
|
|
||||||
<table class="form-table" role="presentation">
|
<table class="form-table" role="presentation">
|
||||||
<tr>
|
<tr>
|
||||||
<th scope="row"><?php _e('DID', 'bluesky-connector'); ?></th>
|
<th scope="row"><?php esc_html_e('Last Post Status', 'bluesky-connctor'); ?></th>
|
||||||
<td><code><?php echo esc_html(get_option('bluesky_did')); ?></code></td>
|
<td>
|
||||||
</tr>
|
<?php
|
||||||
<tr>
|
echo wp_kses(
|
||||||
<th scope="row"><?php _e('Last Token Refresh', 'bluesky-connector'); ?></th>
|
sprintf(
|
||||||
<td><?php echo esc_html(get_option('bluesky_token_created') ? date_i18n(get_option('date_format') . ' ' . get_option('time_format'), get_option('bluesky_token_created')) : __('Never', 'bluesky-connector')); ?></td>
|
/* translators: %s: Time of last post attempt */
|
||||||
|
esc_html__('Last post attempt: %s', 'bluesky-connctor'),
|
||||||
|
get_option('bluesky_last_post_time')
|
||||||
|
? esc_html(date_i18n(get_option('date_format') . ' ' . get_option('time_format'), get_option('bluesky_last_post_time')))
|
||||||
|
: esc_html__('Never', 'bluesky-connctor')
|
||||||
|
),
|
||||||
|
array('b' => array(), 'span' => array('class' => array()))
|
||||||
|
);
|
||||||
|
?>
|
||||||
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
|
<?php if (WP_DEBUG): ?>
|
||||||
|
<tr>
|
||||||
|
<th scope="row"><?php esc_html_e('DID', 'bluesky-connctor'); ?></th>
|
||||||
|
<td><code><?php echo esc_html(get_option('bluesky_did')); ?></code></td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<th scope="row"><?php esc_html_e('Connection Status', 'bluesky-connctor'); ?></th>
|
||||||
|
<td><?php echo esc_html(get_option('bluesky_connection_status')); ?></td>
|
||||||
|
</tr>
|
||||||
|
<?php endif; ?>
|
||||||
</table>
|
</table>
|
||||||
</div>
|
</div>
|
||||||
<?php endif; ?>
|
|
||||||
<?php endif; ?>
|
<?php endif; ?>
|
||||||
</div>
|
</div>
|
Loading…
Reference in New Issue
Block a user