Plugin Directory

Changeset 3270523


Ignore:
Timestamp:
04/10/2025 02:16:18 PM (9 months ago)
Author:
danielpost
Message:

Update to version 0.0.5 from GitHub

Location:
autoblue
Files:
45 added
24 edited
1 copied

Legend:

Unmodified
Added
Removed
  • autoblue/tags/0.0.5/autoblue.php

    r3223262 r3270523  
    88 * License: GPLv2 or later
    99 * License URI: https://www.gnu.org/licenses/gpl-2.0.html
    10  * Version: 0.0.4
     10 * Version: 0.0.5
    1111 * Text Domain: autoblue
    1212 * Requires at least: 6.6
     
    2121require_once __DIR__ . '/vendor/autoload.php';
    2222
    23 define( 'AUTOBLUE_VERSION', '0.0.4' );
     23define( 'AUTOBLUE_VERSION', '0.0.5' );
    2424define( 'AUTOBLUE_SLUG', 'autoblue' );
    2525define( 'AUTOBLUE_BASENAME', plugin_basename( __FILE__ ) );
  • autoblue/tags/0.0.5/composer.json

    r3214602 r3270523  
    1717        "szepeviktor/phpstan-wordpress": "^2.0",
    1818        "phpstan/phpstan": "^2.0",
    19         "php-stubs/wp-cli-stubs": "^2.11"
     19        "php-stubs/wp-cli-stubs": "^2.11",
     20        "lucatume/wp-browser": "^4.4",
     21        "mockery/mockery": "^1.6"
    2022    },
    2123    "config": {
  • autoblue/tags/0.0.5/includes/Bluesky.php

    r3211894 r3270523  
    1414    private Logging\Log $log;
    1515
    16     public function __construct() {
    17         $this->api_client = new Bluesky\API();
    18         $this->log        = new Logging\Log();
     16    public function __construct( Bluesky\API $api_client = null, Logging\Log $log = null ) {
     17        $this->api_client = $api_client ?: new Bluesky\API();
     18        $this->log        = $log ?: new Logging\Log();
    1919    }
    2020
     
    2929     * @return array<string,mixed>|false The connection data or false if no connection is found.
    3030     */
    31     private function get_connection() {
     31    public function get_connection() {
    3232        $connections = ( new ConnectionsManager() )->get_all_connections();
    3333
     
    3737
    3838        return $connections[0];
     39    }
     40
     41    /**
     42     * Refresh the connection tokens for a given DID.
     43     *
     44     * @param string $did The DID to refresh the connection for.
     45     * @return array<string,mixed>|\WP_Error The refreshed connection data or error object.
     46     */
     47    public function refresh_connection( string $did ) {
     48        $connections_manager = new ConnectionsManager();
     49        $connection          = $connections_manager->refresh_tokens( $did );
     50
     51        return $connection;
    3952    }
    4053
     
    112125        }
    113126
    114         $connections_manager = new ConnectionsManager();
    115         $connection          = $connections_manager->refresh_tokens( $connection['did'] );
     127        $connection = $this->refresh_connection( $connection['did'] );
    116128
    117129        if ( is_wp_error( $connection ) ) {
     
    121133
    122134        $message = get_post_meta( $post_id, 'autoblue_custom_message', true );
    123         $excerpt = html_entity_decode( get_the_excerpt( $post->ID ) );
    124         $message = ! empty( $message ) ? wp_strip_all_tags( html_entity_decode( $message ) ) : $excerpt;
     135        $excerpt = get_the_excerpt( $post->ID );
     136        $message = ! empty( $message ) ? $message : $excerpt;
     137
     138        /**
     139         * Filters the message to be shared on Bluesky.
     140         *
     141         * @param string $message The message to be shared.
     142         * @param int    $post_id The post ID of the post being shared.
     143         */
     144        $message = apply_filters( 'autoblue/share_message', $message, $post_id );
     145
     146        $message = html_entity_decode( wp_strip_all_tags( $message ) );
     147        $message = ( new Utils\Text() )->trim_text( $message, 300 );
    125148
    126149        $body = [
  • autoblue/tags/0.0.5/includes/Bluesky/TextParser.php

    r3211894 r3270523  
    1818    // - Can contain letters, numbers, underscores.
    1919    // - Excludes trailing punctuation.
    20     public const TAG_REGEX = '/(?:^|\s)(#[^\d\s]\S*?)(?:\s|$|[!.,;?])/u';
     20    public const TAG_REGEX = '/(^|\s)[##]((?!\x{fe0f})[^\s\x{00AD}\x{2060}\x{200A}\x{200B}\x{200C}\x{200D}\x{20e2}]*[^\d\s\p{P}\x{00AD}\x{2060}\x{200A}\x{200B}\x{200C}\x{200D}\x{20e2}]+[^\s\x{00AD}\x{2060}\x{200A}\x{200B}\x{200C}\x{200D}\x{20e2}]*)?/u';
    2121
    2222    /**
     
    2727    public $api_client;
    2828
    29     public function __construct() {
    30         $this->api_client = new API();
     29    /**
     30     * @param API|null $api_client The Bluesky API client.
     31     */
     32    public function __construct( $api_client = null ) {
     33        $this->api_client = $api_client ?? new API();
    3134    }
    3235
     
    3639    private function is_valid_handle( string $handle ): bool {
    3740        return preg_match( '/^([a-zA-Z0-9]([a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?\.)+[a-zA-Z]([a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?$/', $handle ) === 1;
     41    }
     42
     43    private function is_valid_domain( string $str ): bool {
     44        $tlds = \Autoblue\Utils\TLD::get_valid_tlds();
     45
     46        foreach ( $tlds as $tld ) {
     47            $i = strrpos( $str, $tld );
     48
     49            if ( $i === false ) {
     50                continue;
     51            }
     52
     53            if ( $str[ $i - 1 ] === '.' && $i === strlen( $str ) - strlen( $tld ) ) {
     54                return true;
     55            }
     56        }
     57
     58        return false;
    3859    }
    3960
     
    5071            // Skip if handle doesn't match ATProto spec.
    5172            if ( ! $this->is_valid_handle( $handle ) ) {
     73                continue;
     74            }
     75
     76            // Probably not a domain.
     77            if ( ! $this->is_valid_domain( $handle ) && substr( $handle, -5 ) !== '.test' ) {
    5278                continue;
    5379            }
     
    7096     */
    7197    public function parse_urls( string $text ): array {
    72         $spans = [];
    73         preg_match_all( self::URL_REGEX, $text, $matches, PREG_OFFSET_CAPTURE );
    74 
    75         foreach ( $matches[1] as $match ) {
     98        $spans  = [];
     99        $offset = 0;
     100
     101        while ( preg_match( self::URL_REGEX, $text, $match, PREG_OFFSET_CAPTURE, $offset ) ) {
     102            $uri = $match[1][0];
     103
     104            // If it doesn't start with http, ensure it's a valid domain and add https://.
     105            if ( strpos( $uri, 'http' ) !== 0 ) {
     106                // Extract domain from URI.
     107                if ( preg_match( '/^(?:www\.)?([^\/]+)/', $uri, $domain_match ) ) {
     108                    $domain = $domain_match[1];
     109                    if ( ! $this->is_valid_domain( $domain ) ) {
     110                        $offset = $match[0][1] + 1;
     111                        continue;
     112                    }
     113                    $uri = 'https://' . $uri;
     114                } else {
     115                    $offset = $match[0][1] + 1;
     116                    continue;
     117                }
     118            }
     119
     120            $start = $match[1][1];
     121            $end   = $start + strlen( $match[1][0] );
     122
     123            // Strip ending punctuation.
     124            if ( preg_match( '/[.,;:!?]$/', $uri ) ) {
     125                $uri = substr( $uri, 0, -1 );
     126                --$end;
     127            }
     128
     129            // Handle closing parenthesis.
     130            if ( substr( $uri, -1 ) === ')' && strpos( $uri, '(' ) === false ) {
     131                $uri = substr( $uri, 0, -1 );
     132                --$end;
     133            }
     134
    76135            $spans[] = [
    77                 'start' => mb_strlen( substr( $text, 0, $match[1] ), '8bit' ),
    78                 'end'   => mb_strlen( substr( $text, 0, $match[1] + strlen( $match[0] ) ), '8bit' ),
    79                 'url'   => $match[0],
    80             ];
     136                'start' => mb_strlen( substr( $text, 0, $start ), '8bit' ),
     137                'end'   => mb_strlen( substr( $text, 0, $end ), '8bit' ),
     138                'url'   => $uri,
     139            ];
     140
     141            $offset = $match[0][1] + 1;
    81142        }
    82143
     
    88149     */
    89150    public function parse_tags( string $text ): array {
    90         $spans = [];
    91         preg_match_all( self::TAG_REGEX, $text, $matches, PREG_OFFSET_CAPTURE );
    92 
    93         foreach ( $matches[1] as $match ) {
    94             $tag = $match[0];
    95             // Clean up the tag.
     151        $facets = [];
     152        $offset = 0;
     153
     154        while ( preg_match( self::TAG_REGEX, $text, $match, PREG_OFFSET_CAPTURE, $offset ) ) {
     155            $leading = $match[1][0]; // The space or start of string.
     156            $tag     = $match[2][0] ?? ''; // The tag content without #.
     157
     158            if ( empty( $tag ) ) {
     159                $offset = $match[0][1] + 1;
     160                continue;
     161            }
     162
     163            // Strip ending punctuation and spaces.
    96164            $tag = trim( $tag );
    97             $tag = preg_replace( '/\p{P}+$/u', '', $tag );
    98 
    99             if ( empty( $tag ) ) {
    100                 continue;
    101             }
    102 
    103             // Skip if tag is too long (over 64 chars including #).
    104             if ( mb_strlen( $tag ) > 66 ) {
    105                 continue;
    106             }
    107 
    108             $spans[] = [
    109                 'start' => mb_strlen( substr( $text, 0, $match[1] ), '8bit' ),
    110                 'end'   => mb_strlen( substr( $text, 0, $match[1] + strlen( $tag ) ), '8bit' ),
    111                 'tag'   => ltrim( $tag, '#' ),
    112             ];
    113         }
    114 
    115         return $spans;
     165            $tag = preg_replace( '/[.,!?:;]*$/', '', $tag );
     166
     167            if ( is_null( $tag ) || strlen( $tag ) === 0 || strlen( $tag ) > 64 ) {
     168                $offset = $match[0][1] + 1;
     169                continue;
     170            }
     171
     172            $index = $match[0][1] + strlen( $leading ); // Match index + leading space length.
     173
     174            $facets[] = [
     175                'start' => mb_strlen( substr( $text, 0, $index ), '8bit' ),
     176                'end'   => mb_strlen( substr( $text, 0, $index + strlen( $tag ) + 1 ), '8bit' ), // +1 for #
     177                'tag'   => $tag,
     178            ];
     179
     180            $offset = $match[0][1] + 1; // Move past current match.
     181        }
     182
     183        return $facets;
    116184    }
    117185
  • autoblue/tags/0.0.5/includes/Logging/Setup.php

    r3223262 r3270523  
    44
    55class Setup {
    6     private const DB_VERSION = '20241202'; // YYYYMMDD
     6    private const DB_VERSION = '20241202'; // YYYYMMDD.
    77
    88    public function register_hooks(): void {
     
    1616    }
    1717
    18     private function create_table(): void {
     18    public function create_table(): void {
    1919        global $wpdb;
    2020
  • autoblue/tags/0.0.5/readme.txt

    r3223262 r3270523  
    22Contributors: danielpost
    33Tags: social, bluesky, auto, share, post
    4 Stable tag: 0.0.4
     4Stable tag: 0.0.5
    55Requires at least: 6.6
    66Tested up to: 6.7
     
    4141== Screenshots ==
    4242
    43 1. Easily connect your Bluesky account to your WordPress site.
    44 2. Set a custom text, choose a featured image and publish your post.
     431. Easily connect your Bluesky account to your WordPress site and start sharing.
     442. Show Bluesky likes and replies on your WordPress site.
     453. Keep track of everything that Autoblue does.
    4546
    4647== Changelog ==
     48
     49= 0.0.5 =
     50* Feature: Add filter for setting a custom share message
     51* Fix: Excerpts are now trimmed correctly before being shared
    4752
    4853= 0.0.4 =
  • autoblue/tags/0.0.5/vendor/autoload.php

    r3214602 r3270523  
    1515        }
    1616    }
    17     trigger_error(
    18         $err,
    19         E_USER_ERROR
    20     );
     17    throw new RuntimeException($err);
    2118}
    2219
    2320require_once __DIR__ . '/composer/autoload_real.php';
    2421
    25 return ComposerAutoloaderInit7e988a03fbffe9893b5755a660fe73f5::getLoader();
     22return ComposerAutoloaderInitbfb327bc5b5bd0bc00762088df045c8a::getLoader();
  • autoblue/tags/0.0.5/vendor/composer/InstalledVersions.php

    r3211894 r3270523  
    2828{
    2929    /**
     30     * @var string|null if set (by reflection by Composer), this should be set to the path where this class is being copied to
     31     * @internal
     32     */
     33    private static $selfDir = null;
     34
     35    /**
    3036     * @var mixed[]|null
    3137     * @psalm-var array{root: array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}, versions: array<string, array{pretty_version?: string, version?: string, reference?: string|null, type?: string, install_path?: string, aliases?: string[], dev_requirement: bool, replaced?: string[], provided?: string[]}>}|array{}|null
    3238     */
    3339    private static $installed;
     40
     41    /**
     42     * @var bool
     43     */
     44    private static $installedIsLocalDir;
    3445
    3546    /**
     
    310321        self::$installed = $data;
    311322        self::$installedByVendor = array();
     323
     324        // when using reload, we disable the duplicate protection to ensure that self::$installed data is
     325        // always returned, but we cannot know whether it comes from the installed.php in __DIR__ or not,
     326        // so we have to assume it does not, and that may result in duplicate data being returned when listing
     327        // all installed packages for example
     328        self::$installedIsLocalDir = false;
     329    }
     330
     331    /**
     332     * @return string
     333     */
     334    private static function getSelfDir()
     335    {
     336        if (self::$selfDir === null) {
     337            self::$selfDir = strtr(__DIR__, '\\', '/');
     338        }
     339
     340        return self::$selfDir;
    312341    }
    313342
     
    326355
    327356        if (self::$canGetVendors) {
     357            $selfDir = self::getSelfDir();
    328358            foreach (ClassLoader::getRegisteredLoaders() as $vendorDir => $loader) {
     359                $vendorDir = strtr($vendorDir, '\\', '/');
    329360                if (isset(self::$installedByVendor[$vendorDir])) {
    330361                    $installed[] = self::$installedByVendor[$vendorDir];
     
    334365                    self::$installedByVendor[$vendorDir] = $required;
    335366                    $installed[] = $required;
    336                     if (strtr($vendorDir.'/composer', '\\', '/') === strtr(__DIR__, '\\', '/')) {
     367                    if (self::$installed === null && $vendorDir.'/composer' === $selfDir) {
    337368                        self::$installed = $required;
    338                         $copiedLocalDir = true;
     369                        self::$installedIsLocalDir = true;
    339370                    }
     371                }
     372                if (self::$installedIsLocalDir && $vendorDir.'/composer' === $selfDir) {
     373                    $copiedLocalDir = true;
    340374                }
    341375            }
  • autoblue/tags/0.0.5/vendor/composer/autoload_classmap.php

    r3211894 r3270523  
    3131    'Autoblue\\Setup' => $baseDir . '/includes/Setup.php',
    3232    'Autoblue\\Utils' => $baseDir . '/includes/Utils.php',
     33    'Autoblue\\Utils\\TLD' => $baseDir . '/includes/Utils/TLD.php',
     34    'Autoblue\\Utils\\Text' => $baseDir . '/includes/Utils/Text.php',
    3335    'Composer\\InstalledVersions' => $vendorDir . '/composer/InstalledVersions.php',
    3436);
  • autoblue/tags/0.0.5/vendor/composer/autoload_real.php

    r3214602 r3270523  
    33// autoload_real.php @generated by Composer
    44
    5 class ComposerAutoloaderInit7e988a03fbffe9893b5755a660fe73f5
     5class ComposerAutoloaderInitbfb327bc5b5bd0bc00762088df045c8a
    66{
    77    private static $loader;
     
    2525        require __DIR__ . '/platform_check.php';
    2626
    27         spl_autoload_register(array('ComposerAutoloaderInit7e988a03fbffe9893b5755a660fe73f5', 'loadClassLoader'), true, true);
     27        spl_autoload_register(array('ComposerAutoloaderInitbfb327bc5b5bd0bc00762088df045c8a', 'loadClassLoader'), true, true);
    2828        self::$loader = $loader = new \Composer\Autoload\ClassLoader(\dirname(__DIR__));
    29         spl_autoload_unregister(array('ComposerAutoloaderInit7e988a03fbffe9893b5755a660fe73f5', 'loadClassLoader'));
     29        spl_autoload_unregister(array('ComposerAutoloaderInitbfb327bc5b5bd0bc00762088df045c8a', 'loadClassLoader'));
    3030
    3131        require __DIR__ . '/autoload_static.php';
    32         call_user_func(\Composer\Autoload\ComposerStaticInit7e988a03fbffe9893b5755a660fe73f5::getInitializer($loader));
     32        call_user_func(\Composer\Autoload\ComposerStaticInitbfb327bc5b5bd0bc00762088df045c8a::getInitializer($loader));
    3333
    3434        $loader->register(true);
  • autoblue/tags/0.0.5/vendor/composer/autoload_static.php

    r3214602 r3270523  
    55namespace Composer\Autoload;
    66
    7 class ComposerStaticInit7e988a03fbffe9893b5755a660fe73f5
     7class ComposerStaticInitbfb327bc5b5bd0bc00762088df045c8a
    88{
    99    public static $prefixLengthsPsr4 = array (
     
    5454        'Autoblue\\Setup' => __DIR__ . '/../..' . '/includes/Setup.php',
    5555        'Autoblue\\Utils' => __DIR__ . '/../..' . '/includes/Utils.php',
     56        'Autoblue\\Utils\\TLD' => __DIR__ . '/../..' . '/includes/Utils/TLD.php',
     57        'Autoblue\\Utils\\Text' => __DIR__ . '/../..' . '/includes/Utils/Text.php',
    5658        'Composer\\InstalledVersions' => __DIR__ . '/..' . '/composer/InstalledVersions.php',
    5759    );
     
    6062    {
    6163        return \Closure::bind(function () use ($loader) {
    62             $loader->prefixLengthsPsr4 = ComposerStaticInit7e988a03fbffe9893b5755a660fe73f5::$prefixLengthsPsr4;
    63             $loader->prefixDirsPsr4 = ComposerStaticInit7e988a03fbffe9893b5755a660fe73f5::$prefixDirsPsr4;
    64             $loader->classMap = ComposerStaticInit7e988a03fbffe9893b5755a660fe73f5::$classMap;
     64            $loader->prefixLengthsPsr4 = ComposerStaticInitbfb327bc5b5bd0bc00762088df045c8a::$prefixLengthsPsr4;
     65            $loader->prefixDirsPsr4 = ComposerStaticInitbfb327bc5b5bd0bc00762088df045c8a::$prefixDirsPsr4;
     66            $loader->classMap = ComposerStaticInitbfb327bc5b5bd0bc00762088df045c8a::$classMap;
    6567
    6668        }, null, ClassLoader::class);
  • autoblue/tags/0.0.5/vendor/composer/installed.php

    r3223262 r3270523  
    22    'root' => array(
    33        'name' => 'posty/autoblue',
    4         'pretty_version' => 'v0.0.4',
    5         'version' => '0.0.4.0',
    6         'reference' => '0725945813790284b417f9553bbd96ac7f4c89f2',
     4        'pretty_version' => 'v0.0.5',
     5        'version' => '0.0.5.0',
     6        'reference' => 'e777fe95e9c1caa7da4a76a4b09a863896f9d54b',
    77        'type' => 'project',
    88        'install_path' => __DIR__ . '/../../',
     
    2121        ),
    2222        'posty/autoblue' => array(
    23             'pretty_version' => 'v0.0.4',
    24             'version' => '0.0.4.0',
    25             'reference' => '0725945813790284b417f9553bbd96ac7f4c89f2',
     23            'pretty_version' => 'v0.0.5',
     24            'version' => '0.0.5.0',
     25            'reference' => 'e777fe95e9c1caa7da4a76a4b09a863896f9d54b',
    2626            'type' => 'project',
    2727            'install_path' => __DIR__ . '/../../',
  • autoblue/trunk/autoblue.php

    r3223262 r3270523  
    88 * License: GPLv2 or later
    99 * License URI: https://www.gnu.org/licenses/gpl-2.0.html
    10  * Version: 0.0.4
     10 * Version: 0.0.5
    1111 * Text Domain: autoblue
    1212 * Requires at least: 6.6
     
    2121require_once __DIR__ . '/vendor/autoload.php';
    2222
    23 define( 'AUTOBLUE_VERSION', '0.0.4' );
     23define( 'AUTOBLUE_VERSION', '0.0.5' );
    2424define( 'AUTOBLUE_SLUG', 'autoblue' );
    2525define( 'AUTOBLUE_BASENAME', plugin_basename( __FILE__ ) );
  • autoblue/trunk/composer.json

    r3214602 r3270523  
    1717        "szepeviktor/phpstan-wordpress": "^2.0",
    1818        "phpstan/phpstan": "^2.0",
    19         "php-stubs/wp-cli-stubs": "^2.11"
     19        "php-stubs/wp-cli-stubs": "^2.11",
     20        "lucatume/wp-browser": "^4.4",
     21        "mockery/mockery": "^1.6"
    2022    },
    2123    "config": {
  • autoblue/trunk/includes/Bluesky.php

    r3211894 r3270523  
    1414    private Logging\Log $log;
    1515
    16     public function __construct() {
    17         $this->api_client = new Bluesky\API();
    18         $this->log        = new Logging\Log();
     16    public function __construct( Bluesky\API $api_client = null, Logging\Log $log = null ) {
     17        $this->api_client = $api_client ?: new Bluesky\API();
     18        $this->log        = $log ?: new Logging\Log();
    1919    }
    2020
     
    2929     * @return array<string,mixed>|false The connection data or false if no connection is found.
    3030     */
    31     private function get_connection() {
     31    public function get_connection() {
    3232        $connections = ( new ConnectionsManager() )->get_all_connections();
    3333
     
    3737
    3838        return $connections[0];
     39    }
     40
     41    /**
     42     * Refresh the connection tokens for a given DID.
     43     *
     44     * @param string $did The DID to refresh the connection for.
     45     * @return array<string,mixed>|\WP_Error The refreshed connection data or error object.
     46     */
     47    public function refresh_connection( string $did ) {
     48        $connections_manager = new ConnectionsManager();
     49        $connection          = $connections_manager->refresh_tokens( $did );
     50
     51        return $connection;
    3952    }
    4053
     
    112125        }
    113126
    114         $connections_manager = new ConnectionsManager();
    115         $connection          = $connections_manager->refresh_tokens( $connection['did'] );
     127        $connection = $this->refresh_connection( $connection['did'] );
    116128
    117129        if ( is_wp_error( $connection ) ) {
     
    121133
    122134        $message = get_post_meta( $post_id, 'autoblue_custom_message', true );
    123         $excerpt = html_entity_decode( get_the_excerpt( $post->ID ) );
    124         $message = ! empty( $message ) ? wp_strip_all_tags( html_entity_decode( $message ) ) : $excerpt;
     135        $excerpt = get_the_excerpt( $post->ID );
     136        $message = ! empty( $message ) ? $message : $excerpt;
     137
     138        /**
     139         * Filters the message to be shared on Bluesky.
     140         *
     141         * @param string $message The message to be shared.
     142         * @param int    $post_id The post ID of the post being shared.
     143         */
     144        $message = apply_filters( 'autoblue/share_message', $message, $post_id );
     145
     146        $message = html_entity_decode( wp_strip_all_tags( $message ) );
     147        $message = ( new Utils\Text() )->trim_text( $message, 300 );
    125148
    126149        $body = [
  • autoblue/trunk/includes/Bluesky/TextParser.php

    r3211894 r3270523  
    1818    // - Can contain letters, numbers, underscores.
    1919    // - Excludes trailing punctuation.
    20     public const TAG_REGEX = '/(?:^|\s)(#[^\d\s]\S*?)(?:\s|$|[!.,;?])/u';
     20    public const TAG_REGEX = '/(^|\s)[##]((?!\x{fe0f})[^\s\x{00AD}\x{2060}\x{200A}\x{200B}\x{200C}\x{200D}\x{20e2}]*[^\d\s\p{P}\x{00AD}\x{2060}\x{200A}\x{200B}\x{200C}\x{200D}\x{20e2}]+[^\s\x{00AD}\x{2060}\x{200A}\x{200B}\x{200C}\x{200D}\x{20e2}]*)?/u';
    2121
    2222    /**
     
    2727    public $api_client;
    2828
    29     public function __construct() {
    30         $this->api_client = new API();
     29    /**
     30     * @param API|null $api_client The Bluesky API client.
     31     */
     32    public function __construct( $api_client = null ) {
     33        $this->api_client = $api_client ?? new API();
    3134    }
    3235
     
    3639    private function is_valid_handle( string $handle ): bool {
    3740        return preg_match( '/^([a-zA-Z0-9]([a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?\.)+[a-zA-Z]([a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?$/', $handle ) === 1;
     41    }
     42
     43    private function is_valid_domain( string $str ): bool {
     44        $tlds = \Autoblue\Utils\TLD::get_valid_tlds();
     45
     46        foreach ( $tlds as $tld ) {
     47            $i = strrpos( $str, $tld );
     48
     49            if ( $i === false ) {
     50                continue;
     51            }
     52
     53            if ( $str[ $i - 1 ] === '.' && $i === strlen( $str ) - strlen( $tld ) ) {
     54                return true;
     55            }
     56        }
     57
     58        return false;
    3859    }
    3960
     
    5071            // Skip if handle doesn't match ATProto spec.
    5172            if ( ! $this->is_valid_handle( $handle ) ) {
     73                continue;
     74            }
     75
     76            // Probably not a domain.
     77            if ( ! $this->is_valid_domain( $handle ) && substr( $handle, -5 ) !== '.test' ) {
    5278                continue;
    5379            }
     
    7096     */
    7197    public function parse_urls( string $text ): array {
    72         $spans = [];
    73         preg_match_all( self::URL_REGEX, $text, $matches, PREG_OFFSET_CAPTURE );
    74 
    75         foreach ( $matches[1] as $match ) {
     98        $spans  = [];
     99        $offset = 0;
     100
     101        while ( preg_match( self::URL_REGEX, $text, $match, PREG_OFFSET_CAPTURE, $offset ) ) {
     102            $uri = $match[1][0];
     103
     104            // If it doesn't start with http, ensure it's a valid domain and add https://.
     105            if ( strpos( $uri, 'http' ) !== 0 ) {
     106                // Extract domain from URI.
     107                if ( preg_match( '/^(?:www\.)?([^\/]+)/', $uri, $domain_match ) ) {
     108                    $domain = $domain_match[1];
     109                    if ( ! $this->is_valid_domain( $domain ) ) {
     110                        $offset = $match[0][1] + 1;
     111                        continue;
     112                    }
     113                    $uri = 'https://' . $uri;
     114                } else {
     115                    $offset = $match[0][1] + 1;
     116                    continue;
     117                }
     118            }
     119
     120            $start = $match[1][1];
     121            $end   = $start + strlen( $match[1][0] );
     122
     123            // Strip ending punctuation.
     124            if ( preg_match( '/[.,;:!?]$/', $uri ) ) {
     125                $uri = substr( $uri, 0, -1 );
     126                --$end;
     127            }
     128
     129            // Handle closing parenthesis.
     130            if ( substr( $uri, -1 ) === ')' && strpos( $uri, '(' ) === false ) {
     131                $uri = substr( $uri, 0, -1 );
     132                --$end;
     133            }
     134
    76135            $spans[] = [
    77                 'start' => mb_strlen( substr( $text, 0, $match[1] ), '8bit' ),
    78                 'end'   => mb_strlen( substr( $text, 0, $match[1] + strlen( $match[0] ) ), '8bit' ),
    79                 'url'   => $match[0],
    80             ];
     136                'start' => mb_strlen( substr( $text, 0, $start ), '8bit' ),
     137                'end'   => mb_strlen( substr( $text, 0, $end ), '8bit' ),
     138                'url'   => $uri,
     139            ];
     140
     141            $offset = $match[0][1] + 1;
    81142        }
    82143
     
    88149     */
    89150    public function parse_tags( string $text ): array {
    90         $spans = [];
    91         preg_match_all( self::TAG_REGEX, $text, $matches, PREG_OFFSET_CAPTURE );
    92 
    93         foreach ( $matches[1] as $match ) {
    94             $tag = $match[0];
    95             // Clean up the tag.
     151        $facets = [];
     152        $offset = 0;
     153
     154        while ( preg_match( self::TAG_REGEX, $text, $match, PREG_OFFSET_CAPTURE, $offset ) ) {
     155            $leading = $match[1][0]; // The space or start of string.
     156            $tag     = $match[2][0] ?? ''; // The tag content without #.
     157
     158            if ( empty( $tag ) ) {
     159                $offset = $match[0][1] + 1;
     160                continue;
     161            }
     162
     163            // Strip ending punctuation and spaces.
    96164            $tag = trim( $tag );
    97             $tag = preg_replace( '/\p{P}+$/u', '', $tag );
    98 
    99             if ( empty( $tag ) ) {
    100                 continue;
    101             }
    102 
    103             // Skip if tag is too long (over 64 chars including #).
    104             if ( mb_strlen( $tag ) > 66 ) {
    105                 continue;
    106             }
    107 
    108             $spans[] = [
    109                 'start' => mb_strlen( substr( $text, 0, $match[1] ), '8bit' ),
    110                 'end'   => mb_strlen( substr( $text, 0, $match[1] + strlen( $tag ) ), '8bit' ),
    111                 'tag'   => ltrim( $tag, '#' ),
    112             ];
    113         }
    114 
    115         return $spans;
     165            $tag = preg_replace( '/[.,!?:;]*$/', '', $tag );
     166
     167            if ( is_null( $tag ) || strlen( $tag ) === 0 || strlen( $tag ) > 64 ) {
     168                $offset = $match[0][1] + 1;
     169                continue;
     170            }
     171
     172            $index = $match[0][1] + strlen( $leading ); // Match index + leading space length.
     173
     174            $facets[] = [
     175                'start' => mb_strlen( substr( $text, 0, $index ), '8bit' ),
     176                'end'   => mb_strlen( substr( $text, 0, $index + strlen( $tag ) + 1 ), '8bit' ), // +1 for #
     177                'tag'   => $tag,
     178            ];
     179
     180            $offset = $match[0][1] + 1; // Move past current match.
     181        }
     182
     183        return $facets;
    116184    }
    117185
  • autoblue/trunk/includes/Logging/Setup.php

    r3223262 r3270523  
    44
    55class Setup {
    6     private const DB_VERSION = '20241202'; // YYYYMMDD
     6    private const DB_VERSION = '20241202'; // YYYYMMDD.
    77
    88    public function register_hooks(): void {
     
    1616    }
    1717
    18     private function create_table(): void {
     18    public function create_table(): void {
    1919        global $wpdb;
    2020
  • autoblue/trunk/readme.txt

    r3223262 r3270523  
    22Contributors: danielpost
    33Tags: social, bluesky, auto, share, post
    4 Stable tag: 0.0.4
     4Stable tag: 0.0.5
    55Requires at least: 6.6
    66Tested up to: 6.7
     
    4141== Screenshots ==
    4242
    43 1. Easily connect your Bluesky account to your WordPress site.
    44 2. Set a custom text, choose a featured image and publish your post.
     431. Easily connect your Bluesky account to your WordPress site and start sharing.
     442. Show Bluesky likes and replies on your WordPress site.
     453. Keep track of everything that Autoblue does.
    4546
    4647== Changelog ==
     48
     49= 0.0.5 =
     50* Feature: Add filter for setting a custom share message
     51* Fix: Excerpts are now trimmed correctly before being shared
    4752
    4853= 0.0.4 =
  • autoblue/trunk/vendor/autoload.php

    r3214602 r3270523  
    1515        }
    1616    }
    17     trigger_error(
    18         $err,
    19         E_USER_ERROR
    20     );
     17    throw new RuntimeException($err);
    2118}
    2219
    2320require_once __DIR__ . '/composer/autoload_real.php';
    2421
    25 return ComposerAutoloaderInit7e988a03fbffe9893b5755a660fe73f5::getLoader();
     22return ComposerAutoloaderInitbfb327bc5b5bd0bc00762088df045c8a::getLoader();
  • autoblue/trunk/vendor/composer/InstalledVersions.php

    r3211894 r3270523  
    2828{
    2929    /**
     30     * @var string|null if set (by reflection by Composer), this should be set to the path where this class is being copied to
     31     * @internal
     32     */
     33    private static $selfDir = null;
     34
     35    /**
    3036     * @var mixed[]|null
    3137     * @psalm-var array{root: array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}, versions: array<string, array{pretty_version?: string, version?: string, reference?: string|null, type?: string, install_path?: string, aliases?: string[], dev_requirement: bool, replaced?: string[], provided?: string[]}>}|array{}|null
    3238     */
    3339    private static $installed;
     40
     41    /**
     42     * @var bool
     43     */
     44    private static $installedIsLocalDir;
    3445
    3546    /**
     
    310321        self::$installed = $data;
    311322        self::$installedByVendor = array();
     323
     324        // when using reload, we disable the duplicate protection to ensure that self::$installed data is
     325        // always returned, but we cannot know whether it comes from the installed.php in __DIR__ or not,
     326        // so we have to assume it does not, and that may result in duplicate data being returned when listing
     327        // all installed packages for example
     328        self::$installedIsLocalDir = false;
     329    }
     330
     331    /**
     332     * @return string
     333     */
     334    private static function getSelfDir()
     335    {
     336        if (self::$selfDir === null) {
     337            self::$selfDir = strtr(__DIR__, '\\', '/');
     338        }
     339
     340        return self::$selfDir;
    312341    }
    313342
     
    326355
    327356        if (self::$canGetVendors) {
     357            $selfDir = self::getSelfDir();
    328358            foreach (ClassLoader::getRegisteredLoaders() as $vendorDir => $loader) {
     359                $vendorDir = strtr($vendorDir, '\\', '/');
    329360                if (isset(self::$installedByVendor[$vendorDir])) {
    330361                    $installed[] = self::$installedByVendor[$vendorDir];
     
    334365                    self::$installedByVendor[$vendorDir] = $required;
    335366                    $installed[] = $required;
    336                     if (strtr($vendorDir.'/composer', '\\', '/') === strtr(__DIR__, '\\', '/')) {
     367                    if (self::$installed === null && $vendorDir.'/composer' === $selfDir) {
    337368                        self::$installed = $required;
    338                         $copiedLocalDir = true;
     369                        self::$installedIsLocalDir = true;
    339370                    }
     371                }
     372                if (self::$installedIsLocalDir && $vendorDir.'/composer' === $selfDir) {
     373                    $copiedLocalDir = true;
    340374                }
    341375            }
  • autoblue/trunk/vendor/composer/autoload_classmap.php

    r3211894 r3270523  
    3131    'Autoblue\\Setup' => $baseDir . '/includes/Setup.php',
    3232    'Autoblue\\Utils' => $baseDir . '/includes/Utils.php',
     33    'Autoblue\\Utils\\TLD' => $baseDir . '/includes/Utils/TLD.php',
     34    'Autoblue\\Utils\\Text' => $baseDir . '/includes/Utils/Text.php',
    3335    'Composer\\InstalledVersions' => $vendorDir . '/composer/InstalledVersions.php',
    3436);
  • autoblue/trunk/vendor/composer/autoload_real.php

    r3214602 r3270523  
    33// autoload_real.php @generated by Composer
    44
    5 class ComposerAutoloaderInit7e988a03fbffe9893b5755a660fe73f5
     5class ComposerAutoloaderInitbfb327bc5b5bd0bc00762088df045c8a
    66{
    77    private static $loader;
     
    2525        require __DIR__ . '/platform_check.php';
    2626
    27         spl_autoload_register(array('ComposerAutoloaderInit7e988a03fbffe9893b5755a660fe73f5', 'loadClassLoader'), true, true);
     27        spl_autoload_register(array('ComposerAutoloaderInitbfb327bc5b5bd0bc00762088df045c8a', 'loadClassLoader'), true, true);
    2828        self::$loader = $loader = new \Composer\Autoload\ClassLoader(\dirname(__DIR__));
    29         spl_autoload_unregister(array('ComposerAutoloaderInit7e988a03fbffe9893b5755a660fe73f5', 'loadClassLoader'));
     29        spl_autoload_unregister(array('ComposerAutoloaderInitbfb327bc5b5bd0bc00762088df045c8a', 'loadClassLoader'));
    3030
    3131        require __DIR__ . '/autoload_static.php';
    32         call_user_func(\Composer\Autoload\ComposerStaticInit7e988a03fbffe9893b5755a660fe73f5::getInitializer($loader));
     32        call_user_func(\Composer\Autoload\ComposerStaticInitbfb327bc5b5bd0bc00762088df045c8a::getInitializer($loader));
    3333
    3434        $loader->register(true);
  • autoblue/trunk/vendor/composer/autoload_static.php

    r3214602 r3270523  
    55namespace Composer\Autoload;
    66
    7 class ComposerStaticInit7e988a03fbffe9893b5755a660fe73f5
     7class ComposerStaticInitbfb327bc5b5bd0bc00762088df045c8a
    88{
    99    public static $prefixLengthsPsr4 = array (
     
    5454        'Autoblue\\Setup' => __DIR__ . '/../..' . '/includes/Setup.php',
    5555        'Autoblue\\Utils' => __DIR__ . '/../..' . '/includes/Utils.php',
     56        'Autoblue\\Utils\\TLD' => __DIR__ . '/../..' . '/includes/Utils/TLD.php',
     57        'Autoblue\\Utils\\Text' => __DIR__ . '/../..' . '/includes/Utils/Text.php',
    5658        'Composer\\InstalledVersions' => __DIR__ . '/..' . '/composer/InstalledVersions.php',
    5759    );
     
    6062    {
    6163        return \Closure::bind(function () use ($loader) {
    62             $loader->prefixLengthsPsr4 = ComposerStaticInit7e988a03fbffe9893b5755a660fe73f5::$prefixLengthsPsr4;
    63             $loader->prefixDirsPsr4 = ComposerStaticInit7e988a03fbffe9893b5755a660fe73f5::$prefixDirsPsr4;
    64             $loader->classMap = ComposerStaticInit7e988a03fbffe9893b5755a660fe73f5::$classMap;
     64            $loader->prefixLengthsPsr4 = ComposerStaticInitbfb327bc5b5bd0bc00762088df045c8a::$prefixLengthsPsr4;
     65            $loader->prefixDirsPsr4 = ComposerStaticInitbfb327bc5b5bd0bc00762088df045c8a::$prefixDirsPsr4;
     66            $loader->classMap = ComposerStaticInitbfb327bc5b5bd0bc00762088df045c8a::$classMap;
    6567
    6668        }, null, ClassLoader::class);
  • autoblue/trunk/vendor/composer/installed.php

    r3223262 r3270523  
    22    'root' => array(
    33        'name' => 'posty/autoblue',
    4         'pretty_version' => 'v0.0.4',
    5         'version' => '0.0.4.0',
    6         'reference' => '0725945813790284b417f9553bbd96ac7f4c89f2',
     4        'pretty_version' => 'v0.0.5',
     5        'version' => '0.0.5.0',
     6        'reference' => 'e777fe95e9c1caa7da4a76a4b09a863896f9d54b',
    77        'type' => 'project',
    88        'install_path' => __DIR__ . '/../../',
     
    2121        ),
    2222        'posty/autoblue' => array(
    23             'pretty_version' => 'v0.0.4',
    24             'version' => '0.0.4.0',
    25             'reference' => '0725945813790284b417f9553bbd96ac7f4c89f2',
     23            'pretty_version' => 'v0.0.5',
     24            'version' => '0.0.5.0',
     25            'reference' => 'e777fe95e9c1caa7da4a76a4b09a863896f9d54b',
    2626            'type' => 'project',
    2727            'install_path' => __DIR__ . '/../../',
Note: See TracChangeset for help on using the changeset viewer.