rss = &$GLOBALS['rss'];
$this->id = $id;
$this->flags = $unread;
if ($title) {
$this->title = $title;
} elseif ($description) {
$this->title = trim(firstNwords($description));
}
if (!$this->title) {
$this->title = "[nt]";
}
$this->escapedTitle = rss_uri($title); //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 & RSS_MODE_UNREAD_STATE;
$this ->isPrivate = $unread & RSS_MODE_PRIVATE_STATE;
$this ->isDeleted = $unread & RSS_MODE_DELETED_STATE;
$this ->isSticky = $unread & RSS_MODE_STICKY_STATE;
$this ->isFlag = $unread & RSS_MODE_FLAG_STATE;
//$this -> key = md5(rand(0,10000));
}
function setParent(&$parent) {
$this-> parent=$parent;
}
/**
* Renders a single RSS item
*/
function render() {
$this-> rss -> currentItem = $this;
include($this-> rss -> getTemplateFile("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);
$this->escapedTitle = rss_uri($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->flags & RSS_MODE_UNREAD_STATE) {
$this -> hasUnreadItems = true;
}
}
/**
* Renders a single Feed
*/
function render() {
$this-> rss -> currentFeed = &$this;
//echo $GLOBALS['rss']->renderOptions;
$this -> setCollapseState($this-> rss ->renderOptions);
include($this-> rss ->getTemplateFile("feed.php"));
}
}
/**
* The ItemList is the main entry point for rendering items: one would:
*
* - Instantiate a new ItemList
* - Invoke the
populate method, giving specific
* information on what should be fetched from the database via the sqlWhere parameter
* Render the list
*
*/
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 i.unread & " . RSS_MODE_UNREAD_STATE . " 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 methdo
* 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, i.unread, "
."i.url, i.enclosure, i.author, i.description, c.icon, "
." unix_timestamp(coalesce(i.pubdate,i.added)) as ts, "
." i.pubdate is not null as ispubdate, i.id "
." , null "
//.", r.rating "
;
$this -> _sqlActualFrom = getTable("item") ." i "
//." left join " . getTable("rating") ." r on (i.id = r.iid) "
." inner join " . getTable("channels")." c on (c.id = i.cid) "
." inner join " . getTable("folders") ." f on (f.id = c.parent) ";
$this -> _sqlActualWhere = (false == $includeDeprecated ? " (c.mode & ".RSS_MODE_DELETED_STATE.")=0 and " : "")
." (i.unread & ".RSS_MODE_DELETED_STATE.")=0 and ";
if (hidePrivate()) {
$this -> _sqlActualWhere .= " (i.unread & ".RSS_MODE_PRIVATE_STATE.")=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 = " $itemCount OFFSET $startItem";
$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_, $iurl_, $ienclosure_, $iauthor_, $idescr_, $cicon_, $its_, $iispubdate_, $iid_, $rrating_) = $GLOBALS['rss_db']->rss_fetch_row($res)) {
// Built a new Item
$i = new Item($iid_, $ititle_, $iurl_, $ienclosure_, $cid_, $iauthor_, $idescr_, $its_, $iispubdate_, $iunread_, $rrating_);
// 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_ & RSS_MODE_UNREAD_STATE) {
$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);
include($this-> rss ->getTemplateFile($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 .= (ereg('\?',$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() {
include($this-> _parent -> rss -> getTemplateFile('pagination.php'));
}
}
class PaginatedItemList extends ItemList {
var $page;
var $navigation;
var $itemsPerPage = 0;
var $numItems = 0;
function PaginatedItemList($itemsPerPage=null) {
parent::ItemList();
if (isset($_REQUEST['page'])) {
$this -> page = sanitize($_REQUEST['page'], RSS_SANITIZER_NUMERIC);
} else {
$this -> page = 0;
}
if ($itemsPerPage === null) {
$this -> itemsPerPage = getConfig('rss.output.frontpage.numitems');
} else {
$this -> itemsPerPage = $itemsPerPage;
}
if ($this -> itemsPerPage <= 0) {
$this -> itemsPerPage = 50;
}
}
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, ($itemCount > 0 ? $this -> itemsPerPage : $itemCount), $hint, $includeDeprecated);
$sql = "select count(*) as cnt "
. " from "
. $this -> _sqlActualFrom
. " where "
. $this -> _sqlActualWhere;
list($this -> numItems) = rss_fetch_row(rss_query($sql));
if ($this -> itemsPerPage && $this -> numItems > $this -> itemsPerPage) {
$this -> navigation = new ItemListNavigation($this);
}
}
}
?>