xref: /dokuwiki/inc/TaskRunner.php (revision 9493c275054ef649b724c083fdac46388e6e1361)
1<?php
2
3namespace dokuwiki;
4
5use Doku_Event;
6use Sitemapper;
7use Subscription;
8
9class TaskRunner
10{
11    public function run()
12    {
13        // run one of the jobs
14        $tmp = []; // No event data
15        $evt = new Doku_Event('INDEXER_TASKS_RUN', $tmp);
16        if ($evt->advise_before()) {
17            $this->runIndexer() or
18            $this->runSitemapper() or
19            $this->sendDigest() or
20            $this->runTrimRecentChanges() or
21            $this->runTrimRecentChanges(true) or
22            $evt->advise_after();
23        }
24    }
25
26    /**
27     * Trims the recent changes cache (or imports the old changelog) as needed.
28     *
29     * @param bool $media_changes If the media changelog shall be trimmed instead of
30     *                              the page changelog
31     * @return bool
32     *
33     * @author Ben Coburn <btcoburn@silicodon.net>
34     */
35    protected function runTrimRecentChanges($media_changes = false) {
36        global $conf;
37
38        echo "runTrimRecentChanges($media_changes): started".NL;
39
40        $fn = ($media_changes ? $conf['media_changelog'] : $conf['changelog']);
41
42        // Trim the Recent Changes
43        // Trims the recent changes cache to the last $conf['changes_days'] recent
44        // changes or $conf['recent'] items, which ever is larger.
45        // The trimming is only done once a day.
46        if (file_exists($fn) &&
47            (@filemtime($fn.'.trimmed')+86400)<time() &&
48            !file_exists($fn.'_tmp')) {
49            @touch($fn.'.trimmed');
50            io_lock($fn);
51            $lines = file($fn);
52            if (count($lines)<=$conf['recent']) {
53                // nothing to trim
54                io_unlock($fn);
55                echo "runTrimRecentChanges($media_changes): finished".NL;
56                return false;
57            }
58
59            io_saveFile($fn.'_tmp', '');          // presave tmp as 2nd lock
60            $trim_time = time() - $conf['recent_days']*86400;
61            $out_lines = array();
62            $old_lines = array();
63            for ($i=0; $i<count($lines); $i++) {
64                $log = parseChangelogLine($lines[$i]);
65                if ($log === false) continue;                      // discard junk
66                if ($log['date'] < $trim_time) {
67                    $old_lines[$log['date'].".$i"] = $lines[$i];     // keep old lines for now (append .$i to prevent key collisions)
68                } else {
69                    $out_lines[$log['date'].".$i"] = $lines[$i];     // definitely keep these lines
70                }
71            }
72
73            if (count($lines)==count($out_lines)) {
74                // nothing to trim
75                @unlink($fn.'_tmp');
76                io_unlock($fn);
77                echo "runTrimRecentChanges($media_changes): finished".NL;
78                return false;
79            }
80
81            // sort the final result, it shouldn't be necessary,
82            //   however the extra robustness in making the changelog cache self-correcting is worth it
83            ksort($out_lines);
84            $extra = $conf['recent'] - count($out_lines);        // do we need extra lines do bring us up to minimum
85            if ($extra > 0) {
86                ksort($old_lines);
87                $out_lines = array_merge(array_slice($old_lines,-$extra),$out_lines);
88            }
89
90            // save trimmed changelog
91            io_saveFile($fn.'_tmp', implode('', $out_lines));
92            @unlink($fn);
93            if (!rename($fn.'_tmp', $fn)) {
94                // rename failed so try another way...
95                io_unlock($fn);
96                io_saveFile($fn, implode('', $out_lines));
97                @unlink($fn.'_tmp');
98            } else {
99                io_unlock($fn);
100            }
101            echo "runTrimRecentChanges($media_changes): finished".NL;
102            return true;
103        }
104
105        // nothing done
106        echo "runTrimRecentChanges($media_changes): finished".NL;
107        return false;
108    }
109
110
111    /**
112     * Runs the indexer for the current page
113     *
114     * @author Andreas Gohr <andi@splitbrain.org>
115     */
116    protected function runIndexer(){
117        global $ID;
118        global $conf;
119        print "runIndexer(): started".NL;
120
121        if(!$ID) return false;
122
123        // do the work
124        return idx_addPage($ID, true);
125    }
126
127    /**
128     * Builds a Google Sitemap of all public pages known to the indexer
129     *
130     * The map is placed in the root directory named sitemap.xml.gz - This
131     * file needs to be writable!
132     *
133     * @author Andreas Gohr
134     * @link   https://www.google.com/webmasters/sitemaps/docs/en/about.html
135     */
136    protected function runSitemapper(){
137        print "runSitemapper(): started".NL;
138        $result = Sitemapper::generate() && Sitemapper::pingSearchEngines();
139        print 'runSitemapper(): finished'.NL;
140        return $result;
141    }
142
143    /**
144     * Send digest and list mails for all subscriptions which are in effect for the
145     * current page
146     *
147     * @author Adrian Lang <lang@cosmocode.de>
148     */
149    protected function sendDigest() {
150        global $conf;
151        global $ID;
152
153        echo 'sendDigest(): started'.NL;
154        if(!actionOK('subscribe')) {
155            echo 'sendDigest(): disabled'.NL;
156            return false;
157        }
158        $sub = new Subscription();
159        $sent = $sub->send_bulk($ID);
160
161        echo "sendDigest(): sent $sent mails".NL;
162        echo 'sendDigest(): finished'.NL;
163        return (bool) $sent;
164    }
165}
166