| 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 | |
|---|
| 28 | |
|---|
| 29 | define('COLLAPSED_FOLDERS_COOKIE', 'collapsedfolders'); |
|---|
| 30 | |
|---|
| 31 | /** |
|---|
| 32 | * A FeedListItem object holds a single item in the feeds sidecolumn |
|---|
| 33 | */ |
|---|
| 34 | class FeedListItem { |
|---|
| 35 | |
|---|
| 36 | /** Database id (e.g. item.cid, channels.id) for this feed */ |
|---|
| 37 | var $id; |
|---|
| 38 | /** Feed title */ |
|---|
| 39 | var $title; |
|---|
| 40 | /** URL of this feed */ |
|---|
| 41 | var $url; |
|---|
| 42 | /** URL of this feed, escaped from login/password information, if any */ |
|---|
| 43 | var $publicUrl; |
|---|
| 44 | /** URL of the website publishing this feed */ |
|---|
| 45 | var $siteurl; |
|---|
| 46 | /** Name of the folder holding this feed */ |
|---|
| 47 | var $name; |
|---|
| 48 | /** ID of the folder holding this feed (e.g. channels.parent) */ |
|---|
| 49 | var $parent; |
|---|
| 50 | /** URL of the icon for this feed, probably offsite */ |
|---|
| 51 | var $icon; |
|---|
| 52 | /** The "description" for this feed. (e.g. channel.description) */ |
|---|
| 53 | var $descr = ""; |
|---|
| 54 | /** Feed "mode" (e.g. private, deprecated, ...)*/ |
|---|
| 55 | var $mode; |
|---|
| 56 | /** True when the user is reading the current feed in "feed mode" */ |
|---|
| 57 | var $isActiveFeed; |
|---|
| 58 | |
|---|
| 59 | /** |
|---|
| 60 | * Constructor. Fills in the instance variables, escapes urls accordingly |
|---|
| 61 | */ |
|---|
| 62 | function FeedListItem($id, $title, $url, $siteurl, $name, $parent, $icon, $descr, $mode, $unreadCount) { |
|---|
| 63 | $this->id = $id; |
|---|
| 64 | $this->title = $title; |
|---|
| 65 | $this->url = $url; |
|---|
| 66 | $this->publicUrl = preg_replace( |
|---|
| 67 | '|(https?://)([^:]+:[^@]+@)(.+)$|','\1\3',$url |
|---|
| 68 | ); |
|---|
| 69 | $this->siteurl = $siteurl; |
|---|
| 70 | $this->name = $name; |
|---|
| 71 | $this->parent = $parent; |
|---|
| 72 | |
|---|
| 73 | |
|---|
| 74 | if ( getConfig('rss.output.showfavicons') && $icon){ |
|---|
| 75 | if (substr($icon,0,5) == 'blob:') { |
|---|
| 76 | $this->icon = getPath() . "extlib/favicon.php?url=". rss_real_escape_string(substr($icon,5)); |
|---|
| 77 | } else { |
|---|
| 78 | $this->icon = $icon; |
|---|
| 79 | } |
|---|
| 80 | } elseif (getConfig('rss.output.showfavicons')) { |
|---|
| 81 | $this->icon = getExternalThemeFile("media/noicon.png"); |
|---|
| 82 | } else { |
|---|
| 83 | $this->icon = false; |
|---|
| 84 | } |
|---|
| 85 | |
|---|
| 86 | $this->descr = $descr; |
|---|
| 87 | $this->mode = $mode; |
|---|
| 88 | |
|---|
| 89 | if (getConfig('rss.output.usemodrewrite')) { |
|---|
| 90 | $this->rlink = getPath(rss_uri($title)) . "/"; |
|---|
| 91 | } else { |
|---|
| 92 | $this->rlink = getPath()."feed.php?channel=$id"; |
|---|
| 93 | } |
|---|
| 94 | |
|---|
| 95 | |
|---|
| 96 | if ($unreadCount > 0) { |
|---|
| 97 | $this->rdLbl= sprintf(__('<strong id="%s" style="%s">(%d unread)</strong>'), "cid$id","",$unreadCount); |
|---|
| 98 | $this->class_= "feed title unread"; |
|---|
| 99 | } else { |
|---|
| 100 | $this->rdLbl= ""; |
|---|
| 101 | $this->class_= "feed title"; |
|---|
| 102 | } |
|---|
| 103 | } |
|---|
| 104 | |
|---|
| 105 | |
|---|
| 106 | /** |
|---|
| 107 | * Renders this FeedListItem: Moves the superglobal "current feed" pointer |
|---|
| 108 | * to this, then includes the relevant template |
|---|
| 109 | */ |
|---|
| 110 | function render() { |
|---|
| 111 | $GLOBALS['rss']->currentFeedsFeed = $this; |
|---|
| 112 | include($GLOBALS['rss'] ->getTemplateFile("feedsfeed.php")); |
|---|
| 113 | } |
|---|
| 114 | |
|---|
| 115 | } |
|---|
| 116 | |
|---|
| 117 | |
|---|
| 118 | class FeedFolder { |
|---|
| 119 | var $feeds = array (); |
|---|
| 120 | var $name; |
|---|
| 121 | var $id; |
|---|
| 122 | var $isCollapsed = false; |
|---|
| 123 | var $rlink; |
|---|
| 124 | var $isRootFolder = false; |
|---|
| 125 | var $rootList; |
|---|
| 126 | function FeedFolder($name, $id, &$rootList) { |
|---|
| 127 | $this->name = $name; |
|---|
| 128 | $this->id = $id; |
|---|
| 129 | if (getConfig('rss.output.usemodrewrite')) { |
|---|
| 130 | $this->rlink = $this -> makeFolderUrl($name); // getPath().preg_replace("/[^a-zA-Z0-9_]/", "_", $name)."/"; |
|---|
| 131 | } else { |
|---|
| 132 | $this->rlink = getPath()."feed.php?folder=$id"; |
|---|
| 133 | } |
|---|
| 134 | $this->isRootFolder = ($id == 0); |
|---|
| 135 | $this->rootList = $rootList; |
|---|
| 136 | } |
|---|
| 137 | |
|---|
| 138 | function makeFolderUrl($fn) { |
|---|
| 139 | return getPath( |
|---|
| 140 | preg_replace('#\s#','_',sanitize($fn,RSS_SANITIZER_URL)) |
|---|
| 141 | ) .'/'; |
|---|
| 142 | } |
|---|
| 143 | |
|---|
| 144 | function render() { |
|---|
| 145 | if ((!getConfig('rss.output.minimalchannellist')) || ($this->feeds)) { |
|---|
| 146 | $GLOBALS['rss']->currentFeedsFolder = $this; |
|---|
| 147 | require($GLOBALS['rss'] ->getTemplateFile("feedsfolder.php")); |
|---|
| 148 | } |
|---|
| 149 | } |
|---|
| 150 | } |
|---|
| 151 | |
|---|
| 152 | class FeedList { |
|---|
| 153 | |
|---|
| 154 | var $collapsed_folders = array (); |
|---|
| 155 | var $collapsed_ids = array (); |
|---|
| 156 | var $folders = array (); |
|---|
| 157 | var $activeId; |
|---|
| 158 | var $feedCount = 0; |
|---|
| 159 | var $columnTitle; |
|---|
| 160 | var $stats; |
|---|
| 161 | |
|---|
| 162 | function FeedList($activeId) { |
|---|
| 163 | _pf('FeedList() ctor'); |
|---|
| 164 | $this ->columnTitle= __('Feeds'); |
|---|
| 165 | $this->activeId = $activeId; |
|---|
| 166 | $this->loadCollapsedState(); |
|---|
| 167 | $this->populate(); |
|---|
| 168 | } |
|---|
| 169 | |
|---|
| 170 | function getStats() { |
|---|
| 171 | _pf('getStats()'); |
|---|
| 172 | _pf(' ... unreadCount'); |
|---|
| 173 | |
|---|
| 174 | $unread = getUnreadCount(null, null); |
|---|
| 175 | _pf(' ... done: unreadCount'); |
|---|
| 176 | |
|---|
| 177 | _pf(' ... totalCount'); |
|---|
| 178 | $sql ="select count(*) from ".getTable("item") . "i " |
|---|
| 179 | ."inner join " . getTable('channels') . " c " |
|---|
| 180 | ." on c.id = i.cid " |
|---|
| 181 | ." where not(i.unread & ".RSS_MODE_DELETED_STATE.") " |
|---|
| 182 | ." and not (c.mode & " .RSS_MODE_DELETED_STATE.")" |
|---|
| 183 | . (hidePrivate()? " and not(unread & ".RSS_MODE_PRIVATE_STATE.")":""); |
|---|
| 184 | |
|---|
| 185 | //echo $sql; |
|---|
| 186 | $res = rss_query($sql); |
|---|
| 187 | list ($total) = rss_fetch_row($res); |
|---|
| 188 | _pf(' ... done: totalCount'); |
|---|
| 189 | |
|---|
| 190 | |
|---|
| 191 | _pf(' ... feedsCount'); |
|---|
| 192 | $res = rss_query("select count(*) from " |
|---|
| 193 | .getTable("channels")." where not(mode & ".RSS_MODE_DELETED_STATE.")" |
|---|
| 194 | . (hidePrivate()? " and not(mode & ".RSS_MODE_PRIVATE_STATE.")":"") |
|---|
| 195 | ); |
|---|
| 196 | |
|---|
| 197 | list ($channelcount) = rss_fetch_row($res); |
|---|
| 198 | _pf(' ... done: feedsCount'); |
|---|
| 199 | |
|---|
| 200 | $this ->stats = sprintf(__('<strong>%d</strong> items (<strong id="fucnt">%d</strong> unread) in <strong>%d</strong> feeds'), $total, $unread, $channelcount); |
|---|
| 201 | _pf('done: getStats()'); |
|---|
| 202 | return $this -> stats; |
|---|
| 203 | } |
|---|
| 204 | |
|---|
| 205 | |
|---|
| 206 | function loadCollapsedState() { |
|---|
| 207 | _pf('FeedList->loadCollapsedState()...'); |
|---|
| 208 | |
|---|
| 209 | if (getConfig('rss.output.channelcollapse')) { |
|---|
| 210 | |
|---|
| 211 | //read per-user stored collapsed folders |
|---|
| 212 | if (array_key_exists(COLLAPSED_FOLDERS_COOKIE, $_COOKIE)) { |
|---|
| 213 | $this->collapsed_ids = explode(":", $_COOKIE[COLLAPSED_FOLDERS_COOKIE]); |
|---|
| 214 | } elseif (empty($this->collapsed_ids) && getConfig("rss.output.channelcollapsedefault")) { |
|---|
| 215 | // Lets collapse all folders |
|---|
| 216 | $res = rss_query("select id from " . getTable('folders') . " where id != 0"); |
|---|
| 217 | while (list ($this->collapsed_ids[]) = rss_fetch_row($res)) {} |
|---|
| 218 | if (!headers_sent()) { // Sajax does not allow us to set cookies |
|---|
| 219 | setcookie(COLLAPSED_FOLDERS_COOKIE, |
|---|
| 220 | implode(":", $this->collapsed_ids ) , time()+COOKIE_LIFESPAN,getPath()); |
|---|
| 221 | } |
|---|
| 222 | } |
|---|
| 223 | |
|---|
| 224 | //get unread count per folder |
|---|
| 225 | $sql = "select f.id, f.name, count(*) as cnt " |
|---|
| 226 | ." from " .getTable('item') ." i " |
|---|
| 227 | ." inner join " . getTable('channels') . " c on c.id = i.cid " |
|---|
| 228 | ." inner join " . getTable('folders') ." f on f.id = c.parent " |
|---|
| 229 | ." where i.unread & ". RSS_MODE_UNREAD_STATE |
|---|
| 230 | ." and not(i.unread & ". RSS_MODE_DELETED_STATE .")"; |
|---|
| 231 | if (hidePrivate()) { |
|---|
| 232 | $sql .=" and not(unread & " . RSS_MODE_PRIVATE_STATE .") "; |
|---|
| 233 | } |
|---|
| 234 | $sql .= " and not(c.mode & " . RSS_MODE_DELETED_STATE .") "; |
|---|
| 235 | $sql .= " group by f.id"; |
|---|
| 236 | _pf('query'); |
|---|
| 237 | $res = rss_query($sql); |
|---|
| 238 | _pf('ok'); |
|---|
| 239 | while (list ($cid, $cname, $cuc) = rss_fetch_row($res)) { |
|---|
| 240 | $this->collapsed_folders[$cid] = $cuc; |
|---|
| 241 | } |
|---|
| 242 | |
|---|
| 243 | sort($this->collapsed_ids); |
|---|
| 244 | } |
|---|
| 245 | _pf('done'); |
|---|
| 246 | |
|---|
| 247 | } |
|---|
| 248 | |
|---|
| 249 | function getFeedCount() { |
|---|
| 250 | return $this -> feedCount; |
|---|
| 251 | } |
|---|
| 252 | |
|---|
| 253 | function populate() { |
|---|
| 254 | _pf('FeedList->populate() ...'); |
|---|
| 255 | $sql = "select "." c.id, c.title, c.url, c.siteurl, f.name, c.parent, c.icon, c.descr, c.mode "." from ".getTable("channels")." c " |
|---|
| 256 | ."inner join " . getTable("folders")." f on f.id = c.parent"; |
|---|
| 257 | |
|---|
| 258 | if (hidePrivate()) { |
|---|
| 259 | $sql .= " and not(c.mode & ".RSS_MODE_PRIVATE_STATE.") "; |
|---|
| 260 | } |
|---|
| 261 | |
|---|
| 262 | $sql .= " and not(c.mode & ".RSS_MODE_DELETED_STATE.") "; |
|---|
| 263 | |
|---|
| 264 | if (getConfig('rss.config.absoluteordering')) { |
|---|
| 265 | $sql .= " order by f.position asc, c.position asc"; |
|---|
| 266 | } else { |
|---|
| 267 | $sql .= " order by f.name, c.title asc"; |
|---|
| 268 | } |
|---|
| 269 | $res = rss_query($sql); |
|---|
| 270 | $this -> feedCount = rss_num_rows($res); |
|---|
| 271 | |
|---|
| 272 | $ucres = rss_query ("select cid, count(*) from " .getTable("item") |
|---|
| 273 | ." where unread & " . RSS_MODE_UNREAD_STATE |
|---|
| 274 | . " and not(unread & " . RSS_MODE_DELETED_STATE .") group by cid"); |
|---|
| 275 | $uc = array(); |
|---|
| 276 | while (list($uccid,$ucuc) = rss_fetch_row($ucres)) { |
|---|
| 277 | $uc[$uccid]=$ucuc; |
|---|
| 278 | } |
|---|
| 279 | |
|---|
| 280 | while (list ($cid, $ctitle, $curl, $csiteurl, $fname, $cparent, $cico, $cdescr, $cmode) = rss_fetch_row($res)) { |
|---|
| 281 | $ucc= 0; |
|---|
| 282 | if (array_key_exists($cid,$uc)) { |
|---|
| 283 | $ucc=$uc[$cid]; |
|---|
| 284 | } |
|---|
| 285 | $f = new FeedListItem($cid, $ctitle, $curl, $csiteurl, $fname, $cparent, $cico, $cdescr, $cmode, $ucc); |
|---|
| 286 | $f -> isActiveFeed = ($this->activeId && $cid == $this->activeId ); |
|---|
| 287 | if (!array_key_exists($cparent, $this->folders)) { |
|---|
| 288 | $this->folders[$cparent] = new FeedFolder($fname, $cparent,$this); |
|---|
| 289 | } |
|---|
| 290 | //$this->folders[$cparent]->feeds[] = $f; |
|---|
| 291 | if(($ucc != 0) || (!getConfig('rss.output.minimalchannellist'))) { |
|---|
| 292 | $this->folders[$cparent]->feeds[] = $f; |
|---|
| 293 | } |
|---|
| 294 | $this->folders[$cparent]->isCollapsed = in_array($cparent, $this->collapsed_ids) && ($cparent > 0); |
|---|
| 295 | |
|---|
| 296 | } |
|---|
| 297 | _pf('done'); |
|---|
| 298 | } |
|---|
| 299 | |
|---|
| 300 | function render() { |
|---|
| 301 | _pf('FeedList->render() ...'); |
|---|
| 302 | include($GLOBALS['rss'] ->getTemplateFile("feeds.php")); |
|---|
| 303 | _pf('done'); |
|---|
| 304 | } |
|---|
| 305 | } |
|---|
| 306 | ?> |
|---|