root/trunk/gregarius/cls/update.php

Revision 1774, 18.9 kB (checked in by cfriesen, 11 months ago)

Individual update (hope nothing is missing)

  • Property svn:eolstyle set to native
Line 
1<?php
2###############################################################################
3# Gregarius - A PHP based RSS aggregator.
4# Copyright (C) 2003 - 2006 Marco Bonetti
5#
6###############################################################################
7# This program is free software and open source software; you can redistribute
8# it and/or modify it under the terms of the GNU General Public License as
9# published by the Free Software Foundation; either version 2 of the License,
10# or (at your option) any later version.
11#
12# This program is distributed in the hope that it will be useful, but WITHOUT
13# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
14# FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
15# more details.
16#
17# You should have received a copy of the GNU General Public License along
18# with this program; if not, write to the Free Software Foundation, Inc.,
19# 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA  or visit
20# http://www.gnu.org/licenses/gpl.html
21#
22###############################################################################
23# E-mail:      mbonetti at gmail dot com
24# Web page:    http://gregarius.net/
25#
26###############################################################################
27
28define('PUSH_BOUNDARY', "-------- =_aaaaaaaaaa0");
29define('ERROR_NOERROR', "");
30define('ERROR_WARNING', " warning");
31define('ERROR_ERROR', " error");
32define('NO_NEW_ITEMS', '-');
33define ('UPDATING','...');
34define ('DEFAULT_CID', -1);
35
36
37define ('THIS_FILE',basename(__FILE__));
38
39define ('GROUP_SPLITTER',',');
40define ('SUB_SPLITTER','|');
41define ('SUB_SUB_SPLITTER','.');
42
43// Define the ajax parallel and batch size from the config options
44if (getConfig('rss.config.ajaxbatchsize')) {
45    define ('AJAX_BATCH_SIZE',getConfig('rss.config.ajaxbatchsize'));
46} else {
47    define ('AJAX_BATCH_SIZE',3);
48}
49
50if (getConfig('rss.config.ajaxparallelsize')) {
51    define ('AJAX_PARALLEL_SIZE',getConfig('rss.config.ajaxparallelsize'));
52} else {
53    define ('AJAX_PARALLEL_SIZE',3);
54}
55
56
57/**
58 * Generic Update. Note that this is an "abstract" class
59 * (from the java perspective) as specific sub-classes must
60 * override a couple (implicitly) abstract method, such as
61 * render()
62 */
63class Update {
64
65    var $chans = array ();
66
67    function Update($doPopulate = true, $updatePrivateAlso = false, $cid = DEFAULT_CID) {
68        rss_plugin_hook('rss.plugins.updates.before', null);
69        if($doPopulate) {
70            $this->populate($updatePrivateAlso, $cid);
71        }
72
73        // Script timeout: ten seconds per feed should be a good upper limit
74        @set_time_limit(0);
75        @ini_set('max_execution_time', (10 * count($this->chans) + 300));
76    }
77
78    function populate($updatePrivateAlso = false, $cid) {
79        $sql = "select c.id, c.url, c.title from ".getTable("channels") . " c "
80               . " inner join " . getTable('folders') . " f on f.id = c.parent "
81               . " where not(c.mode & ".RSS_MODE_DELETED_STATE.") ";
82
83        if (hidePrivate() && !$updatePrivateAlso) {
84            $sql .= " and not(mode & ".RSS_MODE_PRIVATE_STATE.") ";
85        }
86       
87        if(DEFAULT_CID != $cid) {
88                $sql .= " and c.id = " . $cid . " ";
89                                } else {
90                                        if (getConfig('rss.config.absoluteordering')) {
91                                                        $sql .= " order by f.position asc, c.position asc";
92                                        } else {
93                                                        $sql .= " order by f.name, c.title asc";
94                                        }
95                                }
96
97        $res = rss_query($sql);
98        while (list ($cid, $url, $title) = rss_fetch_row($res)) {
99            $this->chans[] = array ($cid, $url, $title);
100        }
101    }
102
103    function cleanUp($newIds, $ignorePrivate = false) {
104        if (!hidePrivate() || $ignorePrivate) {
105            if (count($newIds) > 0 && getConfig('rss.config.markreadonupdate')) {
106                rss_query("update ".getTable("item")." set unread = unread & "
107                          .SET_MODE_READ_STATE." where unread & ".RSS_MODE_UNREAD_STATE
108                          ." and id not in (".implode(",", $newIds).")");
109            }
110        }
111
112        setProperty('__meta__','meta.lastupdate','misc',time());
113
114        if (count($newIds) > 0) {
115            rss_invalidate_cache();
116        }
117        rss_plugin_hook('rss.plugins.updates.after', null);
118    }
119
120    function magpieError($error) {
121        if (is_numeric($error) && ($error & MAGPIE_FEED_ORIGIN_CACHE)) {
122            if ($error & MAGPIE_FEED_ORIGIN_HTTP_304) {
123                $label = __('OK (304 Not modified)');
124                $cls = ERROR_NOERROR;
125            }
126            elseif ($error & MAGPIE_FEED_ORIGIN_HTTP_TIMEOUT) {
127                $label = __('HTTP Timeout (Local cache)');
128                $cls = ERROR_ERROR;
129            }
130            elseif ($error & MAGPIE_FEED_ORIGIN_NOT_FETCHED) {
131                $label = __('OK (Local cache)');
132                $cls = ERROR_NOERROR;
133            }
134            elseif ($error & MAGPIE_FEED_ORIGIN_HTTP_404) {
135                $label = __('404 Not Found (Local cache)');
136                $cls = ERROR_ERROR;
137            }
138            elseif ($error & MAGPIE_FEED_ORIGIN_HTTP_403) {
139                $label = __('403 Forbidden (Local cache)');
140                $cls = ERROR_ERROR;
141            }
142            else {
143                $label = $error;
144                $cls = ERROR_ERROR;
145            }
146        }
147        elseif ($error & MAGPIE_FEED_ORIGIN_HTTP_200) {
148            $label = __('OK (HTTP 200)');
149            $cls = ERROR_NOERROR;
150        }
151        else {
152            if (is_numeric($error)) {
153                $label = __('ERROR') ." $error";
154                $cls = ERROR_ERROR;
155            } else {
156                // shoud contain MagpieError at this point
157                $label = $error;
158                $cls = ERROR_ERROR;
159            }
160        }
161
162        return array( $label, $cls);
163    }
164}
165
166/**
167 * HTTP Server Push update
168 */
169class HTTPServerPushUpdate extends Update {
170
171    function HTTPServerPushUpdate($cid) {
172        parent::Update($doPopulate = true, $updatePrivateAlso = false, $cid);
173
174        $GLOBALS['rss']->header->appendHeader("Connection: close");
175        $GLOBALS['rss']->header->appendHeader("Content-type: multipart/x-mixed-replace;boundary=\"".PUSH_BOUNDARY."\"");
176        $GLOBALS['rss']->header->options |= HDR_NO_OUPUTBUFFERING;
177        rss_set_hook('rss.plugins.bodystart', "pushHeaderCallBack");
178        rss_set_hook('rss.plugins.bodyend', "pushFooterCallBack");
179
180        ob_implicit_flush();
181    }
182
183    function render() {
184        $newIds = array ();
185
186        echo
187        "<h2>".sprintf(__('Updating %d Feeds...'), count($this -> chans))."</h2>\n"
188        ."<table id=\"updatetable\">\n"
189        ."<tr>\n"
190        ."<th class=\"lc\">".__('Feed')."</th>\n"
191        ."<th class=\"mc\">".__('Status')."</th>\n"
192        ."<th class=\"rc\">".__('New Items')."</th>\n"
193        ."</tr>";
194
195        foreach ($this->chans as $chan) {
196            list ($cid, $url, $title) = $chan;
197            echo "<tr>\n";
198            echo "<td class=\"lc\">$title</td>\n";
199            flush();
200
201            $ret = update($cid);
202
203            if (is_array($ret)) {
204                list ($error, $unreadIds) = $ret;
205                $newIds = array_merge($newIds, $unreadIds);
206            } else {
207                $error = 0;
208                $unreadIds = array ();
209            }
210            $unread = count($unreadIds);
211
212            list($label,$cls) = parent::magpieError($error);
213
214            if ($cls == ERROR_ERROR && !defined("UPDATE_ERROR")) {
215                define("UPDATE_ERROR", true);
216            }
217            echo "<td class=\"mc$cls\">$label</td>\n";
218            echo "<td class=\"rc\">". ($unread > 0 ? $unread : NO_NEW_ITEMS)."</td>\n";
219            echo "</tr>\n";
220            flush();
221
222        }
223
224        echo "</table>\n";
225        echo "<p><a href=\"".getPath()."\">Redirecting...</a></p>\n";
226        flush();
227        // Sleep two seconds
228        sleep(2);
229
230        parent::cleanUp($newIds);
231    }
232}
233
234/**
235 * AJAXUpdate updates feeds via AJAX. It's a bit more server-intesive
236 * than HTTP Server Push
237 */
238class AJAXUpdate extends Update {
239
240    function AJAXUpdate($cid) {
241        parent::Update($doPopulate = true, $updatePrivateAlso = false, $cid);
242        $GLOBALS['rss']->header->extraHTML .= "<script type=\"text/javascript\" src=\""
243                                              .getPath()."update.php?js\"></script>\n";
244    }
245
246    function render() {
247
248        echo "<h2 style=\"margin-bottom:1em;\">". sprintf(__('Updating %d Feeds...'),count($this -> chans)) ."</h2>\n";
249
250        echo "<table id=\"updatetable\">\n"
251        ."<tr>\n"
252        ."<th class=\"lc\">".__('Feed')."</th>\n"
253        ."<th class=\"mc\">".__('Status')."</th>\n"
254        ."<th class=\"rc\">".__('New Items')."</th>\n"
255        ."</tr>\n";
256
257
258        foreach ($this->chans as $chan) {
259            list ($cid, $url, $title) = $chan;
260            echo "<tr id=\"tr_$cid\">\n";
261            echo "<td class=\"lc\" id=\"u_l_$cid\">$title</td>\n";
262            echo "<td class=\"mc\" id=\"u_m_$cid\">&nbsp;</td>\n";
263            echo "<td class=\"rc\" id=\"u_r_$cid\">&nbsp;</td>\n";
264            echo "</tr>\n";
265        }
266
267        echo "</table>\n";
268        echo "<script type=\"text/javascript\">\n";
269        echo "function runAjaxUpdate() { \n";
270        echo "    for (k =0; k < " . AJAX_PARALLEL_SIZE . "; k++){\n";
271        echo "    doUpdate();\n";
272        echo "    }\n";
273        echo "}\n";
274        // Fix for IE's stupid "Operation Aborted" Error
275        echo "   if (window.addEventListener) window.addEventListener(\"load\",runAjaxUpdate,false); else if (window.attachEvent) window.attachEvent(\"onload\",runAjaxUpdate);\n";
276        echo "</script>\n";
277    }
278}
279
280class CommandLineUpdate extends Update {
281    function CommandLineUpdate($cid) {
282        parent::Update($doPopulate = true, $updatePrivateAlso = true, $cid);
283    }
284
285    function render() {
286        $newIds = array();
287        foreach ($this->chans as $chan) {
288            list ($cid, $url, $title) = $chan;
289            echo "$title ...\t";
290            flush();
291            $ret = update($cid);
292
293            if (is_array($ret)) {
294                list ($error, $unreadIds) = $ret;
295                $newIds = array_merge($newIds, $unreadIds);
296            } else {
297                $error = 0;
298                $unreadIds = array ();
299            }
300            $unread = count($unreadIds);
301
302            list($label,$cls) = parent::magpieError($error);
303            echo "\n$label, $unread " . __('New Items') . "\n\n";
304            flush();
305
306        }
307        parent::cleanUp($newIds, $ignorePrivate = true);
308    }
309}
310
311class MobileUpdate extends Update {
312    function MobileUpdate($cid) {
313        parent::Update($doPopulate = true, $updatePrivateAlso = false, $cid);
314    }
315   
316    function render() {
317        $newIds = array();
318        foreach ($this->chans as $chan) {
319            list ($cid, $url, $title) = $chan;
320            echo "$title ...\t";
321            flush();
322            $ret = update($cid);
323
324            if (is_array($ret)) {
325                list ($error, $unreadIds) = $ret;
326                $newIds = array_merge($newIds, $unreadIds);
327            } else {
328                $error = 0;
329                $unreadIds = array ();
330            }
331            $unread = count($unreadIds);
332            list($label,$cls) = parent::magpieError($error);
333            echo "\n$label, $unread " . __('New Items') . "<br />";
334            flush();
335        }
336    }
337}
338
339/**
340 * CommandLineUpdateNews updates the feeds and displays only feeds with
341 * errors or new items.
342 */
343class CommandLineUpdateNews extends CommandLineUpdate {
344    function render() {
345        $newIds = array();
346        foreach ($this->chans as $chan) {
347            list ($cid, $url, $title) = $chan;
348            $ret = update($cid);
349
350            if (is_array($ret)) {
351                list ($error, $unreadIds) = $ret;
352                $newIds = array_merge($newIds, $unreadIds);
353            } else {
354                $error = 0;
355                $unreadIds = array();
356            }
357            $unread = count($unreadIds);
358
359            list($label, $cls) = parent::magpieError($error);
360
361            if (($cls != ERROR_NOERROR) || ($unread > 0)) {
362                echo "$title ...\t";
363                flush();
364                echo "\n$label, $unread " . __('New Items') . "\n\n";
365                flush();
366            }
367        }
368
369        if (!hidePrivate()) {
370            parent::cleanUp($newIds);
371        }
372    }
373}
374
375/**
376 * SilentUpdate updates the feeds silently for those lame
377 * browsers out there that do not support HTTP Server Push
378 * or AJAX
379 */
380class SilentUpdate extends Update {
381    function SilentUpdate($cid) {
382        parent::Update($doPopulate = false, $updatePrivateAlso = false, $cid);
383    }
384
385    function render() {
386        $newIds = array();
387        $ret = update("");
388        if (is_array($ret)) {
389            $newIds = $ret[1];
390        }
391
392        parent::cleanUp($newIds);
393
394        if (!array_key_exists('silent', $_GET)) {
395            rss_redirect();
396        }
397
398    }
399}
400
401function pushHeaderCallBack() {
402    echo "WARNING: YOUR BROWSER DOESN'T SUPPORT THIS SERVER-PUSH TECHNOLOGY.";
403    echo "\n".PUSH_BOUNDARY."\n";
404    echo "Content-Type: text/html\n\n";
405    flush();
406}
407
408function pushFooterCallBack() {
409    if (defined("UPDATE_ERROR") && UPDATE_ERROR)  {
410        sleep(10);
411    }
412
413    echo "\n".PUSH_BOUNDARY."\n";
414    echo "Content-Type: text/html\n\n"
415    ."<!DOCTYPE html PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\">\n"
416    ."<html>\n"."<head>\n"."<title>Redirecting...</title>\n"
417    ."<meta http-equiv=\"refresh\" content=\"0;url=index.php\"/>\n"
418    ."</head>\n"."<body/>\n"."</html>";
419
420    echo "\n".PUSH_BOUNDARY."\n";
421    echo "WARNING: YOUR BROWSER DOESN'T SUPPORT THIS SERVER-PUSH TECHNOLOGY.\n";
422
423    flush();
424}
425
426/**
427 * This is the function that will handle the server-side AJAX update request
428 */
429function ajaxUpdate($ids) {
430
431    $aids = explode(GROUP_SPLITTER,$ids);
432    $sret = array();
433    foreach($aids as $id) {
434        $ret = update($id);
435        if (is_array($ret)) {
436            $error = $ret[0];
437            $unread = implode(SUB_SUB_SPLITTER,$ret[1]); //count($ret[1]);
438        } else {
439            $error = 0;
440            $unread = 0;
441        }
442        list($label,$cls) = AJAXUpdate::magpieError($error);
443        $sret[] = "$id".SUB_SPLITTER."$unread".SUB_SPLITTER."$label".SUB_SPLITTER."$cls";
444    }
445    // just cat the return elements together, as SAJAX
446    // can't handle complex return types yet.
447    return trim(implode(GROUP_SPLITTER,$sret));
448}
449
450function ajaxUpdateCleanup($ids) {
451    $aids = explode(GROUP_SPLITTER,$ids);
452    Update::cleanUp($aids);
453    return 0;
454}
455
456function ajaxUpdateJavascript () {
457    echo sajax_get_javascript();
458    ?>
459    /// End Sajax javascript
460    /// From here on: Copyright (C) 2003 - 2006 Marco Bonetti, gregarius.net
461    /// Released under GPL
462
463
464    document.cdata = new Array();
465    document.new_ids = new Array();
466    document.returnedUpdates = 0;
467    document.feedCount = 0;
468    document.feedPointer=0;
469
470
471    /**
472     * main entry point: fetch the different channel ids and launch the
473     * channel updates. Might want to have this synchronous to be nice on
474     * the webserver, not sure whether it can be done with Sajax, though
475     */
476    function doUpdate() {
477
478        var ids = new Array();
479        kids = document.getElementById('updatetable').getElementsByTagName("tr");
480        var i=0;
481        var added = 0;
482        //collect feed ids, titles
483        for (i=0; i < kids.length; i++) {
484            var id = kids[i].id.replace(/[^0-9]/gi,'');
485            if (id) {
486                ids[added++] = id;
487                if (!document.cdata[id])  {
488                    var title = '';
489                    if ( tdl = document.getElementById('u_l_' + id)) {
490                        title = tdl.innerHTML;
491                        if (title) {
492                            document.cdata[id] = title;
493                        }
494                    }
495                }
496            }
497        }
498
499        document.feedCount = added;
500        var batch = new Array();
501        var j=0;
502        for (j=0;(j+document.feedPointer) < document.feedCount && j < <?php echo  AJAX_BATCH_SIZE ?>;j++) {
503            batch[j]=ids[j+document.feedPointer];
504            if (tdr = document.getElementById('u_r_' + batch[j])) {
505                tdr.innerHTML = '<?php echo  UPDATING ?>';
506            }
507        }
508        document.feedPointer += j;
509
510        ajaxUpdate(batch);
511
512    }
513
514
515    /**
516     * AJAX Callback function: split the returned data into variables
517     * and play some DOM tricks
518     */
519    function ajaxUpdate_cb(data) {
520        //alert(data);
521        superdarr = data.replace(/[^0-9a-zA-Z\(\)\|\s\.,]/gi,'').split('<?php echo  GROUP_SPLITTER ?>');
522        var lastId = 0;
523        for (var i=0;i<superdarr.length;i++) {
524            darr = superdarr[i].replace(/[^0-9a-zA-Z\(\)\|\s,\.]/gi,'').split('<?php echo  SUB_SPLITTER ?>');
525
526            // channel ID
527            id=darr[0];
528            lastId = id;
529            // unread count
530            if (darr[1] && (unread_ids = darr[1].split('<?php echo  SUB_SUB_SPLITTER ?>'))) {
531                //alert('unread ids: ' + unread_ids);
532                document.new_ids=document.new_ids.concat(unread_ids);
533                //alert('document ids: ' + document.new_ids);
534                unread = unread_ids.length;
535            } else {
536                unread=0;
537            }
538
539            // an error/result-label
540            label=darr[2];
541            // class to be applied to the label, unused for now
542            cls=darr[3];
543
544            // update the table for this row
545            if (mtd = document.getElementById('u_m_'+id)) {
546                //alert(cls);
547                mtd.innerHTML = '<span class="' + cls + '">' + label + '</span>';
548            }
549            if (rtd = document.getElementById('u_r_'+id)) {
550                rtd.innerHTML = unread;
551            }
552
553            // hoorray, we got a result back.
554            document.returnedUpdates++;
555
556        }
557
558        if (document.feedPointer < document.feedCount) {
559            if (document.returnedUpdates >= document.feedPointer -
560                               <?php echo AJAX_BATCH_SIZE * (AJAX_PARALLEL_SIZE - 1) ?>) {
561                doUpdate();
562            }
563        } else {
564            ajaxUpdateCleanup();
565            window.setTimeout('redirect()', 3000);
566        }
567
568    }
569    function ajaxUpdateCleanup_cb(dummy) {}
570
571    function redirect() {
572        document.location = "index.php";
573    }
574
575    function ajaxUpdate(batch) {
576        sBatch='';
577        //alert(batch.length);
578        for(var i=0;i<batch.length;i++) {
579            sBatch += batch[i];
580            if (i<batch.length-1) {
581                sBatch += '<?php echo  GROUP_SPLITTER ?>';
582            }
583        }
584        x_ajaxUpdate(sBatch,ajaxUpdate_cb);
585    }
586
587    function ajaxUpdateCleanup() {
588        ids = '';
589        for(var i=0;i< document.new_ids.length;i++) {
590            var id = Number(document.new_ids[i]);
591            if (id > 0) {
592                ids += id ;
593                if (i<document.new_ids.length-1) {
594                    ids += '<?php echo  GROUP_SPLITTER ?>';
595                }
596            }
597        }
598        //alert(ids);
599        if (ids != '') {
600            x_ajaxUpdateCleanup(ids,ajaxUpdateCleanup_cb);
601        }
602    }
603
604    <?php
605    flush();
606}
607
608
609
610?>
Note: See TracBrowser for help on using the browser.