rss = &$GLOBALS['rss']; $this->id = $id; if ($title) { $this->title = $title; } elseif ($description) { $this->title = trim(firstNwords($description)); } if (!$this->title) { $this->title = "[nt]"; } $this->escapedTitle = preg_replace("/[^A-Za-z0-9%\.]/", "_", utf8_uri_encode($title)); $this->url = trim($url); $this->enclosure = $enclosure; $this->feed = $parent; $this->author = $author; if ($description) { $this->description = trim($description); } elseif($title) { $this -> description = $title; } $this->date = $date; $this->isPubDate = $isPubDate; $this -> tags=array(); //$this -> rating = $rating; $this ->isUnread = $unread; $this ->isPrivate = $private; //$this ->isDeleted = $deleted; $this ->isSticky = $sticky; $this ->isFlag = $flagged; //$this -> key = md5(rand(0,10000)); } function setParent(&$parent) { $this-> parent=$parent; } /** * Renders a single RSS item */ function render() { $this-> rss -> currentItem = $this; eval($this-> rss -> getCachedTemplateFile("item.php")); } } /** * A feed mirrors the channel database table. It contains a list of Items */ class Feed { var $items = array (); //var $tags = array (); var $title = ""; var $cid = 0; var $iconUrl = ""; var $hasUnreadItems = false; var $collapsed = false; /** * Feed constructor */ function Feed($title, $cid, $icon) { $this -> rss = &$GLOBALS['rss']; $this->title = rss_htmlspecialchars($title); $this->cid = $cid; if (substr($icon,0,5) == 'blob:') { $this->iconUrl = getPath() . "extlib/favicon.php?url=" . rss_real_escape_string(substr($icon,5)); } else { $this->iconUrl = $icon; } $this->escapedTitle = preg_replace("/[^A-Za-z0-9\.]/", "_", $title); } function setCollapseState($options) { // Feed collapsion // $collapsed_ids = array (); if (getConfig('rss.output.channelcollapse')) { if (array_key_exists('collapsedfeeds', $_COOKIE)) { $collapsed_ids = explode(":", $_COOKIE['collapsedfeeds']); } } if (getConfig('rss.output.channelcollapse')) { $this->collapsed = in_array($this->cid, $collapsed_ids) && !($options & (IL_NO_COLLAPSE | IL_CHANNEL_VIEW)) && !($this->hasUnreadItems); if (array_key_exists('collapse', $_GET) && $_GET['collapse'] == $this -> cid) { // expanded -> collapsed $this->collapsed = true; if (!in_array($this -> cid, $collapsed_ids)) { $collapsed_ids[] = $this -> cid; $cookie = implode(":", $collapsed_ids); setcookie('collapsedfeeds', $cookie, time() + COOKIE_LIFESPAN); //echo $this->cid . " -> collapsed"; } } elseif (array_key_exists('expand', $_GET) && $_GET['expand'] == $this -> cid && $this->collapsed) { // collapsed -> expanded $this->collapsed = false; if (in_array($this -> cid, $collapsed_ids)) { $key = array_search($this -> cid, $collapsed_ids); unset ($collapsed_ids[$key]); $cookie = implode(":", $collapsed_ids); setcookie('collapsedfeeds', $cookie, time() + COOKIE_LIFESPAN); } } } else { $this->collapsed = false; } if ($this->hasUnreadItems) { $this->collapsed = false; } //echo $this-> collapsed?"Collapsed":"expanded"; return $this->collapsed; } /** * Adds a single RSS item to this feed */ function addItem(&$item) { $item-> setParent($this); $this->items[] = $item; if ((!$this -> hasUnreadItems) && $item->isUnread) { $this -> hasUnreadItems = true; } } /** * Renders a single Feed */ function render() { $this-> rss -> currentFeed = &$this; //echo $GLOBALS['rss']->renderOptions; $this -> setCollapseState($this-> rss ->renderOptions); eval($this-> rss ->getCachedTemplateFile("feed.php")); } } /** * The ItemList is the main entry point for rendering items: one would: * */ class ItemList { var $feeds = array (); var $unreadCount = 0; var $readCount = 0; var $itemCount = 0; var $rowCount = 0; var $allTags = array(); var $renderOptions = IL_NONE; var $title = ""; var $preRender = array(); var $beforeList = ""; var $afterList = ""; var $ORDER_BY_UNREAD_FIRST=null; var $iidInCid = array(); var $iids = array(); var $unreadIids = array(); var $rss; var $_template; var $_sqlActualWhat = ""; var $_sqlActualFrom = ""; var $_sqlActualWhere= ""; var $_sqlActualOrder= ""; var $_sqlActualLimit= ""; function ItemList() { $this -> _template = 'itemlist.php'; $this -> rss = &$GLOBALS['rss']; // make sure we have a default rendering options defined $this -> setRenderOptions( IL_NONE ); // Predefined alternate ordering $this -> ORDER_BY_UNREAD_FIRST = " order by i2u.flgunread desc, "; if (getConfig('rss.config.absoluteordering')) { $this -> ORDER_BY_UNREAD_FIRST .= " f.position asc, c.position asc"; } else { $this -> ORDER_BY_UNREAD_FIRST .= " f.name asc, c.title asc"; } $this -> ORDER_BY_UNREAD_FIRST .= ", i.added desc, i.id asc"; } /** * Populates a an ItemList with items from the Database. Note that this method * can be invoked several times on the same ItemList object instance: upon each * call the new items will be aggregated to the existing ones. * * @param sqlWhere specifies what should be fetched * @param sqlOrder (optional) specifies a different item ordering * @param sqlLimit (optional) specifies how many items should be fetched * @param includeDeprecated (optional) specifies if deprecated feeds should be fetched */ function populate($sqlWhere, $sqlOrder="", $startItem = 0, $itemCount = -1, $hint = ITEM_SORT_HINT_MIXED, $includeDeprecated = false) { _pf('ItemList::populate()'); $this -> _sqlActualWhat = " i.title, c.title, c.id," . " i2u.flgunread, i2u.flgprivate, i2u.flgsticky, i2u.flgdeleted, i2u.flgflagged," ." i.url, i.enclosure, i.author, i.description, c.icon, " ." unix_timestamp(ifnull(i.pubdate,i.added)) as ts, " ." i.pubdate is not null as ispubdate, i.id " //.", r.rating " ; $this -> _sqlActualFrom = "" .getTable('item2user') . " i2u " ." left join " .getTable("item") ." i " ." on (i2u.fkiid = i.id)" //." inner join " . getTable("rating") ." r on (i.id = r.iid), " . " inner join " . getTable('channels2user') ." c2u on (i.cid=c2u.fkcid)" . " inner join " . getTable("channels")." c on (c2u.fkcid=c.id)" . " inner join " . getTable("folders") ." f on (c.parent=f.id) "; $this -> _sqlActualWhere = " i2u.fkuid= " . $GLOBALS['rssuser']->getUserId() . " "; if (hidePrivate()) { $this -> _sqlActualWhere .= " and i2u.flgprivate=0 "; } $this -> _sqlActualWhere .= " and " . (false == $includeDeprecated ? " c2u.flgdeleted=0 and " : "") ." i2u.flgdeleted=0 and "; if ($this -> _sqlActualWhere) { $this -> _sqlActualWhere .= $sqlWhere ." and "; } $this -> _sqlActualWhere .= " 1=1 "; /// Order by $sqlOrder = rss_plugin_hook("rss.plugins.items.order",$sqlOrder); if ($sqlOrder == "") { switch ($hint) { case ITEM_SORT_HINT_MIXED: case ITEM_SORT_HINT_READ: $skey = 'read'; break; case ITEM_SORT_HINT_UNREAD: default: $skey = 'unread'; break; } if (!getConfig('rss.config.feedgrouping')) { if(getConfig("rss.config.datedesc.$skey")){ $this -> _sqlActualOrder = " ts desc, f.position asc, c.position asc "; }else{ $this -> _sqlActualOrder = " ts asc, f.position asc, c.position asc "; } } elseif (getConfig('rss.config.absoluteordering')) { $this -> _sqlActualOrder = " f.position asc, c.position asc"; } else { $this -> _sqlActualOrder = " f.name asc, c.title asc"; } if(getConfig("rss.config.datedesc.$skey")){ $this -> _sqlActualOrder .= ", ts desc, i.id asc"; }else{ $this -> _sqlActualOrder .= ", ts asc, i.id asc"; } } else { $this -> _sqlActualOrder = " $sqlOrder "; } if (($itemCount < 0) || ($itemCount > RSS_DB_MAX_QUERY_RESULTS)) { $itemCount = RSS_DB_MAX_QUERY_RESULTS; } $this -> _sqlActualLimit = " $startItem, $itemCount"; $sql = "select " .$this -> _sqlActualWhat . " from " .$this -> _sqlActualFrom . " where " . $this -> _sqlActualWhere . " order by " . $this -> _sqlActualOrder . " limit " . $this -> _sqlActualLimit; //echo $sql; $this -> iids = array(); $res = $GLOBALS['rss_db']->rss_query($sql); $this -> rowCount = $GLOBALS['rss_db']->rss_num_rows($res); $prevCid = -1; $curIdx = 0; $f=null; while (list ($ititle_, $ctitle_, $cid_, $iunread_, $iprivate_, $isticky_, $ideleted_, $iflagged_, $iurl_, $ienclosure_, $iauthor_, $idescr_, $cicon_, $its_, $iispubdate_, $iid_) = $GLOBALS['rss_db']->rss_fetch_row($res)) { // Built a new Item $i = new Item($iid_, $ititle_, $iurl_, $ienclosure_, $cid_, $iauthor_, $idescr_, $its_, $iispubdate_, $iunread_, $iprivate_, $isticky_, $iflagged_); // no dupes, please if (in_array($iid_,$this -> iids)) { $this -> rowCount--; continue; } // Allow for some item filtering before it is rendered if (($i = rss_plugin_hook('rss.plugins.items.beforerender', $i)) == null) { $this -> rowCount--; continue; } // See if we have a channel for it if ($cid_ != $prevCid) { $f = new Feed($ctitle_, $cid_, $cicon_); $this->feeds[] = $f; $curIdx = count($this->feeds)-1; $prevCid = $cid_; } $this -> iidInCid[$iid_] = $curIdx; // Add it to the channel $this -> iids[] = $iid_; $this -> feeds[$curIdx] ->addItem($i); // Some stats... $this -> itemCount++; if ($iunread_) { $this -> unreadCount++; $this -> unreadIids[] = $iid_; } else { $this -> readCount++; } } // Tags! if (count($this -> iids)) { // fetch the tags for the items; $sql = "select t.tag,m.fid,i.cid " ." from " . getTable('tag')." t " . " inner join " . getTable('metatag')." m on m.tid = t.id" . " inner join " . getTable('item')." i on i.id = m.fid" ." where m.ttype = 'item' and m.fid in (".implode(",", $this -> iids).")"; $res = $GLOBALS['rss_db']->rss_query($sql); while (list ($tag_, $iid_, $cid_) = $GLOBALS['rss_db']->rss_fetch_row($res)) { if (array_key_exists($iid_, $this -> iidInCid)) { $idx = $this->iidInCid[$iid_]; while (list($key, $item) = each($this ->feeds[$idx] -> items)) { if ($item -> id == $iid_) { $this ->feeds[$idx] -> items[$key] -> tags[] = $tag_; break; } } reset($this ->feeds[$idx] -> items); } if (array_key_exists($tag_,$this -> allTags)) { $this -> allTags[ $tag_ ]++; } else { $this -> allTags[ $tag_ ]=1; } } } _pf('done: ItemList::populate()'); } function removeItem($feedId,$itemId,$uncount) { unset($this->feeds[$feedId]->items[$itemId]); if ($uncount) { $this ->itemCount--; } if (count($this->feeds[$feedId]->items) == 0) { unset($this->feeds[$feedId]); } } function setTitle($title) { $this->title=$title; } function setRenderOptions($options) { $this-> rss -> renderOptions |= $options; $this -> renderOptions = $options; } function render() { _pf("ItemList -> render()"); if (($this->readCount + $this->unreadCount) == 0 && $this->beforeList == "") { return; } $this-> rss -> currentItemList = $this; rss_plugin_hook('rss.plugins.items.beforeitems', null); eval($this-> rss ->getCachedTemplateFile($this -> _template)); _pf("done: ItemList -> render()"); rss_plugin_hook('rss.plugins.items.afteritems', null); } } class ItemListNavigation { var $_parent; var $pages; function ItemListNavigation(&$il) { $this -> _parent = $il; $this -> pages = array(); $base = $_SERVER["REQUEST_URI"]; if (!preg_match('#page=[0-9]+$#',$base)) { $base .= "?page=0"; } $last = ceil( $this -> _parent -> numItems / $this -> _parent -> itemsPerPage); $lastin = 0; for ($i = 0; $i < $last; $i++) { if ($i == 0 || $i == $last-1 || abs($i - $this -> _parent -> page) < 3) { $url = preg_replace('#^(.+)page=[0-9]+$#','${1}page='.$i, $base); $this -> pages[$i] = array($url, $i == $this -> _parent -> page, false); $lastin = $i; } elseif ($i - 1 == $lastin) { $this -> pages[$i] = array(null,false,true); } } } function render() { eval($this-> _parent -> rss ->getCachedTemplateFile('pagination.php')); } } class PaginatedItemList extends ItemList { var $page; var $navigation; var $itemsPerPage = 0; var $numItems = 0; function PaginatedItemList() { parent::ItemList(); if (isset($_REQUEST['page'])) { $this -> page = sanitize($_REQUEST['page'], RSS_SANITIZER_NUMERIC); } else { $this -> page = 0; } $this -> itemsPerPage = getConfig('rss.output.frontpage.numitems'); } function populate($sqlWhere, $sqlOrder="", $startItem = 0, $itemCount = -1, $hint = ITEM_SORT_HINT_MIXED, $includeDeprecated = false) { $si = $this -> page * $this -> itemsPerPage; parent::populate($sqlWhere, $sqlOrder, $si, $this -> itemsPerPage, $hint, $includeDeprecated); $sql = "select count(*) as cnt " . " from " . $this -> _sqlActualFrom . " where " . $this -> _sqlActualWhere; list($this -> numItems) = rss_fetch_row(rss_query($sql)); $this -> navigation = new ItemListNavigation($this); } } ?>