summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--classes/PluginHost.php33
-rw-r--r--classes/RSSUtils.php6
-rw-r--r--classes/Scheduler.php21
-rwxr-xr-xplugins/cache_starred_images/init.php104
4 files changed, 105 insertions, 59 deletions
diff --git a/classes/PluginHost.php b/classes/PluginHost.php
index f61a5a9a4..cc7a69a5a 100644
--- a/classes/PluginHost.php
+++ b/classes/PluginHost.php
@@ -38,6 +38,8 @@ class PluginHost {
private static ?PluginHost $instance = null;
+ private ?Scheduler $scheduler = null;
+
const API_VERSION = 2;
const PUBLIC_METHOD_DELIMITER = "--";
@@ -215,6 +217,7 @@ class PluginHost {
function __construct() {
$this->pdo = Db::pdo();
+ $this->scheduler = new Scheduler('PluginHost Scheduler');
$this->storage = [];
}
@@ -438,6 +441,10 @@ class PluginHost {
$this->owner_uid = (int) $owner_uid;
+ if ($this->owner_uid) {
+ $this->set_scheduler_name("PluginHost Scheduler for UID $owner_uid");
+ }
+
foreach ($plugins as $class) {
$class = trim($class);
$class_file = strtolower(basename(clean($class)));
@@ -907,4 +914,30 @@ class PluginHost {
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);
+ }
}
diff --git a/classes/RSSUtils.php b/classes/RSSUtils.php
index b0cfc4823..7339a066d 100644
--- a/classes/RSSUtils.php
+++ b/classes/RSSUtils.php
@@ -1666,6 +1666,7 @@ class RSSUtils {
UserHelper::load_user_plugins($owner_uid, $tmph);
+ $tmph->run_due_tasks();
$tmph->run_hooks(PluginHost::HOOK_HOUSE_KEEPING);
}
@@ -1794,7 +1795,10 @@ class RSSUtils {
static function housekeeping_common(): void {
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 {
diff --git a/classes/Scheduler.php b/classes/Scheduler.php
index 13d8e94ea..ce6a2f67d 100644
--- a/classes/Scheduler.php
+++ b/classes/Scheduler.php
@@ -7,7 +7,11 @@ class Scheduler {
/** @var array<string, mixed> */
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',
function() {
return $this->purge_orphaned_tasks();
@@ -22,6 +26,11 @@ class Scheduler {
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.
*
@@ -42,13 +51,13 @@ class Scheduler {
$task_name = strtolower($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;
} else {
try {
$cron = new Cron\CronExpression($cron_expression);
} 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;
}
@@ -64,7 +73,7 @@ class Scheduler {
* Execute scheduled tasks which are due to run and record last run timestamps.
*/
function run_due_tasks() : void {
- Debug::log('Processing all scheduled tasks...');
+ Debug::log("[$this->name] Processing all scheduled tasks...");
$tasks_succeeded = 0;
$tasks_failed = 0;
@@ -89,7 +98,7 @@ class Scheduler {
try {
$rc = (int) $task['callback']();
} 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;
}
@@ -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.");
}
/**
diff --git a/plugins/cache_starred_images/init.php b/plugins/cache_starred_images/init.php
index 527fb21ee..4ca11235a 100755
--- a/plugins/cache_starred_images/init.php
+++ b/plugins/cache_starred_images/init.php
@@ -31,78 +31,78 @@ class Cache_Starred_Images extends Plugin {
$this->cache_status->put(".no-auto-expiry", "");
if ($this->cache->is_writable() && $this->cache_status->is_writable()) {
- $host->add_hook($host::HOOK_HOUSE_KEEPING, $this);
- $host->add_hook($host::HOOK_ENCLOSURE_ENTRY, $this);
- $host->add_hook($host::HOOK_SANITIZE, $this);
- } else {
- user_error("Starred cache directory ".$this->cache->get_dir()." (or status cache subdir in status-files/) is not writable.", E_USER_WARNING);
- }
- }
-
- /** 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() . "...");
+ $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");
+ $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()])) {
+ if ($sth->execute([$this->host->get_owner_uid()])) {
- $usth = $this->pdo->prepare("UPDATE ttrss_entries SET plugin_data = ? WHERE id = ?");
+ $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);
+ 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 ($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"];
+ if ($success) {
+ $plugin_data = "starred_cache_images," . $line["owner_uid"] . ":" . $line["plugin_data"];
- $usth->execute([$plugin_data, $line['id']]);
+ $usth->execute([$plugin_data, $line['id']]);
+ }
+ }
}
}
- }
- }
+ });
- /* actual housekeeping */
+ $host->add_scheduled_task($this, "expire_caches", "@daily", function() {
- Debug::log("expiring {$this->cache->get_dir()} and {$this->cache_status->get_dir()}...");
+ 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") ?: []),
- ];
+ $files = [
+ ...(glob($this->cache->get_dir() . "/*-*") ?: []),
+ ...(glob($this->cache_status->get_dir() . "/*.status") ?: []),
+ ];
- asort($files);
+ asort($files);
- $last_article_id = 0;
- $article_exists = 1;
+ $last_article_id = 0;
+ $article_exists = 1;
- foreach ($files as $file) {
- list ($article_id, $hash) = explode("-", basename($file));
+ foreach ($files as $file) {
+ list ($article_id, $hash) = explode("-", basename($file));
- if ($article_id != $last_article_id) {
- $last_article_id = $article_id;
+ 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]);
+ $sth = $this->pdo->prepare("SELECT id FROM ttrss_entries WHERE id = ?");
+ $sth->execute([$article_id]);
- $article_exists = $sth->fetch();
- }
+ $article_exists = $sth->fetch();
+ }
- if (!$article_exists) {
- unlink($file);
- }
+ if (!$article_exists) {
+ unlink($file);
+ }
+ }
+
+ });
+
+ $host->add_hook($host::HOOK_ENCLOSURE_ENTRY, $this);
+ $host->add_hook($host::HOOK_SANITIZE, $this);
+ } else {
+ user_error("Starred cache directory ".$this->cache->get_dir()." (or status cache subdir in status-files/) is not writable.", E_USER_WARNING);
}
}