<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Howto on wt-modules</title><link>https://bschwede.codeberg.page/wt-modules/en/docs/cronjob/howto-use/</link><description>Recent content in Howto on wt-modules</description><generator>Hugo</generator><language>en</language><atom:link href="https://bschwede.codeberg.page/wt-modules/en/docs/cronjob/howto-use/index.xml" rel="self" type="application/rss+xml"/><item><title>Service API</title><link>https://bschwede.codeberg.page/wt-modules/en/docs/cronjob/howto-use/service-api/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://bschwede.codeberg.page/wt-modules/en/docs/cronjob/howto-use/service-api/</guid><description>&lt;p&gt;Neighboring modules query the module&amp;rsquo;s state through a stable, read-only facade&#10;(&lt;code&gt;CronjobService&lt;/code&gt;) instead of its internal services. Check availability first -&#10;the module may not be installed, and the class is only autoloadable when its&#10;&lt;a href="https://github.com/bschwede/wt-cronjob/tree/main/module.php" target="_blank" rel="noopener"&gt;&lt;code&gt;module.php&lt;/code&gt;&lt;/a&gt;&#10; has been loaded:&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#cad3f5;background-color:#24273a;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-php" data-lang="php"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#91d7e3;font-weight:bold"&gt;&amp;lt;?&lt;/span&gt;php&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#c6a0f6"&gt;use&lt;/span&gt; Schwendinger\Webtrees\Module\Cronjob\Services\CronjobService;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#c6a0f6"&gt;if&lt;/span&gt; (class_exists(CronjobService&lt;span style="color:#91d7e3;font-weight:bold"&gt;::&lt;/span&gt;&lt;span style="color:#8aadf4"&gt;class&lt;/span&gt;) &lt;span style="color:#91d7e3;font-weight:bold"&gt;&amp;amp;&amp;amp;&lt;/span&gt; CronjobService&lt;span style="color:#91d7e3;font-weight:bold"&gt;::&lt;/span&gt;&lt;span style="color:#8aadf4"&gt;isFunctional&lt;/span&gt;()) {&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f4dbd6"&gt;$status&lt;/span&gt; &lt;span style="color:#91d7e3;font-weight:bold"&gt;=&lt;/span&gt; CronjobService&lt;span style="color:#91d7e3;font-weight:bold"&gt;::&lt;/span&gt;&lt;span style="color:#8aadf4"&gt;jobStatus&lt;/span&gt;(&lt;span style="color:#a6da95"&gt;&amp;#39;mymodule:backup&amp;#39;&lt;/span&gt;); &lt;span style="color:#6e738d;font-style:italic"&gt;// null = unknown job&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f4dbd6"&gt;$age&lt;/span&gt; &lt;span style="color:#91d7e3;font-weight:bold"&gt;=&lt;/span&gt; CronjobService&lt;span style="color:#91d7e3;font-weight:bold"&gt;::&lt;/span&gt;&lt;span style="color:#8aadf4"&gt;lastTickAge&lt;/span&gt;(); &lt;span style="color:#6e738d;font-style:italic"&gt;// seconds since last tick&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f4dbd6"&gt;$listened&lt;/span&gt; &lt;span style="color:#91d7e3;font-weight:bold"&gt;=&lt;/span&gt; CronjobService&lt;span style="color:#91d7e3;font-weight:bold"&gt;::&lt;/span&gt;&lt;span style="color:#8aadf4"&gt;listenedEvents&lt;/span&gt;(); &lt;span style="color:#6e738d;font-style:italic"&gt;// event name =&amp;gt; job names&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&#10;&lt;table&gt;&#10;&#9;&lt;thead&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;th&gt;Question&lt;/th&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;th&gt;Method(s)&lt;/th&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&lt;/thead&gt;&#10;&#9;&lt;tbody&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;Is the service functional?&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;&lt;code&gt;isFunctional()&lt;/code&gt;, &lt;code&gt;problems()&lt;/code&gt; (codes: &lt;code&gt;module_disabled&lt;/code&gt;, &lt;code&gt;not_migrated&lt;/code&gt;, &lt;code&gt;cron_library_missing&lt;/code&gt;, &lt;code&gt;tick_stale&lt;/code&gt;)&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;Which features are enabled?&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;&lt;code&gt;features()&lt;/code&gt; (&lt;code&gt;watch&lt;/code&gt;, &lt;code&gt;pseudo_events&lt;/code&gt;, &lt;code&gt;route_events&lt;/code&gt;), &lt;code&gt;watchStatus()&lt;/code&gt;&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;When did the last tick run?&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;&lt;code&gt;lastTick()&lt;/code&gt; / &lt;code&gt;lastTickAge()&lt;/code&gt; - both trigger types (marker file &lt;code&gt;data/cronjob/tick.last&lt;/code&gt;)&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;Is a job enabled / when did it last run?&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;&lt;code&gt;isJobEnabled($name)&lt;/code&gt;, &lt;code&gt;jobStatus($name)&lt;/code&gt;, &lt;code&gt;lastRun($name)&lt;/code&gt; - job names are namespaced (&lt;code&gt;&amp;lt;module&amp;gt;:&amp;lt;name&amp;gt;&lt;/code&gt;)&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;Which events are listened for?&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;&lt;code&gt;listenedEvents()&lt;/code&gt;, &lt;code&gt;listenersFor($event)&lt;/code&gt;; catalogs: &lt;code&gt;eventCatalog()&lt;/code&gt;, &lt;code&gt;pseudoEvents()&lt;/code&gt;, &lt;code&gt;routeEvents()&lt;/code&gt;&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;Actions (the only two)&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;&lt;code&gt;runJobNow($name)&lt;/code&gt; (queued at the next tick), &lt;code&gt;pushEvent($name, $payload)&lt;/code&gt; (event queue)&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&lt;/tbody&gt;&#10;&lt;/table&gt;&#10;&lt;p&gt;Contract: read-only methods never throw when the schema is missing (safe defaults),&#10;descriptions are source strings (never pre-translated), and &lt;code&gt;lastRun()&lt;/code&gt; deliberately&#10;excludes the job output, which may contain personal data.&lt;/p&gt;</description></item><item><title>Self-registration</title><link>https://bschwede.codeberg.page/wt-modules/en/docs/cronjob/howto-use/self-registration/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://bschwede.codeberg.page/wt-modules/en/docs/cronjob/howto-use/self-registration/</guid><description>&lt;p&gt;Other modules can &lt;em&gt;advertise&lt;/em&gt; their &lt;a href="https://bschwede.codeberg.page/wt-modules/en/docs/cronjob/cli-scripts/"&gt;scheduled jobs&lt;/a&gt; so they show up in the admin UI without manual registry editing. Two ways (a module may use both); both are discovered automatically on each &lt;a href="https://bschwede.codeberg.page/wt-modules/en/docs/cronjob/tick/"&gt;tick&lt;/a&gt;, only for &lt;strong&gt;enabled&lt;/strong&gt; modules:&lt;/p&gt;&#10;&lt;h2 id="marker-method"&gt;Marker method&lt;a class="td-heading-self-link" href="#marker-method" aria-label="Heading self-link"&gt;&lt;/a&gt;&lt;/h2&gt;&#10;&lt;p&gt;&lt;code&gt;getCronJobs(): array&lt;/code&gt; on the module&amp;rsquo;s &lt;a href="https://github.com/bschwede/wt-cronjob/tree/main/module.php" target="_blank" rel="noopener"&gt;&lt;code&gt;module.php&lt;/code&gt;&lt;/a&gt;&#10; class, returning the same shape. Equivalent: &lt;code&gt;getModuleEvents(): array&lt;/code&gt;, &lt;code&gt;getModuleCommands(): array&lt;/code&gt;.&lt;/p&gt;&#10;&lt;h2 id="manifest-file-recommended"&gt;Manifest file (recommended)&lt;a class="td-heading-self-link" href="#manifest-file-recommended" aria-label="Heading self-link"&gt;&lt;/a&gt;&lt;/h2&gt;&#10;&lt;p&gt;&lt;code&gt;modules_v4/&amp;lt;module&amp;gt;/cron-jobs.php&lt;/code&gt; that returns an array of job specs:&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#cad3f5;background-color:#24273a;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-php" data-lang="php"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#91d7e3;font-weight:bold"&gt;&amp;lt;?&lt;/span&gt;php&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#6e738d;font-style:italic"&gt;// modules_v4/linkenhancer/cron-jobs.php&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#c6a0f6"&gt;declare&lt;/span&gt;(strict_types&lt;span style="color:#91d7e3;font-weight:bold"&gt;=&lt;/span&gt;&lt;span style="color:#f5a97f"&gt;1&lt;/span&gt;);&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#c6a0f6"&gt;return&lt;/span&gt; [&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; [&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6da95"&gt;&amp;#39;name&amp;#39;&lt;/span&gt; &lt;span style="color:#91d7e3;font-weight:bold"&gt;=&amp;gt;&lt;/span&gt; &lt;span style="color:#a6da95"&gt;&amp;#39;link-index&amp;#39;&lt;/span&gt;,&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6da95"&gt;&amp;#39;title&amp;#39;&lt;/span&gt; &lt;span style="color:#91d7e3;font-weight:bold"&gt;=&amp;gt;&lt;/span&gt; &lt;span style="color:#a6da95"&gt;&amp;#39;Update the link index&amp;#39;&lt;/span&gt;,&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6da95"&gt;&amp;#39;triggers&amp;#39;&lt;/span&gt; &lt;span style="color:#91d7e3;font-weight:bold"&gt;=&amp;gt;&lt;/span&gt; [&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; [&lt;span style="color:#a6da95"&gt;&amp;#39;type&amp;#39;&lt;/span&gt; &lt;span style="color:#91d7e3;font-weight:bold"&gt;=&amp;gt;&lt;/span&gt; &lt;span style="color:#a6da95"&gt;&amp;#39;time&amp;#39;&lt;/span&gt;, &lt;span style="color:#a6da95"&gt;&amp;#39;cron&amp;#39;&lt;/span&gt; &lt;span style="color:#91d7e3;font-weight:bold"&gt;=&amp;gt;&lt;/span&gt; &lt;span style="color:#a6da95"&gt;&amp;#39;*/30 * * * *&amp;#39;&lt;/span&gt;],&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; ],&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6da95"&gt;&amp;#39;command_type&amp;#39;&lt;/span&gt; &lt;span style="color:#91d7e3;font-weight:bold"&gt;=&amp;gt;&lt;/span&gt; &lt;span style="color:#a6da95"&gt;&amp;#39;module&amp;#39;&lt;/span&gt;,&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6da95"&gt;&amp;#39;command&amp;#39;&lt;/span&gt; &lt;span style="color:#91d7e3;font-weight:bold"&gt;=&amp;gt;&lt;/span&gt; &lt;span style="color:#a6da95"&gt;&amp;#39;modules_v4/linkenhancer/cli/build-link-index.php&amp;#39;&lt;/span&gt;,&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6da95"&gt;&amp;#39;args&amp;#39;&lt;/span&gt; &lt;span style="color:#91d7e3;font-weight:bold"&gt;=&amp;gt;&lt;/span&gt; &lt;span style="color:#a6da95"&gt;&amp;#39;--limit=5000&amp;#39;&lt;/span&gt;,&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; ],&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;];&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&#10;&lt;p&gt;Translatable &lt;code&gt;title&lt;/code&gt; / &lt;code&gt;description&lt;/code&gt; literals in a manifest should be wrapped in&#10;&lt;code&gt;MoreI18N::translate()&lt;/code&gt; (identity marker)&#10;so &lt;code&gt;xgettext&lt;/code&gt; picks them up - translation happens at render time, never at manifest&#10;load.&lt;/p&gt;</description></item></channel></rss>