mirror of
https://git.tt-rss.org/fox/tt-rss.git
synced 2025-08-06 14:17:27 +02:00
expose scheduled tasks to plugins, switch cache_starred_images plugin to use them instead of housekeeping hook
This commit is contained in:
parent
d4faf2d369
commit
fc059fc0fc
@ -38,6 +38,8 @@ class PluginHost {
|
|||||||
|
|
||||||
private static ?PluginHost $instance = null;
|
private static ?PluginHost $instance = null;
|
||||||
|
|
||||||
|
private ?Scheduler $scheduler = null;
|
||||||
|
|
||||||
const API_VERSION = 2;
|
const API_VERSION = 2;
|
||||||
const PUBLIC_METHOD_DELIMITER = "--";
|
const PUBLIC_METHOD_DELIMITER = "--";
|
||||||
|
|
||||||
@ -215,6 +217,7 @@ class PluginHost {
|
|||||||
|
|
||||||
function __construct() {
|
function __construct() {
|
||||||
$this->pdo = Db::pdo();
|
$this->pdo = Db::pdo();
|
||||||
|
$this->scheduler = new Scheduler('PluginHost Scheduler');
|
||||||
$this->storage = [];
|
$this->storage = [];
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -438,6 +441,10 @@ class PluginHost {
|
|||||||
|
|
||||||
$this->owner_uid = (int) $owner_uid;
|
$this->owner_uid = (int) $owner_uid;
|
||||||
|
|
||||||
|
if ($this->owner_uid) {
|
||||||
|
$this->set_scheduler_name("PluginHost Scheduler for UID $owner_uid");
|
||||||
|
}
|
||||||
|
|
||||||
foreach ($plugins as $class) {
|
foreach ($plugins as $class) {
|
||||||
$class = trim($class);
|
$class = trim($class);
|
||||||
$class_file = strtolower(basename(clean($class)));
|
$class_file = strtolower(basename(clean($class)));
|
||||||
@ -907,4 +914,30 @@ class PluginHost {
|
|||||||
return basename(dirname(dirname($ref->getFileName()))) == "plugins.local";
|
return basename(dirname(dirname($ref->getFileName()))) == "plugins.local";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This exposes sheduled tasks functionality to plugins. For system plugins, tasks registered here are
|
||||||
|
* executed (if due) during housekeeping. For user plugins, tasks are only run after any feeds owned by
|
||||||
|
* the user have been processed in an update batch (which means user is not inactive).
|
||||||
|
*
|
||||||
|
* This behaviour mirrors that of `HOOK_HOUSE_KEEPING` for user plugins.
|
||||||
|
*
|
||||||
|
* @see Scheduler::add_scheduled_task()
|
||||||
|
* @see Plugin::hook_house_keeping()
|
||||||
|
*/
|
||||||
|
function add_scheduled_task(Plugin $sender, string $task_name, string $cron_expression, Closure $callback): bool {
|
||||||
|
if ($this->is_system($sender))
|
||||||
|
$task_name = get_class($sender) . ':' . $task_name;
|
||||||
|
else
|
||||||
|
$task_name = get_class($sender) . ':' . $task_name . ':' . $this->owner_uid;
|
||||||
|
|
||||||
|
return $this->scheduler->add_scheduled_task($task_name, $cron_expression, $callback);
|
||||||
|
}
|
||||||
|
|
||||||
|
function run_due_tasks() : void {
|
||||||
|
$this->scheduler->run_due_tasks();
|
||||||
|
}
|
||||||
|
|
||||||
|
private function set_scheduler_name(string $name) : void {
|
||||||
|
$this->scheduler->set_name($name);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -1666,6 +1666,7 @@ class RSSUtils {
|
|||||||
|
|
||||||
UserHelper::load_user_plugins($owner_uid, $tmph);
|
UserHelper::load_user_plugins($owner_uid, $tmph);
|
||||||
|
|
||||||
|
$tmph->run_due_tasks();
|
||||||
$tmph->run_hooks(PluginHost::HOOK_HOUSE_KEEPING);
|
$tmph->run_hooks(PluginHost::HOOK_HOUSE_KEEPING);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1794,7 +1795,10 @@ class RSSUtils {
|
|||||||
static function housekeeping_common(): void {
|
static function housekeeping_common(): void {
|
||||||
Scheduler::getInstance()->run_due_tasks();
|
Scheduler::getInstance()->run_due_tasks();
|
||||||
|
|
||||||
PluginHost::getInstance()->run_hooks(PluginHost::HOOK_HOUSE_KEEPING);
|
$pluginhost = PluginHost::getInstance();
|
||||||
|
|
||||||
|
$pluginhost->run_due_tasks();
|
||||||
|
$pluginhost->run_hooks(PluginHost::HOOK_HOUSE_KEEPING);
|
||||||
}
|
}
|
||||||
|
|
||||||
static function update_favicon(string $site_url, int $feed): false|string {
|
static function update_favicon(string $site_url, int $feed): false|string {
|
||||||
|
@ -7,7 +7,11 @@ class Scheduler {
|
|||||||
/** @var array<string, mixed> */
|
/** @var array<string, mixed> */
|
||||||
private array $scheduled_tasks = [];
|
private array $scheduled_tasks = [];
|
||||||
|
|
||||||
function __construct() {
|
private string $name;
|
||||||
|
|
||||||
|
function __construct(string $name = 'Default Scheduler') {
|
||||||
|
$this->set_name($name);
|
||||||
|
|
||||||
$this->add_scheduled_task('purge_orphaned_scheduled_tasks', '@weekly',
|
$this->add_scheduled_task('purge_orphaned_scheduled_tasks', '@weekly',
|
||||||
function() {
|
function() {
|
||||||
return $this->purge_orphaned_tasks();
|
return $this->purge_orphaned_tasks();
|
||||||
@ -22,6 +26,11 @@ class Scheduler {
|
|||||||
return self::$instance;
|
return self::$instance;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Sets specific identifier for this instance of Scheduler used in debug logging */
|
||||||
|
public function set_name(string $name) : void {
|
||||||
|
$this->name = $name;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Adds a backend scheduled task which will be executed by updater (if due) during housekeeping.
|
* Adds a backend scheduled task which will be executed by updater (if due) during housekeeping.
|
||||||
*
|
*
|
||||||
@ -42,13 +51,13 @@ class Scheduler {
|
|||||||
$task_name = strtolower($task_name);
|
$task_name = strtolower($task_name);
|
||||||
|
|
||||||
if (isset($this->scheduled_tasks[$task_name])) {
|
if (isset($this->scheduled_tasks[$task_name])) {
|
||||||
user_error("Attempted to override already registered scheduled task $task_name", E_USER_WARNING);
|
user_error("[$this->name] Attempted to override already registered scheduled task $task_name", E_USER_WARNING);
|
||||||
return false;
|
return false;
|
||||||
} else {
|
} else {
|
||||||
try {
|
try {
|
||||||
$cron = new Cron\CronExpression($cron_expression);
|
$cron = new Cron\CronExpression($cron_expression);
|
||||||
} catch (InvalidArgumentException $e) {
|
} catch (InvalidArgumentException $e) {
|
||||||
user_error("Attempt to register scheduled task $task_name failed: " . $e->getMessage(), E_USER_WARNING);
|
user_error("[$this->name] Attempt to register scheduled task $task_name failed: " . $e->getMessage(), E_USER_WARNING);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -64,7 +73,7 @@ class Scheduler {
|
|||||||
* Execute scheduled tasks which are due to run and record last run timestamps.
|
* Execute scheduled tasks which are due to run and record last run timestamps.
|
||||||
*/
|
*/
|
||||||
function run_due_tasks() : void {
|
function run_due_tasks() : void {
|
||||||
Debug::log('Processing all scheduled tasks...');
|
Debug::log("[$this->name] Processing all scheduled tasks...");
|
||||||
|
|
||||||
$tasks_succeeded = 0;
|
$tasks_succeeded = 0;
|
||||||
$tasks_failed = 0;
|
$tasks_failed = 0;
|
||||||
@ -89,7 +98,7 @@ class Scheduler {
|
|||||||
try {
|
try {
|
||||||
$rc = (int) $task['callback']();
|
$rc = (int) $task['callback']();
|
||||||
} catch (Exception $e) {
|
} catch (Exception $e) {
|
||||||
user_error("Scheduled task $task_name failed with exception: " . $e->getMessage(), E_USER_WARNING);
|
user_error("[$this->name] Scheduled task $task_name failed with exception: " . $e->getMessage(), E_USER_WARNING);
|
||||||
|
|
||||||
$rc = self::TASK_RC_EXCEPTION;
|
$rc = self::TASK_RC_EXCEPTION;
|
||||||
}
|
}
|
||||||
@ -125,7 +134,7 @@ class Scheduler {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Debug::log("Processing scheduled tasks finished with $tasks_succeeded tasks succeeded and $tasks_failed tasks failed.");
|
Debug::log("[$this->name] Processing scheduled tasks finished with $tasks_succeeded tasks succeeded and $tasks_failed tasks failed.");
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -31,7 +31,74 @@ class Cache_Starred_Images extends Plugin {
|
|||||||
$this->cache_status->put(".no-auto-expiry", "");
|
$this->cache_status->put(".no-auto-expiry", "");
|
||||||
|
|
||||||
if ($this->cache->is_writable() && $this->cache_status->is_writable()) {
|
if ($this->cache->is_writable() && $this->cache_status->is_writable()) {
|
||||||
$host->add_hook($host::HOOK_HOUSE_KEEPING, $this);
|
|
||||||
|
$host->add_scheduled_task($this, "cache_starred_images", "@hourly", function() {
|
||||||
|
Debug::log("caching media of starred articles for user " . $this->host->get_owner_uid() . "...");
|
||||||
|
|
||||||
|
$sth = $this->pdo->prepare("SELECT content, ttrss_entries.title,
|
||||||
|
ttrss_user_entries.owner_uid, link, site_url, ttrss_entries.id, plugin_data
|
||||||
|
FROM ttrss_entries, ttrss_user_entries LEFT JOIN ttrss_feeds ON
|
||||||
|
(ttrss_user_entries.feed_id = ttrss_feeds.id)
|
||||||
|
WHERE ref_id = ttrss_entries.id AND
|
||||||
|
marked = true AND
|
||||||
|
site_url != '' AND
|
||||||
|
ttrss_user_entries.owner_uid = ? AND
|
||||||
|
plugin_data NOT LIKE '%starred_cache_images%'
|
||||||
|
ORDER BY RANDOM() LIMIT 100");
|
||||||
|
|
||||||
|
if ($sth->execute([$this->host->get_owner_uid()])) {
|
||||||
|
|
||||||
|
$usth = $this->pdo->prepare("UPDATE ttrss_entries SET plugin_data = ? WHERE id = ?");
|
||||||
|
|
||||||
|
while ($line = $sth->fetch()) {
|
||||||
|
Debug::log("processing article " . $line["title"], Debug::LOG_VERBOSE);
|
||||||
|
|
||||||
|
if ($line["site_url"]) {
|
||||||
|
$success = $this->cache_article_images($line["content"], $line["site_url"], $line["owner_uid"], $line["id"]);
|
||||||
|
|
||||||
|
if ($success) {
|
||||||
|
$plugin_data = "starred_cache_images," . $line["owner_uid"] . ":" . $line["plugin_data"];
|
||||||
|
|
||||||
|
$usth->execute([$plugin_data, $line['id']]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
$host->add_scheduled_task($this, "expire_caches", "@daily", function() {
|
||||||
|
|
||||||
|
Debug::log("expiring {$this->cache->get_dir()} and {$this->cache_status->get_dir()}...");
|
||||||
|
|
||||||
|
$files = [
|
||||||
|
...(glob($this->cache->get_dir() . "/*-*") ?: []),
|
||||||
|
...(glob($this->cache_status->get_dir() . "/*.status") ?: []),
|
||||||
|
];
|
||||||
|
|
||||||
|
asort($files);
|
||||||
|
|
||||||
|
$last_article_id = 0;
|
||||||
|
$article_exists = 1;
|
||||||
|
|
||||||
|
foreach ($files as $file) {
|
||||||
|
list ($article_id, $hash) = explode("-", basename($file));
|
||||||
|
|
||||||
|
if ($article_id != $last_article_id) {
|
||||||
|
$last_article_id = $article_id;
|
||||||
|
|
||||||
|
$sth = $this->pdo->prepare("SELECT id FROM ttrss_entries WHERE id = ?");
|
||||||
|
$sth->execute([$article_id]);
|
||||||
|
|
||||||
|
$article_exists = $sth->fetch();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!$article_exists) {
|
||||||
|
unlink($file);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
$host->add_hook($host::HOOK_ENCLOSURE_ENTRY, $this);
|
$host->add_hook($host::HOOK_ENCLOSURE_ENTRY, $this);
|
||||||
$host->add_hook($host::HOOK_SANITIZE, $this);
|
$host->add_hook($host::HOOK_SANITIZE, $this);
|
||||||
} else {
|
} else {
|
||||||
@ -39,73 +106,6 @@ class Cache_Starred_Images extends Plugin {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/** since HOOK_UPDATE_TASK is not available to user plugins, this hook is a next best thing */
|
|
||||||
function hook_house_keeping() {
|
|
||||||
|
|
||||||
Debug::log("caching media of starred articles for user " . $this->host->get_owner_uid() . "...");
|
|
||||||
|
|
||||||
$sth = $this->pdo->prepare("SELECT content, ttrss_entries.title,
|
|
||||||
ttrss_user_entries.owner_uid, link, site_url, ttrss_entries.id, plugin_data
|
|
||||||
FROM ttrss_entries, ttrss_user_entries LEFT JOIN ttrss_feeds ON
|
|
||||||
(ttrss_user_entries.feed_id = ttrss_feeds.id)
|
|
||||||
WHERE ref_id = ttrss_entries.id AND
|
|
||||||
marked = true AND
|
|
||||||
site_url != '' AND
|
|
||||||
ttrss_user_entries.owner_uid = ? AND
|
|
||||||
plugin_data NOT LIKE '%starred_cache_images%'
|
|
||||||
ORDER BY RANDOM() LIMIT 100");
|
|
||||||
|
|
||||||
if ($sth->execute([$this->host->get_owner_uid()])) {
|
|
||||||
|
|
||||||
$usth = $this->pdo->prepare("UPDATE ttrss_entries SET plugin_data = ? WHERE id = ?");
|
|
||||||
|
|
||||||
while ($line = $sth->fetch()) {
|
|
||||||
Debug::log("processing article " . $line["title"], Debug::LOG_VERBOSE);
|
|
||||||
|
|
||||||
if ($line["site_url"]) {
|
|
||||||
$success = $this->cache_article_images($line["content"], $line["site_url"], $line["owner_uid"], $line["id"]);
|
|
||||||
|
|
||||||
if ($success) {
|
|
||||||
$plugin_data = "starred_cache_images," . $line["owner_uid"] . ":" . $line["plugin_data"];
|
|
||||||
|
|
||||||
$usth->execute([$plugin_data, $line['id']]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* actual housekeeping */
|
|
||||||
|
|
||||||
Debug::log("expiring {$this->cache->get_dir()} and {$this->cache_status->get_dir()}...");
|
|
||||||
|
|
||||||
$files = [
|
|
||||||
...(glob($this->cache->get_dir() . "/*-*") ?: []),
|
|
||||||
...(glob($this->cache_status->get_dir() . "/*.status") ?: []),
|
|
||||||
];
|
|
||||||
|
|
||||||
asort($files);
|
|
||||||
|
|
||||||
$last_article_id = 0;
|
|
||||||
$article_exists = 1;
|
|
||||||
|
|
||||||
foreach ($files as $file) {
|
|
||||||
list ($article_id, $hash) = explode("-", basename($file));
|
|
||||||
|
|
||||||
if ($article_id != $last_article_id) {
|
|
||||||
$last_article_id = $article_id;
|
|
||||||
|
|
||||||
$sth = $this->pdo->prepare("SELECT id FROM ttrss_entries WHERE id = ?");
|
|
||||||
$sth->execute([$article_id]);
|
|
||||||
|
|
||||||
$article_exists = $sth->fetch();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!$article_exists) {
|
|
||||||
unlink($file);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function hook_enclosure_entry($enc, $article_id, $rv) {
|
function hook_enclosure_entry($enc, $article_id, $rv) {
|
||||||
$local_filename = $article_id . "-" . sha1($enc["content_url"]);
|
$local_filename = $article_id . "-" . sha1($enc["content_url"]);
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user