From 2951243b079499e3a70e855a8b1d9b078cbfbfd9 Mon Sep 17 00:00:00 2001 From: Michael Date: Wed, 28 Jul 2021 22:22:00 +0000 Subject: [PATCH 01/18] Preparation for delayed posts --- database.sql | 58 +++++++++++++++++--------------- doc/database/db_delayed-post.md | 6 ++++ src/Core/Worker.php | 18 +++++----- src/Model/Post/Delayed.php | 15 +++++++-- static/dbstructure.config.php | 59 ++++++++++++++++++--------------- 5 files changed, 92 insertions(+), 64 deletions(-) diff --git a/database.sql b/database.sql index 8ae1b913b5..da39341f15 100644 --- a/database.sql +++ b/database.sql @@ -1,6 +1,6 @@ -- ------------------------------------------ -- Friendica 2021.09-dev (Siberian Iris) --- DB_UPDATE_VERSION 1430 +-- DB_UPDATE_VERSION 1431 -- ------------------------------------------ @@ -492,16 +492,47 @@ CREATE TABLE IF NOT EXISTS `conversation` ( INDEX `received` (`received`) ) DEFAULT COLLATE utf8mb4_general_ci COMMENT='Raw data and structure information for messages'; +-- +-- TABLE workerqueue +-- +CREATE TABLE IF NOT EXISTS `workerqueue` ( + `id` int unsigned NOT NULL auto_increment COMMENT 'Auto incremented worker task id', + `command` varchar(100) COMMENT 'Task command', + `parameter` mediumtext COMMENT 'Task parameter', + `priority` tinyint unsigned NOT NULL DEFAULT 0 COMMENT 'Task priority', + `created` datetime NOT NULL DEFAULT '0001-01-01 00:00:00' COMMENT 'Creation date', + `pid` int unsigned NOT NULL DEFAULT 0 COMMENT 'Process id of the worker', + `executed` datetime NOT NULL DEFAULT '0001-01-01 00:00:00' COMMENT 'Execution date', + `next_try` datetime NOT NULL DEFAULT '0001-01-01 00:00:00' COMMENT 'Next retrial date', + `retrial` tinyint NOT NULL DEFAULT 0 COMMENT 'Retrial counter', + `done` boolean NOT NULL DEFAULT '0' COMMENT 'Marked 1 when the task was done - will be deleted later', + PRIMARY KEY(`id`), + INDEX `command` (`command`), + INDEX `done_command_parameter` (`done`,`command`,`parameter`(64)), + INDEX `done_executed` (`done`,`executed`), + INDEX `done_priority_retrial_created` (`done`,`priority`,`retrial`,`created`), + INDEX `done_priority_next_try` (`done`,`priority`,`next_try`), + INDEX `done_pid_next_try` (`done`,`pid`,`next_try`), + INDEX `done_pid_retrial` (`done`,`pid`,`retrial`), + INDEX `done_pid_priority_created` (`done`,`pid`,`priority`,`created`) +) DEFAULT COLLATE utf8mb4_general_ci COMMENT='Background tasks queue entries'; + -- -- TABLE delayed-post -- CREATE TABLE IF NOT EXISTS `delayed-post` ( `id` int unsigned NOT NULL auto_increment, `uri` varchar(255) COMMENT 'URI of the post that will be distributed later', + `title` varchar(255) COMMENT 'post title', + `body` mediumtext COMMENT 'post body content', + `private` tinyint unsigned COMMENT '0=public, 1=private, 2=unlisted', + `wid` int unsigned COMMENT 'Workerqueue id', `uid` mediumint unsigned COMMENT 'Owner User id', `delayed` datetime COMMENT 'delay time', PRIMARY KEY(`id`), UNIQUE INDEX `uid_uri` (`uid`,`uri`(190)), + INDEX `wid` (`wid`), + FOREIGN KEY (`wid`) REFERENCES `workerqueue` (`id`) ON UPDATE RESTRICT ON DELETE CASCADE, FOREIGN KEY (`uid`) REFERENCES `user` (`uid`) ON UPDATE RESTRICT ON DELETE CASCADE ) DEFAULT COLLATE utf8mb4_general_ci COMMENT='Posts that are about to be distributed at a later time'; @@ -1474,31 +1505,6 @@ CREATE TABLE IF NOT EXISTS `worker-ipc` ( PRIMARY KEY(`key`) ) ENGINE=MEMORY DEFAULT COLLATE utf8mb4_general_ci COMMENT='Inter process communication between the frontend and the worker'; --- --- TABLE workerqueue --- -CREATE TABLE IF NOT EXISTS `workerqueue` ( - `id` int unsigned NOT NULL auto_increment COMMENT 'Auto incremented worker task id', - `command` varchar(100) COMMENT 'Task command', - `parameter` mediumtext COMMENT 'Task parameter', - `priority` tinyint unsigned NOT NULL DEFAULT 0 COMMENT 'Task priority', - `created` datetime NOT NULL DEFAULT '0001-01-01 00:00:00' COMMENT 'Creation date', - `pid` int unsigned NOT NULL DEFAULT 0 COMMENT 'Process id of the worker', - `executed` datetime NOT NULL DEFAULT '0001-01-01 00:00:00' COMMENT 'Execution date', - `next_try` datetime NOT NULL DEFAULT '0001-01-01 00:00:00' COMMENT 'Next retrial date', - `retrial` tinyint NOT NULL DEFAULT 0 COMMENT 'Retrial counter', - `done` boolean NOT NULL DEFAULT '0' COMMENT 'Marked 1 when the task was done - will be deleted later', - PRIMARY KEY(`id`), - INDEX `command` (`command`), - INDEX `done_command_parameter` (`done`,`command`,`parameter`(64)), - INDEX `done_executed` (`done`,`executed`), - INDEX `done_priority_retrial_created` (`done`,`priority`,`retrial`,`created`), - INDEX `done_priority_next_try` (`done`,`priority`,`next_try`), - INDEX `done_pid_next_try` (`done`,`pid`,`next_try`), - INDEX `done_pid_retrial` (`done`,`pid`,`retrial`), - INDEX `done_pid_priority_created` (`done`,`pid`,`priority`,`created`) -) DEFAULT COLLATE utf8mb4_general_ci COMMENT='Background tasks queue entries'; - -- -- VIEW application-view -- diff --git a/doc/database/db_delayed-post.md b/doc/database/db_delayed-post.md index fe3251f70a..77cd677f65 100644 --- a/doc/database/db_delayed-post.md +++ b/doc/database/db_delayed-post.md @@ -10,6 +10,10 @@ Fields | ------- | ---------------------------------------------- | ------------------ | ---- | --- | ------- | -------------- | | id | | int unsigned | NO | PRI | NULL | auto_increment | | uri | URI of the post that will be distributed later | varchar(255) | YES | | NULL | | +| title | post title | varchar(255) | YES | | NULL | | +| body | post body content | mediumtext | YES | | NULL | | +| private | 0=public, 1=private, 2=unlisted | tinyint unsigned | YES | | NULL | | +| wid | Workerqueue id | int unsigned | YES | | NULL | | | uid | Owner User id | mediumint unsigned | YES | | NULL | | | delayed | delay time | datetime | YES | | NULL | | @@ -20,12 +24,14 @@ Indexes | ------- | --------------------- | | PRIMARY | id | | uid_uri | UNIQUE, uid, uri(190) | +| wid | wid | Foreign Keys ------------ | Field | Target Table | Target Field | |-------|--------------|--------------| +| wid | [workerqueue](help/database/db_workerqueue) | id | | uid | [user](help/database/db_user) | uid | Return to [database documentation](help/database) diff --git a/src/Core/Worker.php b/src/Core/Worker.php index 824275fa74..307451c052 100644 --- a/src/Core/Worker.php +++ b/src/Core/Worker.php @@ -1200,7 +1200,7 @@ class Worker * or: Worker::add(PRIORITY_HIGH, "Notifier", Delivery::DELETION, $drop_id); * or: Worker::add(array('priority' => PRIORITY_HIGH, 'dont_fork' => true), "Delivery", $post_id); * - * @return boolean "false" if worker queue entry already existed or there had been an error + * @return int "0" if worker queue entry already existed or there had been an error, otherwise the ID of the worker task * @throws \Friendica\Network\HTTPException\InternalServerErrorException * @note $cmd and string args are surrounded with "" * @@ -1213,14 +1213,14 @@ class Worker $args = func_get_args(); if (!count($args)) { - return false; + return 0; } $arr = ['args' => $args, 'run_cmd' => true]; Hook::callAll("proc_run", $arr); if (!$arr['run_cmd'] || !count($args)) { - return true; + return 1; } $priority = PRIORITY_MEDIUM; @@ -1255,7 +1255,7 @@ class Worker $command = array_shift($args); $parameters = json_encode($args); $found = DBA::exists('workerqueue', ['command' => $command, 'parameter' => $parameters, 'done' => false]); - $added = false; + $added = 0; if (!in_array($priority, PRIORITIES)) { Logger::warning('Invalid priority', ['priority' => $priority, 'command' => $command, 'callstack' => System::callstack(20)]); @@ -1264,15 +1264,15 @@ class Worker // Quit if there was a database error - a precaution for the update process to 3.5.3 if (DBA::errorNo() != 0) { - return false; + return 0; } if (!$found) { - $added = DBA::insert('workerqueue', ['command' => $command, 'parameter' => $parameters, 'created' => $created, - 'priority' => $priority, 'next_try' => $delayed]); - if (!$added) { - return false; + if (!DBA::insert('workerqueue', ['command' => $command, 'parameter' => $parameters, 'created' => $created, + 'priority' => $priority, 'next_try' => $delayed])) { + return 0; } + $added = DBA::lastInsertId(); } elseif ($force_priority) { DBA::update('workerqueue', ['priority' => $priority], ['command' => $command, 'parameter' => $parameters, 'done' => false, 'pid' => 0]); } diff --git a/src/Model/Post/Delayed.php b/src/Model/Post/Delayed.php index 2097f0f537..8d631a285d 100644 --- a/src/Model/Post/Delayed.php +++ b/src/Model/Post/Delayed.php @@ -64,13 +64,24 @@ class Delayed Logger::notice('Adding post for delayed publishing', ['uid' => $item['uid'], 'delayed' => $delayed, 'uri' => $uri]); - if (!Worker::add(['priority' => PRIORITY_HIGH, 'delayed' => $delayed], 'DelayedPublish', $item, $notify, $taglist, $attachments, $unprepared, $uri)) { + $wid = Worker::add(['priority' => PRIORITY_HIGH, 'delayed' => $delayed], 'DelayedPublish', $item, $notify, $taglist, $attachments, $unprepared, $uri); + if (!$wid) { return false; } DI::pConfig()->set($item['uid'], 'system', 'last_publish', $next_publish); - return DBA::insert('delayed-post', ['uri' => $uri, 'uid' => $item['uid'], 'delayed' => $delayed], Database::INSERT_IGNORE); + $delayed_post = [ + 'uri' => $uri, + 'title' => $item['title'], + 'body' => $item['body'], + 'private' => $item['private'], + 'wid' => $item['wid'], + 'uid' => $item['uid'], + 'delayed' => $delayed, + ]; + + return DBA::insert('delayed-post', $delayed_post, Database::INSERT_IGNORE); } /** diff --git a/static/dbstructure.config.php b/static/dbstructure.config.php index ded539af50..0376315e3f 100644 --- a/static/dbstructure.config.php +++ b/static/dbstructure.config.php @@ -55,7 +55,7 @@ use Friendica\Database\DBA; if (!defined('DB_UPDATE_VERSION')) { - define('DB_UPDATE_VERSION', 1430); + define('DB_UPDATE_VERSION', 1431); } return [ @@ -554,17 +554,48 @@ return [ "received" => ["received"], ] ], + "workerqueue" => [ + "comment" => "Background tasks queue entries", + "fields" => [ + "id" => ["type" => "int unsigned", "not null" => "1", "extra" => "auto_increment", "primary" => "1", "comment" => "Auto incremented worker task id"], + "command" => ["type" => "varchar(100)", "comment" => "Task command"], + "parameter" => ["type" => "mediumtext", "comment" => "Task parameter"], + "priority" => ["type" => "tinyint unsigned", "not null" => "1", "default" => "0", "comment" => "Task priority"], + "created" => ["type" => "datetime", "not null" => "1", "default" => DBA::NULL_DATETIME, "comment" => "Creation date"], + "pid" => ["type" => "int unsigned", "not null" => "1", "default" => "0", "comment" => "Process id of the worker"], + "executed" => ["type" => "datetime", "not null" => "1", "default" => DBA::NULL_DATETIME, "comment" => "Execution date"], + "next_try" => ["type" => "datetime", "not null" => "1", "default" => DBA::NULL_DATETIME, "comment" => "Next retrial date"], + "retrial" => ["type" => "tinyint", "not null" => "1", "default" => "0", "comment" => "Retrial counter"], + "done" => ["type" => "boolean", "not null" => "1", "default" => "0", "comment" => "Marked 1 when the task was done - will be deleted later"], + ], + "indexes" => [ + "PRIMARY" => ["id"], + "command" => ["command"], + "done_command_parameter" => ["done", "command", "parameter(64)"], + "done_executed" => ["done", "executed"], + "done_priority_retrial_created" => ["done", "priority", "retrial", "created"], + "done_priority_next_try" => ["done", "priority", "next_try"], + "done_pid_next_try" => ["done", "pid", "next_try"], + "done_pid_retrial" => ["done", "pid", "retrial"], + "done_pid_priority_created" => ["done", "pid", "priority", "created"] + ] + ], "delayed-post" => [ "comment" => "Posts that are about to be distributed at a later time", "fields" => [ "id" => ["type" => "int unsigned", "not null" => "1", "extra" => "auto_increment", "primary" => "1"], "uri" => ["type" => "varchar(255)", "comment" => "URI of the post that will be distributed later"], + "title" => ["type" => "varchar(255)", "comment" => "post title"], + "body" => ["type" => "mediumtext", "comment" => "post body content"], + "private" => ["type" => "tinyint unsigned", "comment" => "0=public, 1=private, 2=unlisted"], + "wid" => ["type" => "int unsigned", "foreign" => ["workerqueue" => "id"], "comment" => "Workerqueue id"], "uid" => ["type" => "mediumint unsigned", "foreign" => ["user" => "uid"], "comment" => "Owner User id"], "delayed" => ["type" => "datetime", "comment" => "delay time"], ], "indexes" => [ "PRIMARY" => ["id"], "uid_uri" => ["UNIQUE", "uid", "uri(190)"], + "wid" => ["wid"], ] ], "diaspora-interaction" => [ @@ -1496,30 +1527,4 @@ return [ ], "engine" => "MEMORY", ], - "workerqueue" => [ - "comment" => "Background tasks queue entries", - "fields" => [ - "id" => ["type" => "int unsigned", "not null" => "1", "extra" => "auto_increment", "primary" => "1", "comment" => "Auto incremented worker task id"], - "command" => ["type" => "varchar(100)", "comment" => "Task command"], - "parameter" => ["type" => "mediumtext", "comment" => "Task parameter"], - "priority" => ["type" => "tinyint unsigned", "not null" => "1", "default" => "0", "comment" => "Task priority"], - "created" => ["type" => "datetime", "not null" => "1", "default" => DBA::NULL_DATETIME, "comment" => "Creation date"], - "pid" => ["type" => "int unsigned", "not null" => "1", "default" => "0", "comment" => "Process id of the worker"], - "executed" => ["type" => "datetime", "not null" => "1", "default" => DBA::NULL_DATETIME, "comment" => "Execution date"], - "next_try" => ["type" => "datetime", "not null" => "1", "default" => DBA::NULL_DATETIME, "comment" => "Next retrial date"], - "retrial" => ["type" => "tinyint", "not null" => "1", "default" => "0", "comment" => "Retrial counter"], - "done" => ["type" => "boolean", "not null" => "1", "default" => "0", "comment" => "Marked 1 when the task was done - will be deleted later"], - ], - "indexes" => [ - "PRIMARY" => ["id"], - "command" => ["command"], - "done_command_parameter" => ["done", "command", "parameter(64)"], - "done_executed" => ["done", "executed"], - "done_priority_retrial_created" => ["done", "priority", "retrial", "created"], - "done_priority_next_try" => ["done", "priority", "next_try"], - "done_pid_next_try" => ["done", "pid", "next_try"], - "done_pid_retrial" => ["done", "pid", "retrial"], - "done_pid_priority_created" => ["done", "pid", "priority", "created"] - ] - ], ]; From fbbe9a3c1f0d5587068993d052f9910b51ed9966 Mon Sep 17 00:00:00 2001 From: Michael Date: Thu, 29 Jul 2021 03:57:37 +0000 Subject: [PATCH 02/18] "wid" is not an item field --- src/Model/Post/Delayed.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Model/Post/Delayed.php b/src/Model/Post/Delayed.php index 8d631a285d..d75c380f3f 100644 --- a/src/Model/Post/Delayed.php +++ b/src/Model/Post/Delayed.php @@ -76,7 +76,7 @@ class Delayed 'title' => $item['title'], 'body' => $item['body'], 'private' => $item['private'], - 'wid' => $item['wid'], + 'wid' => $wid, 'uid' => $item['uid'], 'delayed' => $delayed, ]; From 6ea3d4aa61f6e62dae92ca4054ebdc67df2148a7 Mon Sep 17 00:00:00 2001 From: Michael Date: Thu, 29 Jul 2021 10:34:31 +0000 Subject: [PATCH 03/18] Added API calls, removed fields --- database.sql | 9 +- doc/database/db_delayed-post.md | 7 +- src/DI.php | 8 ++ src/Factory/Api/Mastodon/ScheduledStatus.php | 59 +++++++++++++ src/Model/Post/Delayed.php | 39 ++++++++- src/Module/Api/Mastodon/ScheduledStatuses.php | 15 ++++ src/Object/Api/Mastodon/ScheduledStatus.php | 83 +++++++++++++++++++ static/dbstructure.config.php | 5 +- static/routes.config.php | 2 +- 9 files changed, 207 insertions(+), 20 deletions(-) create mode 100644 src/Factory/Api/Mastodon/ScheduledStatus.php create mode 100644 src/Object/Api/Mastodon/ScheduledStatus.php diff --git a/database.sql b/database.sql index da39341f15..1c361b6762 100644 --- a/database.sql +++ b/database.sql @@ -523,17 +523,14 @@ CREATE TABLE IF NOT EXISTS `workerqueue` ( CREATE TABLE IF NOT EXISTS `delayed-post` ( `id` int unsigned NOT NULL auto_increment, `uri` varchar(255) COMMENT 'URI of the post that will be distributed later', - `title` varchar(255) COMMENT 'post title', - `body` mediumtext COMMENT 'post body content', - `private` tinyint unsigned COMMENT '0=public, 1=private, 2=unlisted', - `wid` int unsigned COMMENT 'Workerqueue id', `uid` mediumint unsigned COMMENT 'Owner User id', `delayed` datetime COMMENT 'delay time', + `wid` int unsigned COMMENT 'Workerqueue id', PRIMARY KEY(`id`), UNIQUE INDEX `uid_uri` (`uid`,`uri`(190)), INDEX `wid` (`wid`), - FOREIGN KEY (`wid`) REFERENCES `workerqueue` (`id`) ON UPDATE RESTRICT ON DELETE CASCADE, - FOREIGN KEY (`uid`) REFERENCES `user` (`uid`) ON UPDATE RESTRICT ON DELETE CASCADE + FOREIGN KEY (`uid`) REFERENCES `user` (`uid`) ON UPDATE RESTRICT ON DELETE CASCADE, + FOREIGN KEY (`wid`) REFERENCES `workerqueue` (`id`) ON UPDATE RESTRICT ON DELETE CASCADE ) DEFAULT COLLATE utf8mb4_general_ci COMMENT='Posts that are about to be distributed at a later time'; -- diff --git a/doc/database/db_delayed-post.md b/doc/database/db_delayed-post.md index 77cd677f65..b792f2f005 100644 --- a/doc/database/db_delayed-post.md +++ b/doc/database/db_delayed-post.md @@ -10,12 +10,9 @@ Fields | ------- | ---------------------------------------------- | ------------------ | ---- | --- | ------- | -------------- | | id | | int unsigned | NO | PRI | NULL | auto_increment | | uri | URI of the post that will be distributed later | varchar(255) | YES | | NULL | | -| title | post title | varchar(255) | YES | | NULL | | -| body | post body content | mediumtext | YES | | NULL | | -| private | 0=public, 1=private, 2=unlisted | tinyint unsigned | YES | | NULL | | -| wid | Workerqueue id | int unsigned | YES | | NULL | | | uid | Owner User id | mediumint unsigned | YES | | NULL | | | delayed | delay time | datetime | YES | | NULL | | +| wid | Workerqueue id | int unsigned | YES | | NULL | | Indexes ------------ @@ -31,7 +28,7 @@ Foreign Keys | Field | Target Table | Target Field | |-------|--------------|--------------| -| wid | [workerqueue](help/database/db_workerqueue) | id | | uid | [user](help/database/db_user) | uid | +| wid | [workerqueue](help/database/db_workerqueue) | id | Return to [database documentation](help/database) diff --git a/src/DI.php b/src/DI.php index d8e51b4398..0bfaacf89a 100644 --- a/src/DI.php +++ b/src/DI.php @@ -311,6 +311,14 @@ abstract class DI return self::$dice->create(Factory\Api\Mastodon\Status::class); } + /** + * @return Factory\Api\Mastodon\ScheduledStatus + */ + public static function mstdnScheduledStatus() + { + return self::$dice->create(Factory\Api\Mastodon\ScheduledStatus::class); + } + /** * @return Factory\Api\Mastodon\ListEntity */ diff --git a/src/Factory/Api/Mastodon/ScheduledStatus.php b/src/Factory/Api/Mastodon/ScheduledStatus.php new file mode 100644 index 0000000000..f0d846fc00 --- /dev/null +++ b/src/Factory/Api/Mastodon/ScheduledStatus.php @@ -0,0 +1,59 @@ +. + * + */ + +namespace Friendica\Factory\Api\Mastodon; + +use Friendica\BaseFactory; +use Friendica\Database\Database; +use Friendica\Model\Post; +use Friendica\Network\HTTPException; +use Psr\Log\LoggerInterface; + +class ScheduledStatus extends BaseFactory +{ + /** @var Database */ + private $dba; + + public function __construct(LoggerInterface $logger, Database $dba) + { + parent::__construct($logger); + $this->dba = $dba; + } + + /** + * @param int $id Id of the delayed post + * @param int $uid Post user + * + * @return \Friendica\Object\Api\Mastodon\ScheduledStatus + * @throws HTTPException\InternalServerErrorException + */ + public function createFromId(int $id, int $uid): \Friendica\Object\Api\Mastodon\ScheduledStatus + { + $delayed_post = $this->dba->selectFirst('delayed-post', [], ['id' => $id, 'uid' => $uid]); + if (empty($delayed_post)) { + throw new HTTPException\NotFoundException('Scheduled status with ID ' . $id . ' not found for user ' . $uid . '.'); + } + + $parameters = Post\Delayed::getParametersForid($delayed_post['id']); + + return new \Friendica\Object\Api\Mastodon\ScheduledStatus($delayed_post, $parameters); + } +} diff --git a/src/Model/Post/Delayed.php b/src/Model/Post/Delayed.php index d75c380f3f..5af32d52ef 100644 --- a/src/Model/Post/Delayed.php +++ b/src/Model/Post/Delayed.php @@ -73,12 +73,9 @@ class Delayed $delayed_post = [ 'uri' => $uri, - 'title' => $item['title'], - 'body' => $item['body'], - 'private' => $item['private'], - 'wid' => $wid, 'uid' => $item['uid'], 'delayed' => $delayed, + 'wid' => $wid, ]; return DBA::insert('delayed-post', $delayed_post, Database::INSERT_IGNORE); @@ -110,6 +107,40 @@ class Delayed return DBA::exists('delayed-post', ['uri' => $uri, 'uid' => $uid]); } + /** + * Fetch parameters for delayed posts + * + * @param integer $id + * @return array + */ + public static function getParametersForid(int $id) + { + $delayed = DBA::selectFirst('delayed-post', ['id', 'wid', 'delayed'], ['id' => $id]); + if (empty($delayed['wid'])) { + return []; + } + + $worker = DBA::selectFirst('workerqueue', ['parameter'], ['id' => $delayed['wid'], 'command' => 'DelayedPublish']); + if (empty($worker)) { + return []; + } + + $parameters = json_decode($worker['parameter']); + if (empty($parameters)) { + return []; + } + + return [ + 'parameters' => $delayed, + 'item' => $parameters[0], + 'notify' => $parameters[1], + 'taglist' => $parameters[2], + 'attachments' => $parameters[3], + 'unprepared' => $parameters[4], + 'uri' => $parameters[5], + ]; + } + /** * Publish a delayed post * diff --git a/src/Module/Api/Mastodon/ScheduledStatuses.php b/src/Module/Api/Mastodon/ScheduledStatuses.php index 28f174da18..fbf451e6ca 100644 --- a/src/Module/Api/Mastodon/ScheduledStatuses.php +++ b/src/Module/Api/Mastodon/ScheduledStatuses.php @@ -22,6 +22,7 @@ namespace Friendica\Module\Api\Mastodon; use Friendica\Core\System; +use Friendica\DI; use Friendica\Module\BaseApi; /** @@ -35,6 +36,20 @@ class ScheduledStatuses extends BaseApi */ public static function rawContent(array $parameters = []) { + self::checkAllowedScope(self::SCOPE_READ); + $uid = self::getCurrentUserID(); + + if (isset($parameters['id'])) { + System::jsonExit(DI::mstdnScheduledStatus()->createFromId($parameters['id'], $uid)); + } + + $request = self::getRequest([ + 'limit' => 20, // Max number of results to return. Defaults to 20. + 'max_id' => 0, // Return results older than ID + 'since_id' => 0, // Return results newer than ID + 'min_id' => 0, // Return results immediately newer than ID + ]); + System::jsonExit([]); } } diff --git a/src/Object/Api/Mastodon/ScheduledStatus.php b/src/Object/Api/Mastodon/ScheduledStatus.php new file mode 100644 index 0000000000..3e78ce69a6 --- /dev/null +++ b/src/Object/Api/Mastodon/ScheduledStatus.php @@ -0,0 +1,83 @@ +. + * + */ + +namespace Friendica\Object\Api\Mastodon; + +use Friendica\BaseDataTransferObject; +use Friendica\Content\Text\BBCode; +use Friendica\Util\DateTimeFormat; + +/** + * Class ScheduledStatus + * + * @see https://docs.joinmastodon.org/entities/scheduledstatus + */ +class ScheduledStatus extends BaseDataTransferObject +{ + /** @var string */ + protected $id; + /** @var string (Datetime) */ + protected $scheduled_at; + /** @var array */ + protected $params = [ + 'text' => '', + 'media_ids' => null, + 'sensitive' => null, + 'spoiler_text' => null, + 'visibility' => '', + 'scheduled_at' => null, + 'poll' => null, + 'idempotency' => null, + 'in_reply_to_id' => null, + 'application_id' => '' + ]; + /** @var Attachment */ + protected $media_attachments = []; + + /** + * Creates a status record from a delayed-post record. + * + * @param array $delayed_post + * @throws \Friendica\Network\HTTPException\InternalServerErrorException + */ + public function __construct(array $delayed_post, array $parameters) + { + $visibility = ['public', 'private', 'unlisted']; + + $this->id = (string)$delayed_post['uri-id']; + $this->scheduled_at = DateTimeFormat::utc($delayed_post['scheduled_at'], DateTimeFormat::JSON); + + $this->params = [ + 'text' => BBCode::convert(BBCode::setMentionsToNicknames($parameters['item']['body'] ?? ''), false, BBCode::API), + 'media_ids' => null, + 'sensitive' => null, + 'spoiler_text' => $parameters['item']['title'] ?? '', + 'visibility' => $visibility[$parameters['item']['private']], + 'scheduled_at' => $this->scheduled_at, + 'poll' => null, + 'idempotency' => null, + 'in_reply_to_id' => null, + 'application_id' => '' + ]; + + $this->media_attachments = []; + } +} diff --git a/static/dbstructure.config.php b/static/dbstructure.config.php index 0376315e3f..d1cc42033a 100644 --- a/static/dbstructure.config.php +++ b/static/dbstructure.config.php @@ -585,12 +585,9 @@ return [ "fields" => [ "id" => ["type" => "int unsigned", "not null" => "1", "extra" => "auto_increment", "primary" => "1"], "uri" => ["type" => "varchar(255)", "comment" => "URI of the post that will be distributed later"], - "title" => ["type" => "varchar(255)", "comment" => "post title"], - "body" => ["type" => "mediumtext", "comment" => "post body content"], - "private" => ["type" => "tinyint unsigned", "comment" => "0=public, 1=private, 2=unlisted"], - "wid" => ["type" => "int unsigned", "foreign" => ["workerqueue" => "id"], "comment" => "Workerqueue id"], "uid" => ["type" => "mediumint unsigned", "foreign" => ["user" => "uid"], "comment" => "Owner User id"], "delayed" => ["type" => "datetime", "comment" => "delay time"], + "wid" => ["type" => "int unsigned", "foreign" => ["workerqueue" => "id"], "comment" => "Workerqueue id"], ], "indexes" => [ "PRIMARY" => ["id"], diff --git a/static/routes.config.php b/static/routes.config.php index 74cc59df81..87a8242d8f 100644 --- a/static/routes.config.php +++ b/static/routes.config.php @@ -128,7 +128,7 @@ return [ '/push/subscription' => [Module\Api\Mastodon\Unimplemented::class, [R::GET, R::POST, R::PUT, R::DELETE]], // not supported '/reports' => [Module\Api\Mastodon\Unimplemented::class, [ R::POST]], // not supported '/scheduled_statuses' => [Module\Api\Mastodon\ScheduledStatuses::class, [R::GET ]], // Dummy, not supported - '/scheduled_statuses/{id:\d+}' => [Module\Api\Mastodon\Unimplemented::class, [R::GET, R::PUT, R::DELETE]], // not supported + '/scheduled_statuses/{id:\d+}' => [Module\Api\Mastodon\ScheduledStatuses::class, [R::GET, R::PUT, R::DELETE]], '/statuses' => [Module\Api\Mastodon\Statuses::class, [ R::POST]], '/statuses/{id:\d+}' => [Module\Api\Mastodon\Statuses::class, [R::GET, R::DELETE]], '/statuses/{id:\d+}/card' => [Module\Api\Mastodon\Statuses\Card::class, [R::GET ]], From 20e69c1e719259ae7571cc29ad2c2e3359a361f4 Mon Sep 17 00:00:00 2001 From: Michael Date: Thu, 29 Jul 2021 10:46:40 +0000 Subject: [PATCH 04/18] Return as array --- src/Model/Post/Delayed.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Model/Post/Delayed.php b/src/Model/Post/Delayed.php index 5af32d52ef..9d4fa69022 100644 --- a/src/Model/Post/Delayed.php +++ b/src/Model/Post/Delayed.php @@ -125,7 +125,7 @@ class Delayed return []; } - $parameters = json_decode($worker['parameter']); + $parameters = json_decode($worker['parameter'], true); if (empty($parameters)) { return []; } From a1828430e5bac275fd3d1a075a165acf68230d29 Mon Sep 17 00:00:00 2001 From: Michael Date: Thu, 29 Jul 2021 14:58:04 +0000 Subject: [PATCH 05/18] Fixing variables --- src/Model/Post/Delayed.php | 2 +- src/Module/Api/Mastodon/ScheduledStatuses.php | 2 +- src/Object/Api/Mastodon/ScheduledStatus.php | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Model/Post/Delayed.php b/src/Model/Post/Delayed.php index 9d4fa69022..65d29188b5 100644 --- a/src/Model/Post/Delayed.php +++ b/src/Model/Post/Delayed.php @@ -115,7 +115,7 @@ class Delayed */ public static function getParametersForid(int $id) { - $delayed = DBA::selectFirst('delayed-post', ['id', 'wid', 'delayed'], ['id' => $id]); + $delayed = DBA::selectFirst('delayed-post', ['id', 'uid', 'wid', 'delayed'], ['id' => $id]); if (empty($delayed['wid'])) { return []; } diff --git a/src/Module/Api/Mastodon/ScheduledStatuses.php b/src/Module/Api/Mastodon/ScheduledStatuses.php index fbf451e6ca..1c8a222823 100644 --- a/src/Module/Api/Mastodon/ScheduledStatuses.php +++ b/src/Module/Api/Mastodon/ScheduledStatuses.php @@ -40,7 +40,7 @@ class ScheduledStatuses extends BaseApi $uid = self::getCurrentUserID(); if (isset($parameters['id'])) { - System::jsonExit(DI::mstdnScheduledStatus()->createFromId($parameters['id'], $uid)); + System::jsonExit(DI::mstdnScheduledStatus()->createFromId($parameters['id'], $uid)->toArray()); } $request = self::getRequest([ diff --git a/src/Object/Api/Mastodon/ScheduledStatus.php b/src/Object/Api/Mastodon/ScheduledStatus.php index 3e78ce69a6..f955869357 100644 --- a/src/Object/Api/Mastodon/ScheduledStatus.php +++ b/src/Object/Api/Mastodon/ScheduledStatus.php @@ -62,7 +62,7 @@ class ScheduledStatus extends BaseDataTransferObject { $visibility = ['public', 'private', 'unlisted']; - $this->id = (string)$delayed_post['uri-id']; + $this->id = (string)$delayed_post['id']; $this->scheduled_at = DateTimeFormat::utc($delayed_post['scheduled_at'], DateTimeFormat::JSON); $this->params = [ From c9eca1edeb44780ecab0aa334970ed4bf732ff32 Mon Sep 17 00:00:00 2001 From: Michael Date: Thu, 29 Jul 2021 15:01:09 +0000 Subject: [PATCH 06/18] Fix array index --- src/Object/Api/Mastodon/ScheduledStatus.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Object/Api/Mastodon/ScheduledStatus.php b/src/Object/Api/Mastodon/ScheduledStatus.php index f955869357..f63ba8db51 100644 --- a/src/Object/Api/Mastodon/ScheduledStatus.php +++ b/src/Object/Api/Mastodon/ScheduledStatus.php @@ -63,7 +63,7 @@ class ScheduledStatus extends BaseDataTransferObject $visibility = ['public', 'private', 'unlisted']; $this->id = (string)$delayed_post['id']; - $this->scheduled_at = DateTimeFormat::utc($delayed_post['scheduled_at'], DateTimeFormat::JSON); + $this->scheduled_at = DateTimeFormat::utc($delayed_post['delayed'], DateTimeFormat::JSON); $this->params = [ 'text' => BBCode::convert(BBCode::setMentionsToNicknames($parameters['item']['body'] ?? ''), false, BBCode::API), From 432206bc5b590faae3eab3509e097d8235493d64 Mon Sep 17 00:00:00 2001 From: Michael Date: Fri, 30 Jul 2021 06:19:02 +0000 Subject: [PATCH 07/18] Use router constants --- .../Api/Mastodon/Accounts/UpdateCredentials.php | 3 ++- src/Module/Api/Mastodon/Filters.php | 3 ++- src/Module/Api/Mastodon/Lists/Accounts.php | 7 ++++--- src/Module/Api/Mastodon/Markers.php | 3 ++- src/Module/Api/Mastodon/Unimplemented.php | 11 ++++++----- 5 files changed, 16 insertions(+), 11 deletions(-) diff --git a/src/Module/Api/Mastodon/Accounts/UpdateCredentials.php b/src/Module/Api/Mastodon/Accounts/UpdateCredentials.php index d609cdaaeb..5051aec4ec 100644 --- a/src/Module/Api/Mastodon/Accounts/UpdateCredentials.php +++ b/src/Module/Api/Mastodon/Accounts/UpdateCredentials.php @@ -21,6 +21,7 @@ namespace Friendica\Module\Api\Mastodon\Accounts; +use Friendica\App\Router; use Friendica\Core\Logger; use Friendica\Module\BaseApi; use Friendica\Util\HTTPInputData; @@ -39,6 +40,6 @@ class UpdateCredentials extends BaseApi Logger::info('Patch data', ['data' => $data]); - self::unsupported('patch'); + self::unsupported(Router::PATCH); } } diff --git a/src/Module/Api/Mastodon/Filters.php b/src/Module/Api/Mastodon/Filters.php index 6c15066478..2b505e0f29 100644 --- a/src/Module/Api/Mastodon/Filters.php +++ b/src/Module/Api/Mastodon/Filters.php @@ -21,6 +21,7 @@ namespace Friendica\Module\Api\Mastodon; +use Friendica\App\Router; use Friendica\Core\System; use Friendica\Module\BaseApi; @@ -33,7 +34,7 @@ class Filters extends BaseApi { self::checkAllowedScope(self::SCOPE_WRITE); - self::unsupported('post'); + self::unsupported(Router::POST); } /** diff --git a/src/Module/Api/Mastodon/Lists/Accounts.php b/src/Module/Api/Mastodon/Lists/Accounts.php index 6e10ad6bfa..013a9f4aa9 100644 --- a/src/Module/Api/Mastodon/Lists/Accounts.php +++ b/src/Module/Api/Mastodon/Lists/Accounts.php @@ -21,6 +21,7 @@ namespace Friendica\Module\Api\Mastodon\Lists; +use Friendica\App\Router; use Friendica\Core\System; use Friendica\Database\DBA; use Friendica\DI; @@ -35,12 +36,12 @@ class Accounts extends BaseApi { public static function delete(array $parameters = []) { - self::unsupported('delete'); + self::unsupported(Router::DELETE); } public static function post(array $parameters = []) { - self::unsupported('post'); + self::unsupported(Router::POST); } /** @@ -74,7 +75,7 @@ class Accounts extends BaseApi if ($request['limit'] != 0) { $params['limit'] = min($request['limit'], 40); } - + $condition = ['gid' => $id]; if (!empty($request['max_id'])) { diff --git a/src/Module/Api/Mastodon/Markers.php b/src/Module/Api/Mastodon/Markers.php index a3a3879e07..50c0864990 100644 --- a/src/Module/Api/Mastodon/Markers.php +++ b/src/Module/Api/Mastodon/Markers.php @@ -21,6 +21,7 @@ namespace Friendica\Module\Api\Mastodon; +use Friendica\App\Router; use Friendica\Core\System; use Friendica\Module\BaseApi; @@ -33,7 +34,7 @@ class Markers extends BaseApi { self::checkAllowedScope(self::SCOPE_WRITE); - self::unsupported('post'); + self::unsupported(Router::POST); } /** diff --git a/src/Module/Api/Mastodon/Unimplemented.php b/src/Module/Api/Mastodon/Unimplemented.php index fa9618818a..035b6d2c95 100644 --- a/src/Module/Api/Mastodon/Unimplemented.php +++ b/src/Module/Api/Mastodon/Unimplemented.php @@ -21,6 +21,7 @@ namespace Friendica\Module\Api\Mastodon; +use Friendica\App\Router; use Friendica\Module\BaseApi; /** @@ -34,7 +35,7 @@ class Unimplemented extends BaseApi */ public static function delete(array $parameters = []) { - self::unsupported('delete'); + self::unsupported(Router::DELETE); } /** @@ -43,7 +44,7 @@ class Unimplemented extends BaseApi */ public static function patch(array $parameters = []) { - self::unsupported('patch'); + self::unsupported(Router::PATCH); } /** @@ -52,7 +53,7 @@ class Unimplemented extends BaseApi */ public static function post(array $parameters = []) { - self::unsupported('post'); + self::unsupported(Router::POST); } /** @@ -61,7 +62,7 @@ class Unimplemented extends BaseApi */ public static function put(array $parameters = []) { - self::unsupported('put'); + self::unsupported(Router::PUT); } /** @@ -70,6 +71,6 @@ class Unimplemented extends BaseApi */ public static function rawContent(array $parameters = []) { - self::unsupported('get'); + self::unsupported(Router::GET); } } From 5e75ba3083cf35f6723d8b8980a5821af4f5a534 Mon Sep 17 00:00:00 2001 From: Michael Date: Fri, 30 Jul 2021 06:19:25 +0000 Subject: [PATCH 08/18] guid function is now public --- src/Model/Item.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Model/Item.php b/src/Model/Item.php index 7cda359003..798886ad48 100644 --- a/src/Model/Item.php +++ b/src/Model/Item.php @@ -363,7 +363,7 @@ class Item return true; } - private static function guid($item, $notify) + public static function guid($item, $notify) { if (!empty($item['guid'])) { return Strings::escapeTags(trim($item['guid'])); From 4137a6250bf95a5ffd793c76e3e890d3783dbaba Mon Sep 17 00:00:00 2001 From: Michael Date: Fri, 30 Jul 2021 06:20:10 +0000 Subject: [PATCH 09/18] Delayed now return their id on insert --- src/Model/Post/Delayed.php | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/src/Model/Post/Delayed.php b/src/Model/Post/Delayed.php index 65d29188b5..49bf77a834 100644 --- a/src/Model/Post/Delayed.php +++ b/src/Model/Post/Delayed.php @@ -43,13 +43,13 @@ class Delayed * @param string $delayed * @param array $taglist * @param array $attachments - * @return bool insert success + * @return int ID of the created delayed post entry */ public static function add(string $uri, array $item, int $notify = 0, bool $unprepared = false, string $delayed = '', array $taglist = [], array $attachments = []) { if (empty($item['uid']) || self::exists($uri, $item['uid'])) { Logger::notice('No uid or already found'); - return false; + return 0; } if (empty($delayed)) { @@ -66,7 +66,7 @@ class Delayed $wid = Worker::add(['priority' => PRIORITY_HIGH, 'delayed' => $delayed], 'DelayedPublish', $item, $notify, $taglist, $attachments, $unprepared, $uri); if (!$wid) { - return false; + return 0; } DI::pConfig()->set($item['uid'], 'system', 'last_publish', $next_publish); @@ -78,7 +78,11 @@ class Delayed 'wid' => $wid, ]; - return DBA::insert('delayed-post', $delayed_post, Database::INSERT_IGNORE); + if (DBA::insert('delayed-post', $delayed_post, Database::INSERT_IGNORE)) { + return DBA::lastInsertId(); + } else { + return 0; + } } /** From bb37c41bd7c481de88ec75ee2b93f85f4b6c5cba Mon Sep 17 00:00:00 2001 From: Michael Date: Fri, 30 Jul 2021 06:22:32 +0000 Subject: [PATCH 10/18] Scheduled posts can now be created --- src/Module/Api/Mastodon/ScheduledStatuses.php | 65 ++++++++++++++++++- src/Module/Api/Mastodon/Statuses.php | 12 +++- 2 files changed, 75 insertions(+), 2 deletions(-) diff --git a/src/Module/Api/Mastodon/ScheduledStatuses.php b/src/Module/Api/Mastodon/ScheduledStatuses.php index 1c8a222823..5e7f4b9547 100644 --- a/src/Module/Api/Mastodon/ScheduledStatuses.php +++ b/src/Module/Api/Mastodon/ScheduledStatuses.php @@ -21,7 +21,9 @@ namespace Friendica\Module\Api\Mastodon; +use Friendica\App\Router; use Friendica\Core\System; +use Friendica\Database\DBA; use Friendica\DI; use Friendica\Module\BaseApi; @@ -30,6 +32,36 @@ use Friendica\Module\BaseApi; */ class ScheduledStatuses extends BaseApi { + public static function put(array $parameters = []) + { + self::checkAllowedScope(self::SCOPE_WRITE); + $uid = self::getCurrentUserID(); + + self::unsupported(Router::PUT); + } + + public static function delete(array $parameters = []) + { + self::checkAllowedScope(self::SCOPE_WRITE); + $uid = self::getCurrentUserID(); + + if (empty($parameters['id'])) { + DI::mstdnError()->UnprocessableEntity(); + } + + $condtion = ['id' => $parameters['id'], 'uid' => $uid]; + $post = DBA::selectFirst('delayed-post', ['id'], $condtion); + if (empty($post['id'])) { + DI::mstdnError()->RecordNotFound(); + } + + if (!DBA::delete('delayed-post', $condtion)) { + DI::mstdnError()->RecordNotFound(); + } + + System::jsonExit([]); + } + /** * @param array $parameters * @throws \Friendica\Network\HTTPException\InternalServerErrorException @@ -50,6 +82,37 @@ class ScheduledStatuses extends BaseApi 'min_id' => 0, // Return results immediately newer than ID ]); - System::jsonExit([]); + $params = ['order' => ['id' => true], 'limit' => $request['limit']]; + + $condition = ["`uid` = ? AND NOT `wid` IS NULL", $uid]; + + if (!empty($request['max_id'])) { + $condition = DBA::mergeConditions($condition, ["`uri-id` < ?", $request['max_id']]); + } + + if (!empty($request['since_id'])) { + $condition = DBA::mergeConditions($condition, ["`uri-id` > ?", $request['since_id']]); + } + + if (!empty($request['min_id'])) { + $condition = DBA::mergeConditions($condition, ["`uri-id` > ?", $request['min_id']]); + $params['order'] = ['uri-id']; + } + + $posts = DBA::select('delayed-post', ['id'], $condition, $params); + + $statuses = []; + while ($post = DBA::fetch($posts)) { + self::setBoundaries($post['id']); + $statuses[] = DI::mstdnScheduledStatus()->createFromId($post['id'], $uid); + } + DBA::close($posts); + + if (!empty($request['min_id'])) { + array_reverse($statuses); + } + + self::setLinkHeader(); + System::jsonExit($statuses); } } diff --git a/src/Module/Api/Mastodon/Statuses.php b/src/Module/Api/Mastodon/Statuses.php index 1007bea0be..40bbca0cf0 100644 --- a/src/Module/Api/Mastodon/Statuses.php +++ b/src/Module/Api/Mastodon/Statuses.php @@ -104,7 +104,7 @@ class Statuses extends BaseApi $item['deny_gid'] = $owner['deny_gid']; } else { $item['allow_cid'] = ''; - $item['allow_gid'] = [Group::FOLLOWERS]; + $item['allow_gid'] = '<' . Group::FOLLOWERS . '>'; $item['deny_cid'] = ''; $item['deny_gid'] = ''; } @@ -183,6 +183,16 @@ class Statuses extends BaseApi } } + if (!empty($request['scheduled_at'])) { + $item['guid'] = Item::guid($item, true); + $item['uri'] = Item::newURI($item['uid'], $item['guid']); + $id = Post\Delayed::add($item['uri'], $item, PRIORITY_HIGH, false, $request['scheduled_at']); + if (empty($id)) { + DI::mstdnError()->InternalError(); + } + System::jsonExit(DI::mstdnScheduledStatus()->createFromId($id, $uid)->toArray()); + } + $id = Item::insert($item, true); if (!empty($id)) { $item = Post::selectFirst(['uri-id'], ['id' => $id]); From aac965be7d33aca1119d6593c71589e7e8179e78 Mon Sep 17 00:00:00 2001 From: Michael Date: Fri, 30 Jul 2021 06:22:52 +0000 Subject: [PATCH 11/18] "tag" is no field anymore --- mod/photos.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mod/photos.php b/mod/photos.php index 132222f046..9e1fa9cede 100644 --- a/mod/photos.php +++ b/mod/photos.php @@ -411,7 +411,7 @@ function photos_post(App $a) } if ($item_id) { - $item = Post::selectFirst(['tag', 'inform', 'uri-id'], ['id' => $item_id, 'uid' => $page_owner_uid]); + $item = Post::selectFirst(['inform', 'uri-id'], ['id' => $item_id, 'uid' => $page_owner_uid]); if (DBA::isResult($item)) { $old_inform = $item['inform']; From 93263a820dd06997030f1cc9e65ed52bc99ea3d1 Mon Sep 17 00:00:00 2001 From: Michael Date: Fri, 30 Jul 2021 10:24:08 +0000 Subject: [PATCH 12/18] Function renamed, documentation added --- src/Factory/Api/Mastodon/ScheduledStatus.php | 5 ++++- src/Module/Api/Mastodon/ScheduledStatuses.php | 4 ++-- src/Module/Api/Mastodon/Statuses.php | 2 +- src/Object/Api/Mastodon/ScheduledStatus.php | 3 ++- 4 files changed, 9 insertions(+), 5 deletions(-) diff --git a/src/Factory/Api/Mastodon/ScheduledStatus.php b/src/Factory/Api/Mastodon/ScheduledStatus.php index f0d846fc00..ce79cc8022 100644 --- a/src/Factory/Api/Mastodon/ScheduledStatus.php +++ b/src/Factory/Api/Mastodon/ScheduledStatus.php @@ -45,7 +45,7 @@ class ScheduledStatus extends BaseFactory * @return \Friendica\Object\Api\Mastodon\ScheduledStatus * @throws HTTPException\InternalServerErrorException */ - public function createFromId(int $id, int $uid): \Friendica\Object\Api\Mastodon\ScheduledStatus + public function createFromDelayedPostId(int $id, int $uid): \Friendica\Object\Api\Mastodon\ScheduledStatus { $delayed_post = $this->dba->selectFirst('delayed-post', [], ['id' => $id, 'uid' => $uid]); if (empty($delayed_post)) { @@ -53,6 +53,9 @@ class ScheduledStatus extends BaseFactory } $parameters = Post\Delayed::getParametersForid($delayed_post['id']); + if (empty($parameters)) { + throw new HTTPException\NotFoundException('Scheduled status with ID ' . $id . ' not found for user ' . $uid . '.'); + } return new \Friendica\Object\Api\Mastodon\ScheduledStatus($delayed_post, $parameters); } diff --git a/src/Module/Api/Mastodon/ScheduledStatuses.php b/src/Module/Api/Mastodon/ScheduledStatuses.php index 5e7f4b9547..fab70738f0 100644 --- a/src/Module/Api/Mastodon/ScheduledStatuses.php +++ b/src/Module/Api/Mastodon/ScheduledStatuses.php @@ -72,7 +72,7 @@ class ScheduledStatuses extends BaseApi $uid = self::getCurrentUserID(); if (isset($parameters['id'])) { - System::jsonExit(DI::mstdnScheduledStatus()->createFromId($parameters['id'], $uid)->toArray()); + System::jsonExit(DI::mstdnScheduledStatus()->createFromDelayedPostId($parameters['id'], $uid)->toArray()); } $request = self::getRequest([ @@ -104,7 +104,7 @@ class ScheduledStatuses extends BaseApi $statuses = []; while ($post = DBA::fetch($posts)) { self::setBoundaries($post['id']); - $statuses[] = DI::mstdnScheduledStatus()->createFromId($post['id'], $uid); + $statuses[] = DI::mstdnScheduledStatus()->createFromDelayedPostId($post['id'], $uid); } DBA::close($posts); diff --git a/src/Module/Api/Mastodon/Statuses.php b/src/Module/Api/Mastodon/Statuses.php index 40bbca0cf0..cd6bbda3f8 100644 --- a/src/Module/Api/Mastodon/Statuses.php +++ b/src/Module/Api/Mastodon/Statuses.php @@ -190,7 +190,7 @@ class Statuses extends BaseApi if (empty($id)) { DI::mstdnError()->InternalError(); } - System::jsonExit(DI::mstdnScheduledStatus()->createFromId($id, $uid)->toArray()); + System::jsonExit(DI::mstdnScheduledStatus()->createFromDelayedPostId($id, $uid)->toArray()); } $id = Item::insert($item, true); diff --git a/src/Object/Api/Mastodon/ScheduledStatus.php b/src/Object/Api/Mastodon/ScheduledStatus.php index f63ba8db51..473cd99a54 100644 --- a/src/Object/Api/Mastodon/ScheduledStatus.php +++ b/src/Object/Api/Mastodon/ScheduledStatus.php @@ -55,7 +55,8 @@ class ScheduledStatus extends BaseDataTransferObject /** * Creates a status record from a delayed-post record. * - * @param array $delayed_post + * @param array $delayed_post Record with the delayed post + * @param array $parameters Parameters for the workerqueue entry for the delayed post * @throws \Friendica\Network\HTTPException\InternalServerErrorException */ public function __construct(array $delayed_post, array $parameters) From 929de9081ea25d8f4ecf62d4aac5ddd33d9aa2ab Mon Sep 17 00:00:00 2001 From: Michael Date: Fri, 30 Jul 2021 13:22:06 +0000 Subject: [PATCH 13/18] All needed fields are now filled --- src/Factory/Api/Mastodon/ScheduledStatus.php | 15 ++++++++++++++- src/Model/Photo.php | 19 +++++++++++++++++-- src/Model/Post/Delayed.php | 6 ++++++ src/Module/Api/Mastodon/ScheduledStatuses.php | 2 +- src/Object/Api/Mastodon/ScheduledStatus.php | 6 +++--- 5 files changed, 41 insertions(+), 7 deletions(-) diff --git a/src/Factory/Api/Mastodon/ScheduledStatus.php b/src/Factory/Api/Mastodon/ScheduledStatus.php index ce79cc8022..f4b805a50a 100644 --- a/src/Factory/Api/Mastodon/ScheduledStatus.php +++ b/src/Factory/Api/Mastodon/ScheduledStatus.php @@ -23,6 +23,8 @@ namespace Friendica\Factory\Api\Mastodon; use Friendica\BaseFactory; use Friendica\Database\Database; +use Friendica\Model\ItemURI; +use Friendica\Model\Photo; use Friendica\Model\Post; use Friendica\Network\HTTPException; use Psr\Log\LoggerInterface; @@ -57,6 +59,17 @@ class ScheduledStatus extends BaseFactory throw new HTTPException\NotFoundException('Scheduled status with ID ' . $id . ' not found for user ' . $uid . '.'); } - return new \Friendica\Object\Api\Mastodon\ScheduledStatus($delayed_post, $parameters); + $media_ids = []; + foreach ($parameters['attachments'] as $attachment) { + $media_ids[] = Photo::getIdForName($attachment['url']); + } + + if (isset($parameters['item']['thr-parent']) && ($parameters['item']['gravity'] ?? GRAVITY_PARENT != GRAVITY_PARENT)) { + $in_reply_to_id = ItemURI::getIdByURI($parameters['item']['thr-parent']); + } else { + $in_reply_to_id= null; + } + + return new \Friendica\Object\Api\Mastodon\ScheduledStatus($delayed_post, $parameters, $media_ids, $in_reply_to_id); } } diff --git a/src/Model/Photo.php b/src/Model/Photo.php index 30e6668987..26369a3540 100644 --- a/src/Model/Photo.php +++ b/src/Model/Photo.php @@ -841,13 +841,28 @@ class Photo * @throws \Exception */ public static function isLocal($name) + { + return (bool)self::getIdForName($name); + } + + /** + * Return the id of a local photo + * + * @param string $name Picture link + * @return int + */ + public static function getIdForName($name) { $data = self::getResourceData($name); if (empty($data)) { - return false; + return 0; } - return DBA::exists('photo', ['resource-id' => $data['guid'], 'scale' => $data['scale']]); + $photo = DBA::selectFirst('photo', ['id'], ['resource-id' => $data['guid'], 'scale' => $data['scale']]); + if (!empty($photo['id'])) { + return $photo['id']; + } + return 0; } /** diff --git a/src/Model/Post/Delayed.php b/src/Model/Post/Delayed.php index 49bf77a834..0bae1a8e2e 100644 --- a/src/Model/Post/Delayed.php +++ b/src/Model/Post/Delayed.php @@ -134,6 +134,12 @@ class Delayed return []; } + // Make sure to only publish the attachments in the dedicated array field + if (empty($parameters[3]) && !empty($parameters[0]['attachments'])) { + $parameters[3] = $parameters[0]['attachments']; + unset($parameters[0]['attachments']); + } + return [ 'parameters' => $delayed, 'item' => $parameters[0], diff --git a/src/Module/Api/Mastodon/ScheduledStatuses.php b/src/Module/Api/Mastodon/ScheduledStatuses.php index fab70738f0..d18e51d9d5 100644 --- a/src/Module/Api/Mastodon/ScheduledStatuses.php +++ b/src/Module/Api/Mastodon/ScheduledStatuses.php @@ -72,7 +72,7 @@ class ScheduledStatuses extends BaseApi $uid = self::getCurrentUserID(); if (isset($parameters['id'])) { - System::jsonExit(DI::mstdnScheduledStatus()->createFromDelayedPostId($parameters['id'], $uid)->toArray()); + System::jsonExit(DI::mstdnScheduledStatus()->createFromDelayedPostId($parameters['id'], $uid)->toArray()); } $request = self::getRequest([ diff --git a/src/Object/Api/Mastodon/ScheduledStatus.php b/src/Object/Api/Mastodon/ScheduledStatus.php index 473cd99a54..e4f702a234 100644 --- a/src/Object/Api/Mastodon/ScheduledStatus.php +++ b/src/Object/Api/Mastodon/ScheduledStatus.php @@ -59,7 +59,7 @@ class ScheduledStatus extends BaseDataTransferObject * @param array $parameters Parameters for the workerqueue entry for the delayed post * @throws \Friendica\Network\HTTPException\InternalServerErrorException */ - public function __construct(array $delayed_post, array $parameters) + public function __construct(array $delayed_post, array $parameters, array $media_ids = null, int $in_reply_to_id = null) { $visibility = ['public', 'private', 'unlisted']; @@ -68,14 +68,14 @@ class ScheduledStatus extends BaseDataTransferObject $this->params = [ 'text' => BBCode::convert(BBCode::setMentionsToNicknames($parameters['item']['body'] ?? ''), false, BBCode::API), - 'media_ids' => null, + 'media_ids' => $media_ids, 'sensitive' => null, 'spoiler_text' => $parameters['item']['title'] ?? '', 'visibility' => $visibility[$parameters['item']['private']], 'scheduled_at' => $this->scheduled_at, 'poll' => null, 'idempotency' => null, - 'in_reply_to_id' => null, + 'in_reply_to_id' => $in_reply_to_id, 'application_id' => '' ]; From 48f279059365a6b140f99a33ba8c953c6c737570 Mon Sep 17 00:00:00 2001 From: Michael Date: Fri, 30 Jul 2021 14:05:57 +0000 Subject: [PATCH 14/18] Spaces removed --- src/Factory/Api/Mastodon/ScheduledStatus.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Factory/Api/Mastodon/ScheduledStatus.php b/src/Factory/Api/Mastodon/ScheduledStatus.php index f4b805a50a..987ba201f6 100644 --- a/src/Factory/Api/Mastodon/ScheduledStatus.php +++ b/src/Factory/Api/Mastodon/ScheduledStatus.php @@ -63,7 +63,7 @@ class ScheduledStatus extends BaseFactory foreach ($parameters['attachments'] as $attachment) { $media_ids[] = Photo::getIdForName($attachment['url']); } - + if (isset($parameters['item']['thr-parent']) && ($parameters['item']['gravity'] ?? GRAVITY_PARENT != GRAVITY_PARENT)) { $in_reply_to_id = ItemURI::getIdByURI($parameters['item']['thr-parent']); } else { From 9b609ba19d053daae30ac2fc2615fe529adeff1b Mon Sep 17 00:00:00 2001 From: Michael Date: Fri, 30 Jul 2021 14:06:18 +0000 Subject: [PATCH 15/18] Space added --- src/Factory/Api/Mastodon/ScheduledStatus.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Factory/Api/Mastodon/ScheduledStatus.php b/src/Factory/Api/Mastodon/ScheduledStatus.php index 987ba201f6..14ff479b92 100644 --- a/src/Factory/Api/Mastodon/ScheduledStatus.php +++ b/src/Factory/Api/Mastodon/ScheduledStatus.php @@ -67,7 +67,7 @@ class ScheduledStatus extends BaseFactory if (isset($parameters['item']['thr-parent']) && ($parameters['item']['gravity'] ?? GRAVITY_PARENT != GRAVITY_PARENT)) { $in_reply_to_id = ItemURI::getIdByURI($parameters['item']['thr-parent']); } else { - $in_reply_to_id= null; + $in_reply_to_id = null; } return new \Friendica\Object\Api\Mastodon\ScheduledStatus($delayed_post, $parameters, $media_ids, $in_reply_to_id); From 13529fa97deed6f38c3852d943ba33fbf1c5f3f4 Mon Sep 17 00:00:00 2001 From: Michael Date: Fri, 30 Jul 2021 22:39:13 +0000 Subject: [PATCH 16/18] Support the "media_attachments" field --- src/Factory/Api/Mastodon/ScheduledStatus.php | 8 ++++++-- src/Object/Api/Mastodon/ScheduledStatus.php | 4 ++-- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/src/Factory/Api/Mastodon/ScheduledStatus.php b/src/Factory/Api/Mastodon/ScheduledStatus.php index 14ff479b92..608d615ca1 100644 --- a/src/Factory/Api/Mastodon/ScheduledStatus.php +++ b/src/Factory/Api/Mastodon/ScheduledStatus.php @@ -23,6 +23,7 @@ namespace Friendica\Factory\Api\Mastodon; use Friendica\BaseFactory; use Friendica\Database\Database; +use Friendica\DI; use Friendica\Model\ItemURI; use Friendica\Model\Photo; use Friendica\Model\Post; @@ -60,8 +61,11 @@ class ScheduledStatus extends BaseFactory } $media_ids = []; + $media_attachments = []; foreach ($parameters['attachments'] as $attachment) { - $media_ids[] = Photo::getIdForName($attachment['url']); + $id = Photo::getIdForName($attachment['url']); + $media_ids[] = (string)$id; + $media_attachments[] = DI::mstdnAttachment()->createFromPhoto($id); } if (isset($parameters['item']['thr-parent']) && ($parameters['item']['gravity'] ?? GRAVITY_PARENT != GRAVITY_PARENT)) { @@ -70,6 +74,6 @@ class ScheduledStatus extends BaseFactory $in_reply_to_id = null; } - return new \Friendica\Object\Api\Mastodon\ScheduledStatus($delayed_post, $parameters, $media_ids, $in_reply_to_id); + return new \Friendica\Object\Api\Mastodon\ScheduledStatus($delayed_post, $parameters, $media_ids, $media_attachments, $in_reply_to_id); } } diff --git a/src/Object/Api/Mastodon/ScheduledStatus.php b/src/Object/Api/Mastodon/ScheduledStatus.php index e4f702a234..4df92cc367 100644 --- a/src/Object/Api/Mastodon/ScheduledStatus.php +++ b/src/Object/Api/Mastodon/ScheduledStatus.php @@ -59,7 +59,7 @@ class ScheduledStatus extends BaseDataTransferObject * @param array $parameters Parameters for the workerqueue entry for the delayed post * @throws \Friendica\Network\HTTPException\InternalServerErrorException */ - public function __construct(array $delayed_post, array $parameters, array $media_ids = null, int $in_reply_to_id = null) + public function __construct(array $delayed_post, array $parameters, array $media_ids = null, array $media_attachments = [], int $in_reply_to_id = null) { $visibility = ['public', 'private', 'unlisted']; @@ -79,6 +79,6 @@ class ScheduledStatus extends BaseDataTransferObject 'application_id' => '' ]; - $this->media_attachments = []; + $this->media_attachments = $media_attachments; } } From 01197f66e586d24e7d46526d6235f3c5456f002a Mon Sep 17 00:00:00 2001 From: Michael Date: Fri, 30 Jul 2021 22:43:11 +0000 Subject: [PATCH 17/18] Improved docs --- doc/API-Mastodon.md | 2 +- static/routes.config.php | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/doc/API-Mastodon.md b/doc/API-Mastodon.md index 2eadc2a1bd..006b8da473 100644 --- a/doc/API-Mastodon.md +++ b/doc/API-Mastodon.md @@ -93,6 +93,7 @@ These endpoints use the [Mastodon API entities](https://docs.joinmastodon.org/en - [`POST /api/v1/notifications/clear`](https://docs.joinmastodon.org/methods/notifications/) - [`POST /api/v1/notifications/:id/dismiss`](https://docs.joinmastodon.org/methods/notifications/) - [`GET /api/v1/preferences`](https://docs.joinmastodon.org/methods/accounts/preferences/) +- [`GET /api/v1/scheduled_statuses`](https://docs.joinmastodon.org/methods/statuses/scheduled_statuses/) - [`GET /api/v1/search`](https://docs.joinmastodon.org/methods/search/) - [`POST /api/v1/statuses`](https://docs.joinmastodon.org/methods/statuses/) - [`GET /api/v1/statuses/:id`](https://docs.joinmastodon.org/methods/statuses/) @@ -137,7 +138,6 @@ They refer to features that don't exist in Friendica yet. - [`GET /api/v1/endorsements`](https://docs.joinmastodon.org/methods/accounts/endorsements/) - [`GET /api/v1/filters`](https://docs.joinmastodon.org/methods/accounts/filters/) - [`GET /api/v1/markers`](https://docs.joinmastodon.org/methods/timelines/markers/) -- [`GET /api/v1/scheduled_statuses`](https://docs.joinmastodon.org/methods/statuses/scheduled_statuses/) ## Non supportable endpoints diff --git a/static/routes.config.php b/static/routes.config.php index 87a8242d8f..40739de7e2 100644 --- a/static/routes.config.php +++ b/static/routes.config.php @@ -127,7 +127,7 @@ return [ '/preferences' => [Module\Api\Mastodon\Preferences::class, [R::GET ]], '/push/subscription' => [Module\Api\Mastodon\Unimplemented::class, [R::GET, R::POST, R::PUT, R::DELETE]], // not supported '/reports' => [Module\Api\Mastodon\Unimplemented::class, [ R::POST]], // not supported - '/scheduled_statuses' => [Module\Api\Mastodon\ScheduledStatuses::class, [R::GET ]], // Dummy, not supported + '/scheduled_statuses' => [Module\Api\Mastodon\ScheduledStatuses::class, [R::GET ]], '/scheduled_statuses/{id:\d+}' => [Module\Api\Mastodon\ScheduledStatuses::class, [R::GET, R::PUT, R::DELETE]], '/statuses' => [Module\Api\Mastodon\Statuses::class, [ R::POST]], '/statuses/{id:\d+}' => [Module\Api\Mastodon\Statuses::class, [R::GET, R::DELETE]], From e89d0f2599396689cfdc472da7f90a45c129d642 Mon Sep 17 00:00:00 2001 From: Michael Date: Sat, 31 Jul 2021 06:22:08 +0000 Subject: [PATCH 18/18] Style --- src/Factory/Api/Mastodon/ScheduledStatus.php | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/Factory/Api/Mastodon/ScheduledStatus.php b/src/Factory/Api/Mastodon/ScheduledStatus.php index 608d615ca1..80dcdfc595 100644 --- a/src/Factory/Api/Mastodon/ScheduledStatus.php +++ b/src/Factory/Api/Mastodon/ScheduledStatus.php @@ -60,11 +60,12 @@ class ScheduledStatus extends BaseFactory throw new HTTPException\NotFoundException('Scheduled status with ID ' . $id . ' not found for user ' . $uid . '.'); } - $media_ids = []; + $media_ids = []; $media_attachments = []; foreach ($parameters['attachments'] as $attachment) { $id = Photo::getIdForName($attachment['url']); - $media_ids[] = (string)$id; + + $media_ids[] = (string)$id; $media_attachments[] = DI::mstdnAttachment()->createFromPhoto($id); }