'); // Suffix to encapsulate data in php code.
define('MIN_TIME_UPDATE', 5); // Minimum accepted time for update
// Updates check frequency. 86400 seconds = 24 hours
define('UPDATECHECK_INTERVAL', 86400);
// fix some warning
date_default_timezone_set('Europe/Paris');
class Feed
{
public $dataFile = '';
public $cacheDir = '';
public $kfc;
private $_data = array();
public function __construct($dataFile, $cacheDir, $kfc)
{
$this->kfc = $kfc;
$this->dataFile = $dataFile;
$this->cacheDir = $cacheDir;
}
public function getData()
{
return $this->_data;
}
public function setData($data)
{
$this->_data = $data;
}
public function initData()
{
$this->_data['feeds'] = array(
MyTool::smallHash('http://tontof.net/?rss') => array(
'title' => 'Tontof',
'foldersHash' => array(),
'timeUpdate' => 'auto',
'lastUpdate' => 0,
'nbUnread' => 0,
'nbAll' => 0,
'htmlUrl' => 'http://tontof.net',
'xmlUrl' => 'http://tontof.net/?rss',
'description' => 'A simple and smart (or stupid) blog'));
$this->_data['folders'] = array();
$this->_data['items'] = array();
$this->_data['newItems'] = array();
}
public function loadData()
{
if (empty($this->_data)) {
if (file_exists($this->dataFile)) {
$this->_data = unserialize(
gzinflate(
base64_decode(
substr(
file_get_contents($this->dataFile),
strlen(PHPPREFIX),
-strlen(PHPSUFFIX)
)
)
)
);
return true;
} else {
$this->initData();
$this->writeData();
return false;
}
}
// data already loaded
return true;
}
public function writeData()
{
if ($this->kfc->isLogged()) {
$write = @file_put_contents(
$this->dataFile,
PHPPREFIX
. base64_encode(gzdeflate(serialize($this->_data)))
. PHPSUFFIX,
LOCK_EX
);
if (!$write) {
die("Can't write to " . $this->dataFile);
}
}
}
public function setFeeds($feeds) {
$this->_data['feeds'] = $feeds;
}
public function getFeeds()
{
return $this->_data['feeds'];
}
public function sortFeeds()
{
uasort(
$this->_data['feeds'],
'Feed::sortByTitle'
);
}
public function sortFolders()
{
uasort(
$this->_data['folders'],
'Feed::sortByOrder'
);
}
public function getFeedsView()
{
$feedsView = array('all' => array('title' => Intl::msg('All feeds'), 'nbUnread' => 0, 'nbAll' => 0, 'feeds' => array()), 'folders' => array());
foreach ($this->_data['feeds'] as $feedHash => $feed) {
if (isset($feed['error'])) {
$feed['error'] = $feed['error'];
}
if (isset($feed['nbUnread'])) {
$feedsView['all']['nbUnread'] += $feed['nbUnread'];
} else {
$feedsView['all']['nbUnread'] += $feed['nbAll'];
}
$feedsView['all']['nbAll'] += $feed['nbAll'];
if (empty($feed['foldersHash'])) {
$feedsView['all']['feeds'][$feedHash] = $feed;
if (!isset($feed['nbUnread'])) {
$feedsView['all']['feeds'][$feedHash]['nbUnread'] = $feed['nbAll'];
}
} else {
foreach ($feed['foldersHash'] as $folderHash) {
$folder = $this->getFolder($folderHash);
if ($folder !== false) {
if (!isset($feedsView['folders'][$folderHash]['title'])) {
$feedsView['folders'][$folderHash]['title'] = $folder['title'];
$feedsView['folders'][$folderHash]['isOpen'] = $folder['isOpen'];
$feedsView['folders'][$folderHash]['nbUnread'] = 0;
$feedsView['folders'][$folderHash]['nbAll'] = 0;
if (isset($folder['order'])) {
$feedsView['folders'][$folderHash]['order'] = $folder['order'];
} else {
$feedsView['folders'][$folderHash]['order'] = 0;
}
}
$feedsView['folders'][$folderHash]['feeds'][$feedHash] = $feed;
$feedsView['folders'][$folderHash]['nbUnread'] += $feed['nbUnread'];
$feedsView['folders'][$folderHash]['nbAll'] += $feed['nbAll'];
}
}
}
}
uasort($feedsView['folders'], 'Feed::sortByOrder');
return $feedsView;
}
public function getFeed($feedHash)
{
if (isset($this->_data['feeds'][$feedHash])) {
// FIX: problem of version 6 &
$this->_data['feeds'][$feedHash]['xmlUrl'] = preg_replace('/&(amp;)*/', '&', $this->_data['feeds'][$feedHash]['xmlUrl']);
$this->_data['feeds'][$feedHash]['htmlUrl'] = preg_replace('/&(amp;)*/', '&', $this->_data['feeds'][$feedHash]['htmlUrl']);
return $this->_data['feeds'][$feedHash];
}
return false;
}
public function getFaviconFeed($feedHash)
{
$htmlUrl = $this->_data['feeds'][$feedHash]['htmlUrl'];
//$url = 'https://s2.googleusercontent.com/s2/favicons?domain='.parse_url($htmlUrl, PHP_URL_HOST);
$url = 'https://icons.duckduckgo.com/ip3/'.parse_url($htmlUrl, PHP_URL_HOST).'.ico';
$file = FAVICON_DIR.'/favicon.'.$feedHash.'.ico';
if ($this->kfc->isLogged() && $this->kfc->addFavicon) {
MyTool::grabToLocal($url, $file);
}
if (file_exists($file)) {
return $file;
} else {
return $url;
}
}
public function getFeedHtmlUrl($feedHash)
{
if (isset($this->_data['feeds'][$feedHash]['htmlUrl'])) {
return $this->_data['feeds'][$feedHash]['htmlUrl'];
}
return false;
}
public function getFeedTitle($feedHash)
{
if (isset($this->_data['feeds'][$feedHash]['title'])) {
return $this->_data['feeds'][$feedHash]['title'];
}
return false;
}
public function loadFeed($feedHash)
{
if (!isset($this->_data['feeds'][$feedHash]['items'])) {
$this->_data['feeds'][$feedHash]['items'] = array();
if (file_exists($this->cacheDir.'/'.$feedHash.'.php')) {
$items = unserialize(
gzinflate(
base64_decode(
substr(
file_get_contents($this->cacheDir.'/'.$feedHash.'.php'),
strlen(PHPPREFIX),
-strlen(PHPSUFFIX)
)
)
)
);
$this->_data['feeds'][$feedHash]['items'] = $items;
}
}
}
public function editFeed(
$feedHash,
$title,
$description,
$foldersHash,
$timeUpdate,
$htmlUrl)
{
if (isset($this->_data['feeds'][$feedHash])) {
if (!empty($title)) {
$this->_data['feeds'][$feedHash]['title'] = $title;
}
if (!empty($description)) {
$this->_data['feeds'][$feedHash]['description'] = $description;
}
if (!empty($htmlUrl)) {
$this->_data['feeds'][$feedHash]['htmlUrl'] = $htmlUrl;
}
$this->_data['feeds'][$feedHash]['foldersHash'] = $foldersHash;
$this->_data['feeds'][$feedHash]['timeUpdate'] = 'auto';
if (!empty($timeUpdate)) {
if ($timeUpdate == 'max') {
$this->_data['feeds'][$feedHash]['timeUpdate'] = $timeUpdate;
} else {
$this->_data['feeds'][$feedHash]['timeUpdate'] = (int) $timeUpdate;
$maxUpdate = $this->kfc->maxUpdate;
if ($this->_data['feeds'][$feedHash]['timeUpdate'] < MIN_TIME_UPDATE
|| $this->_data['feeds'][$feedHash]['timeUpdate'] > $maxUpdate
) {
$this->_data['feeds'][$feedHash]['timeUpdate'] = 'auto';
}
}
}
}
}
public function removeFeed($feedHash)
{
if (isset($this->_data['feeds'][$feedHash])) {
unset($this->_data['feeds'][$feedHash]);
unlink($this->cacheDir. '/' .$feedHash.'.php' );
foreach (array_keys($this->_data['items']) as $itemHash) {
if (substr($itemHash, 0, 6) === $feedHash) {
unset($this->_data['items'][$itemHash]);
}
}
foreach (array_keys($this->_data['newItems']) as $itemHash) {
if (substr($itemHash, 0, 6) === $feedHash) {
unset($this->_data['newItems'][$itemHash]);
}
}
}
}
public function writeFeed($feedHash, $feed)
{
if ($this->kfc->isLogged() || (isset($_GET['cron']) && $_GET['cron'] === sha1($this->kfc->salt.$this->kfc->hash))) {
if (!is_dir($this->cacheDir)) {
if (!@mkdir($this->cacheDir, 0755)) {
die("Can not create cache dir: ".$this->cacheDir);
}
@chmod($this->cacheDir, 0755);
if (!is_file($this->cacheDir.'/.htaccess')) {
if (!@file_put_contents(
$this->cacheDir.'/.htaccess',
"Allow from none\nDeny from all\n"
)) {
die("Can not protect cache dir: ".$this->cacheDir);
}
}
}
$write = @file_put_contents(
$this->cacheDir.'/'.$feedHash.'.php',
PHPPREFIX
. base64_encode(gzdeflate(serialize($feed)))
. PHPSUFFIX,
LOCK_EX
);
if (!$write) {
die("Can't write to " . $this->cacheDir.'/'.$feedHash.'.php');
}
}
}
public function orderFeedsForUpdate($feedsHash)
{
$newFeedsHash = array();
foreach(array_keys($this->_data['items']) as $itemHash) {
$feedHash = substr($itemHash, 0, 6);
if (in_array($feedHash, $feedsHash) and !in_array($feedHash, $newFeedsHash)) {
$newFeedsHash[] = $feedHash;
}
}
if ($this->kfc->order !== 'newerFirst') {
$newFeedsHash = array_reverse($newFeedsHash);
}
foreach($feedsHash as $feedHash) {
if (!in_array($feedHash, $newFeedsHash)) {
$newFeedsHash[] = $feedHash;
}
}
return $newFeedsHash;
}
public function getFeedsHashFromFolderHash($folderHash)
{
$list = array();
$folders = $this->getFolders();
if (isset($folders[$folderHash])) {
foreach ($this->_data['feeds'] as $feedHash => $feed) {
if (in_array($folderHash, $feed['foldersHash'])) {
$list[] = $feedHash;
}
}
}
return $list;
}
public function getFolders()
{
return $this->_data['folders'];
}
public function getFolder($folderHash)
{
if (isset($this->_data['folders'][$folderHash])) {
return $this->_data['folders'][$folderHash];
}
return false;
}
public function addFolder($folderTitle, $newFolderHash = '')
{
if (empty($newFolderHash)) {
$newFolderHash = MyTool::smallHash($newFolderTitle);
}
$this->_data['folders'][$newFolderHash] = array(
'title' => $folderTitle,
'isOpen' => 1
);
}
public function renameFolder($oldFolderHash, $newFolderTitle)
{
$newFolderHash = '';
if (!empty($newFolderTitle)) {
$newFolderHash = MyTool::smallHash($newFolderTitle);
$this->addFolder($newFolderTitle, $newFolderHash);
$this->_data['folders'][$newFolderHash]['isOpen'] = $this->_data['folders'][$oldFolderHash]['isOpen'];
}
unset($this->_data['folders'][$oldFolderHash]);
foreach ($this->_data['feeds'] as $feedHash => $feed) {
$i = array_search($oldFolderHash, $feed['foldersHash']);
if ($i !== false) {
unset($this->_data['feeds'][$feedHash]['foldersHash'][$i]);
if (!empty($newFolderTitle)) {
$this->_data['feeds'][$feedHash]['foldersHash'][] = $newFolderHash;
}
}
}
}
public function orderFolder(
$folderHash,
$order)
{
if (isset($this->_data['folders'][$folderHash])) {
$this->_data['folders'][$folderHash]['order'] = $order;
}
}
public function toggleFolder($hash)
{
if ($this->_data['folders'][$hash]) {
$isOpen = $this->_data['folders'][$hash]['isOpen'];
if ($isOpen) {
$this->_data['folders'][$hash]['isOpen'] = 0;
} else {
$this->_data['folders'][$hash]['isOpen'] = 1;
}
}
return true;
}
public function getFolderTitle($folderHash)
{
if (isset($this->_data['folders'][$folderHash])) {
return $this->_data['folders'][$folderHash]['title'];
}
return false;
}
public function getItems($hash = 'all', $filter = 'all')
{
if (empty($hash) or $hash == 'all' and $filter == 'all') {
if (isset($this->_data['newItems'])) {
return $this->_data['items']+$this->_data['newItems'];
} else {
return $this->_data['items'];
}
}
if (empty($hash) or $hash == 'all' and $filter == 'old') {
return $this->_data['items'];
}
if (empty($hash) or $hash == 'all' and $filter == 'new') {
if (isset($this->_data['newItems'])) {
return $this->_data['newItems'];
} else {
return array();
}
}
$list = array();
$isRead = 1;
if ($filter === 'unread') {
$isRead = 0;
}
if (empty($hash) || $hash == 'all') {
// all items
foreach ($this->_data['items'] as $itemHash => $item) {
if ($item[1] === $isRead) {
$list[$itemHash] = $item;
}
}
if (isset($this->_data['newItems'])) {
foreach ($this->_data['newItems'] as $itemHash => $item) {
if ($item[1] === $isRead) {
$list[$itemHash] = $item;
}
}
}
} else {
if (strlen($hash) === 12) {
// an item
if (isset($this->_data['items'][$hash])) {
$list[$hash] = $this->_data['items'][$hash];
} else if (isset($this->_data['newItems']) && isset($this->_data['newItems'][$hash])) {
$list[$hash] = $this->_data['newItems'][$hash];
}
} else {
$feedsHash = array();
if (isset($this->_data['feeds'][$hash])) {
// a feed
$feedsHash[] = $hash;
} else if (isset($this->_data['folders'][$hash])) {
// a folder
foreach ($this->_data['feeds'] as $feedHash => $feed) {
if (in_array($hash, $feed['foldersHash'])) {
$feedsHash[] = $feedHash;
}
}
}
// get items from a list of feeds
if (!empty($feedsHash)) {
$flipFeedsHash = array_flip($feedsHash);
foreach ($this->_data['items'] as $itemHash => $item) {
if (isset($flipFeedsHash[substr($itemHash, 0, 6)])) {
if ($filter === 'all' or $item[1] === $isRead) {
$list[$itemHash] = $item;
}
}
}
if (isset($this->_data['newItems'])) {
foreach ($this->_data['newItems'] as $itemHash => $item) {
if (isset($flipFeedsHash[substr($itemHash, 0, 6)])) {
if ($filter === 'all' or $item[1] === $isRead) {
$list[$itemHash] = $item;
}
}
}
}
}
}
}
return $list;
}
public function setItems($items)
{
$this->_data['items'] = $items;
}
public function loadItem($itemHash, $keep)
{
$feedHash = substr($itemHash, 0, 6);
$item = array();
if (isset($this->_data['feeds'][$feedHash]['items'])) {
if (isset($this->_data['feeds'][$feedHash]['items'][$itemHash])) {
$item = $this->_data['feeds'][$feedHash]['items'][$itemHash];
}
} else {
$this->loadFeed($feedHash);
return $this->loadItem($itemHash, $keep);
}
if (!$keep) {
unset($this->_data['feeds'][$feedHash]['items']);
}
return $item;
}
public function getItem($itemHash, $keep = true)
{
$item = $this->loadItem($itemHash, $keep);
if (!empty($item)) {
$item['itemHash'] = $itemHash;
$time = $item['time'];
if (date('Y-m-d', $time) == date('Y-m-d', time())) {
// Today
$item['time'] = array('time' => $time, 'list' => date('H:i', $time), 'expanded' => date('l jS F Y H:i:s', $time));
} else {
if (date('Y', $time) == date('Y', time())) {
$item['time'] = array('time' => $time, 'list' => date('F d', $time), 'expanded' => date('l jS F Y H:i:s', $time));
} else {
$item['time'] = array('time' => $time, 'list' => date('Y-m-d', $time), 'expanded' => date('l jS F Y H:i:s', $time));
}
}
if (isset($this->_data['items'][$itemHash])) {
$item['read'] = is_array($this->_data['items'][$itemHash])?$this->_data['items'][$itemHash][1]:0;
} else if (isset($this->_data['newItems'][$itemHash])) {
$item['read'] = $this->_data['newItems'][$itemHash][1];
$currentNewItemIndex = array_search($itemHash, array_keys($this->_data['newItems']));
if (isset($_SESSION['lastNewItemsHash'])) {
$lastNewItemIndex = array_search($_SESSION['lastNewItemsHash'], array_keys($this->_data['newItems']));
if ($lastNewItemIndex < $currentNewItemIndex) {
$_SESSION['lastNewItemsHash'] = $itemHash;
}
} else {
$_SESSION['lastNewItemsHash'] = $itemHash;
}
} else {
// FIX: data may be corrupted
return false;
}
$item['author'] = htmlspecialchars(html_entity_decode(strip_tags($item['author']), ENT_QUOTES, 'utf-8'), ENT_NOQUOTES);
$item['title'] = htmlspecialchars(html_entity_decode(strip_tags($item['title']), ENT_QUOTES, 'utf-8'), ENT_NOQUOTES);
$item['link'] = htmlspecialchars($item['link']);
if (empty($item['title'])) {
$item['title'] = $item['link'];
}
$item['via'] = htmlspecialchars($item['via']);
$item['favicon'] = $this->getFaviconFeed(substr($itemHash, 0, 6));
$item['xmlUrl'] = htmlspecialchars($item['xmlUrl']);
if (isset($GLOBALS['starredItems'][$itemHash])) {
$item['starred'] = 1 ;
} else {
$item['starred'] = 0 ;
}
return $item;
}
return false;
}
public function updateItems()
{
if (isset($this->_data['needSort']) or (isset($this->_data['order']) and $this->_data['order'] != $this->kfc->order)) {
unset($this->_data['needSort']);
$this->_data['items'] = $this->_data['items']+$this->_data['newItems'];
$this->_data['newItems'] = array();
// sort items
if ($this->kfc->order === 'newerFirst') {
arsort($this->_data['items']);
} else {
asort($this->_data['items']);
}
$this->_data['order'] = $this->kfc->order;
return true;
}
return false;
}
public function initFeedCache($feed, $force)
{
if (!empty($feed)) {
if ($force) {
$feed['etag'] = '';
$feed['lastModified'] = '';
}
$headers = [];
foreach(MyTool::$opts['http']['headers'] as $header) {
if (strpos($header, 'If-Modified-Since:') === false && strpos($header, 'If-None-Match:') === false) {
$headers[] = $header;
}
}
MyTool::$opts['http']['headers'] = $headers;
if (!empty($feed['lastModified'])) {
MyTool::$opts['http']['headers'][] = 'If-Modified-Since: ' . $feed['lastModified'];
}
if (!empty($feed['etag'])) {
MyTool::$opts['http']['headers'][] = 'If-None-Match: ' . $feed['etag'];
}
}
return $feed;
}
public function updateFeedCache($feed, $outputUrl)
{
// really new (2XX) and errors (4XX and 5XX) are considered new
if ($outputUrl['code'] != 304) {
if (preg_match('/^ETag: ([^\r\n]*)[\r\n]*$/im', $outputUrl['header'], $matches)) {
$feed['etag'] = $matches[1];
}
if (preg_match('/^Last-Modified: ([^\r\n]*)[\r\n]*$/im', $outputUrl['header'], $matches)) {
$feed['lastModified'] = $matches[1];
}
}
if (empty($feed['etag'])) {
unset($feed['etag']);
}
if (empty($feed['lastModified'])) {
unset($feed['lastModified']);
}
return $feed;
}
public function updateFeedFromDom($feed, $dom) {
if (empty($feed)) {
// addFeed
$feed = Rss::getFeed($dom);
if (!MyTool::isUrl($feed['htmlUrl'])) {
$feed['htmlUrl'] = ' ';
}
if (empty($feed['description'])) {
$feed['description'] = ' ';
}
$feed['foldersHash'] = array();
$feed['timeUpdate'] = 'auto';
} else if (empty($feed['description']) || empty($feed['htmlUrl'])) {
// if feed description/htmlUrl is empty try to update
// (after opml import, description/htmlUrl are often empty)
$rssFeed = Rss::getFeed($dom);
if (empty($feed['description'])) {
if (empty($rssFeed['description'])) {
$rssFeed['description'] = ' ';
}
$feed['description'] = $rssFeed['description'];
}
if (empty($feed['htmlUrl'])) {
if (empty($rssFeed['htmlUrl'])) {
$rssFeed['htmlUrl'] = ' ';
}
$feed['htmlUrl'] = $rssFeed['htmlUrl'];
}
}
return $feed;
}
private function showEnclosure($enclosure) {
$path = parse_url($enclosure, PHP_URL_PATH);
$ext = pathinfo($path, PATHINFO_EXTENSION);
$link = ''.$enclosure.'';
switch(strtolower($ext)) {
case '':
if (strpos($enclosure, 'https://www.youtube.com') === 0) {
$link = '';
}
break;
case 'jpg':
case 'jpeg':
case 'png':
case 'gif':
$link = '';
break;
case 'mp3':
case 'oga':
case 'wav':
$link = '';
break;
case 'mp4':
case 'ogg':
case 'webm':
$link = '';
break;
}
return $link;
}
public function updateItemsFromDom($dom) {
$items = Rss::getItems($dom);
$newItems = array();
foreach($items as $item) {
if (!empty($item['link'])) {
$hashUrl = MyTool::smallHash($item['link']);
$newItems[$hashUrl] = array();
$newItems[$hashUrl]['title'] = empty($item['title'])?$item['link']:$item['title'];
$newItems[$hashUrl]['time'] = strtotime($item['time'])
? strtotime($item['time'])
: time();
if (MyTool::isUrl($item['via']) &&
parse_url($item['via'], PHP_URL_HOST)
!= parse_url($item['link'], PHP_URL_HOST)) {
$newItems[$hashUrl]['via'] = $item['via'];
} else {
$newItems[$hashUrl]['via'] = '';
}
$newItems[$hashUrl]['link'] = $item['link'];
$newItems[$hashUrl]['author'] = $item['author'];
mb_internal_encoding("UTF-8");
$newItems[$hashUrl]['description'] = mb_substr(
strip_tags($item['description']), 0, 500
);
if(!empty($item['enclosure'])) {
foreach($item['enclosure'] as $enclosure) {
$item['content'] .= '
'.$this->showEnclosure($enclosure);
}
}
if(!empty($item['thumbnail'])) {
$item['content'] .= '
'.$this->showEnclosure($item['thumbnail']);
}
$newItems[$hashUrl]['content'] = $item['content'];
}
}
return $newItems;
}
public function loadRss($xmlUrl, $feed = array(), $force = false)
{
$items = array();
$feed = $this->initFeedCache($feed, $force);
if( !ini_get('safe_mode') && isset(MyTool::$opts['http']['timeout'])){
set_time_limit(MyTool::$opts['http']['timeout']+1);
}
$outputUrl = MyTool::loadUrl($xmlUrl);
if (!empty($outputUrl['error'])) {
$feed['error'] = $outputUrl['error'];
} else if (empty($outputUrl['data'])) {
if ($outputUrl['code'] != 304) { // 304 Not modified
$feed['error'] = Intl::msg('Empty output data');;
}
} else {
$outputDom = Rss::loadDom($outputUrl['data']);
if (!empty($outputDom['error'])) {
$feed['error'] = $outputDom['error'];
} else {
unset($feed['error']);
$feed = $this->updateFeedFromDom($feed, $outputDom['dom']);
$feed = $this->updateFeedCache($feed, $outputUrl);
$items = $this->updateItemsFromDom($outputDom['dom']);
}
}
$feed['lastUpdate'] = time();
return array(
'feed' => $feed,
'items' => $items,
);
}
public function addChannel($xmlUrl)
{
$feedHash = MyTool::smallHash($xmlUrl);
$error = '';
if (!isset($this->_data['feeds'][$feedHash])) {
$output = $this->loadRss($xmlUrl);
if (empty($output['feed']['error'])) {
$output['feed']['xmlUrl'] = $xmlUrl;
$output['feed']['nbUnread'] = count($output['items']);
$output['feed']['nbAll'] = count($output['items']);
$this->_data['feeds'][$feedHash] = $output['feed'];
$this->_data['needSort'] = true;
$items = $output['items'];
foreach (array_keys($items) as $itemHash) {
if (empty($items[$itemHash]['via'])) {
$items[$itemHash]['via'] = $output['feed']['htmlUrl'];
}
if (empty($items[$itemHash]['author'])) {
$items[$itemHash]['author'] = $output['feed']['title'];
} else {
$items[$itemHash]['author']
= $output['feed']['title'] . ' ('
. $items[$itemHash]['author'] . ')';
}
$items[$itemHash]['xmlUrl'] = $xmlUrl;
$this->_data['newItems'][$feedHash . $itemHash] = array(
$items[$itemHash]['time'],
0
);
$items[$feedHash . $itemHash] = $items[$itemHash];
unset($items[$itemHash]);
}
$this->writeFeed($feedHash, $items);
} else {
$error = $output['feed']['error'];
}
} else {
$error = Intl::msg('Duplicated feed');
}
return array('error' => $error);
}
public function getTimeUpdate($feed)
{
$max = $feed['timeUpdate'];
if ($max == 'auto') {
$max = $this->kfc->maxUpdate;
} elseif ($max == 'max') {
$max = $this->kfc->maxUpdate;
} elseif ((int) $max < MIN_TIME_UPDATE
|| (int) $max > $this->kfc->maxUpdate) {
$max = $this->kfc->maxUpdate;
}
return (int) $max;
}
public function needUpdate($feed)
{
$diff = (int) (time()-$feed['lastUpdate']);
if ($diff > $this->getTimeUpdate($feed) * 60) {
return true;
}
return false;
}
public function updateChannel($feedHash, $force = false)
{
$error = '';
$newItems = array();
if (!isset($this->_data['feeds'][$feedHash])) {
return array(
'error' => Intl::msg('Unknown feedhash'),
'newItems' => $newItems
);
}
$xmlUrl = $this->_data['feeds'][$feedHash]['xmlUrl'];
$output = $this->loadRss($xmlUrl, $this->_data['feeds'][$feedHash], $force);
// Update feed information
$this->_data['feeds'][$feedHash] = $output['feed'];
if (empty($output['feed']['error'])) {
$this->loadFeed($feedHash);
$oldItems = array();
if (!empty($this->_data['feeds'][$feedHash]['items']) && is_array($this->_data['feeds'][$feedHash]['items'])) {
$oldItems = $this->_data['feeds'][$feedHash]['items'];
}
$lastTime = 0;
if (isset($this->_data['feeds'][$feedHash]['lastTime'])) {
$lastTime = $this->_data['feeds'][$feedHash]['lastTime'];
}
if (!empty($oldItems)) {
$lastTime = current($oldItems);
$lastTime = $lastTime['time'];
}
$newLastTime = $lastTime;
$rssItems = $output['items'];
$rssItems = array_slice($rssItems, 0, $this->kfc->maxItems, true);
$rssItemsHash = array_keys($rssItems);
if (count($rssItemsHash) !== 0) {
// Look for new items
foreach ($rssItemsHash as $itemHash) {
// itemHash is smallHash of link. To compare to item
// hashes into data, we need to concatenate to feedHash.
if (!isset($oldItems[$feedHash.$itemHash])) {
if (empty($rssItems[$itemHash]['via'])) {
$rssItems[$itemHash]['via']
= $this->_data['feeds'][$feedHash]['htmlUrl'];
}
if (empty($rssItems[$itemHash]['author'])) {
$rssItems[$itemHash]['author']
= $this->_data['feeds'][$feedHash]['title'];
} else {
$rssItems[$itemHash]['author']
= $this->_data['feeds'][$feedHash]['title'] . ' ('
. $rssItems[$itemHash]['author'] . ')';
}
$rssItems[$itemHash]['xmlUrl'] = $xmlUrl;
if ($rssItems[$itemHash]['time'] > $lastTime) {
if ($rssItems[$itemHash]['time'] > $newLastTime) {
$newLastTime = $rssItems[$itemHash]['time'];
}
$newItems[$feedHash . $itemHash] = $rssItems[$itemHash];
}
}
}
$newItemsHash = array_keys($newItems);
$this->_data['feeds'][$feedHash]['items']
= $newItems+$oldItems;
// Check if items may have been missed
if (count($oldItems) !== 0 and count($rssItemsHash) === count($newItemsHash)) {
$error = Intl::msg('Items may have been missed since last update');
}
// Remove from cache already read items not any more in the feed
$listOfOldItems = $this->getItems($feedHash);
foreach ($listOfOldItems as $itemHash => $item) {
$itemRssHash = substr($itemHash, 6, 6);
if (!isset($rssItems[$itemRssHash]) and $item[1] == 1) {
unset($this->_data['feeds'][$feedHash]['items'][$itemHash]);
}
}
// Check if quota exceeded
$nbAll = count($this->_data['feeds'][$feedHash]['items']);
if ($nbAll > $this->kfc->maxItems) {
$this->_data['feeds'][$feedHash]['items']
= array_slice(
$this->_data['feeds'][$feedHash]['items'],
0,
$this->kfc->maxItems, true
);
$nbAll = $this->kfc->maxItems;
}
// Remove items not any more in the cache
foreach (array_keys($listOfOldItems) as $itemHash) {
if (!isset($this->_data['feeds'][$feedHash]['items'][$itemHash])) {
// Remove items not any more in the cache
unset($this->_data['items'][$itemHash]);
unset($this->_data['newItems'][$itemHash]);
}
}
// Update items list and feed information (nbUnread, nbAll)
$this->_data['feeds'][$feedHash]['nbAll'] = $nbAll;
$nbUnread = 0;
foreach ($this->_data['feeds'][$feedHash]['items'] as $itemHash => $item) {
if (isset($this->_data['items'][$itemHash])) {
if ($this->_data['items'][$itemHash][1] === 0) {
$nbUnread++;
}
} else if (isset($this->_data['newItems'][$itemHash])) {
if ($this->_data['newItems'][$itemHash][1] === 0) {
$nbUnread++;
}
} else {
// TODO: Check if itemHash is appended at the end ??
$this->_data['newItems'][$itemHash] = array(
$item['time'],
0
);
$nbUnread++;
}
}
$this->_data['feeds'][$feedHash]['nbUnread'] = $nbUnread;
}
if (empty($this->_data['feeds'][$feedHash]['items'])) {
$this->_data['feeds'][$feedHash]['lastTime'] = $newLastTime;
} else {
unset($this->_data['feeds'][$feedHash]['lastTime']);
}
$this->writeFeed($feedHash, $this->_data['feeds'][$feedHash]['items']);
unset($this->_data['feeds'][$feedHash]['items']);
} else {
$error = $output['feed']['error'];
}
if (!empty($error)) {
$this->_data['feeds'][$feedHash]['error'] = $error;
}
if (empty($newItems)) {
$this->writeData();
} else {
$this->_data['needSort'] = true;
if (isset($_SESSION['lastNewItemsHash'])) {
$lastNewItemIndex = array_search($_SESSION['lastNewItemsHash'], array_keys($this->_data['newItems']));
$this->_data['items'] = $this->_data['items']+array_slice($this->_data['newItems'], 0, $lastNewItemIndex + 1, true);
$this->_data['newItems'] = array_slice($this->_data['newItems'], $lastNewItemIndex + 1, count($this->_data['newItems']) - $lastNewItemIndex, true);
unset($_SESSION['lastNewItemsHash']);
}
if ($this->kfc->order === 'newerFirst') {
arsort($this->_data['newItems']);
} else {
asort($this->_data['newItems']);
}
$this->_data['order'] = $this->kfc->order;
$this->writeData();
}
return array(
'error' => $error,
'newItems' => $newItems
);
}
public function updateFeedsHash($feedsHash, $force, $format = '')
{
$i = 0;
$errorCount = 0;
$noUpdateCount = 0;
$successCount = 0;
$nbItemsAdded = 0;
$feedsHash = $this->orderFeedsForUpdate($feedsHash);
$nbFeeds = count($feedsHash);
ob_end_flush();
if (ob_get_level() == 0) ob_start();
if ($format === 'html') {
echo '
# |
'.Intl::msg('Feed').' |
'.Intl::msg('New items').' |
'.Intl::msg('Time').' |
'.Intl::msg('Status').' |
';
}
$start = microtime(true);
$lastTime = $start;
foreach ($feedsHash as $feedHash) {
$i++;
$feed = $this->getFeed($feedHash);
$strBegin = "\n".''.str_pad($i.'/'.$nbFeeds, 7, ' ', STR_PAD_LEFT).' | '.substr(str_pad($feed['title'], 50), 0, 50).' | ';
if ($format === 'html') {
echo str_pad($strBegin, 4096);
ob_flush();
flush();
}
$strEnd = '';
$time = microtime(true) - $lastTime;
$lastTime += $time;
if ($force or $this->needUpdate($feed)) {
$info = $this->updateChannel($feedHash, $force);
$countItems = count($info['newItems']);
$strEnd .= ''.str_pad($countItems, 3, ' ', STR_PAD_LEFT).' | '.str_pad(number_format($time, 1), 6, ' ', STR_PAD_LEFT).'s | ';
if (empty($info['error'])) {
$strEnd .= Intl::msg('Successfully updated').' |
';
$successCount++;
$nbItemsAdded += $countItems;
} else {
$strEnd .= ''.$info['error'].'';
$errorCount++;
}
} else {
$strEnd .= str_pad('0', 3, ' ', STR_PAD_LEFT).' '.str_pad(number_format($time, 1), 6, ' ', STR_PAD_LEFT).'s | '.Intl::msg('Already up-to-date').' | ';
$noUpdateCount++;
}
if ($format==='html') {
echo str_pad($strEnd, 4096);
ob_flush();
flush();
} else {
echo strip_tags($strBegin.$strEnd);
}
}
// summary
$strBegin = "\n".' | '.Intl::msg('Total:').' '.$nbFeeds.' '.($nbFeeds > 1 ? Intl::msg('items') : Intl::msg('item')).' | ';
if ($format === 'html') {
echo str_pad($strBegin, 4096);
ob_flush();
flush();
}
$strEnd = str_pad($nbItemsAdded, 3, ' ', STR_PAD_LEFT).' | '.str_pad(number_format(microtime(true) - $start, 1), 6, ' ', STR_PAD_LEFT).'s | '
.''.$successCount.' '.($successCount > 1 ? Intl::msg('Successes') : Intl::msg('Success')).' '
.''.$noUpdateCount.' '.Intl::msg('Up-to-date').' '
.''.$errorCount.' '.($errorCount > 1 ? Intl::msg('Errors') : Intl::msg('Error')).' |
';
if ($format === 'html') {
echo str_pad($strEnd, 4096);
ob_flush();
flush();
} else {
echo strip_tags($strBegin.$strEnd);
}
if ($format === 'html') {
echo '
';
}
return '';
}
public function markAll($read) {
$save = false;
foreach (array_keys($this->_data['items']) as $itemHash) {
if (!$save and $this->_data['items'][$itemHash][1] != $read) {
$save = true;
}
$this->_data['items'][$itemHash][1] = $read;
}
foreach (array_keys($this->_data['newItems']) as $itemHash) {
if (!$save and $this->_data['newItems'][$itemHash][1] != $read) {
$save = true;
}
$this->_data['newItems'][$itemHash][1] = $read;
}
if ($save) {
foreach ($this->_data['feeds'] as $feedHash => $feed) {
if ($read == 1) {
$this->_data['feeds'][$feedHash]['nbUnread'] = 0;
} else {
$this->_data['feeds'][$feedHash]['nbUnread'] = $this->_data['feeds'][$feedHash]['nbAll'];
}
}
}
return $save;
}
public function markItem($itemHash, $read) {
$save = false;
if (isset($this->_data['items'][$itemHash])) {
if ($this->_data['items'][$itemHash][1] != $read) {
$save = true;
$this->_data['items'][$itemHash][1] = $read;
}
} else if (isset($this->_data['newItems'][$itemHash])) {
if ($this->_data['newItems'][$itemHash][1] != $read) {
$save = true;
$this->_data['newItems'][$itemHash][1] = $read;
}
}
if ($save) {
$feedHash = substr($itemHash, 0, 6);
if ($read == 1) {
$this->_data['feeds'][$feedHash]['nbUnread']--;
} else {
$this->_data['feeds'][$feedHash]['nbUnread']++;
}
}
return $save;
}
public function markFeeds($feedsHash, $read) {
$save = false;
// get items from a list of feeds
$flipFeedsHash = array_flip($feedsHash);
foreach ($this->_data['items'] as $itemHash => $item) {
if (isset($flipFeedsHash[substr($itemHash, 0, 6)])) {
if ($this->_data['items'][$itemHash][1] != $read) {
$save = true;
$this->_data['items'][$itemHash][1] = $read;
}
}
}
foreach ($this->_data['newItems'] as $itemHash => $item) {
if (isset($flipFeedsHash[substr($itemHash, 0, 6)])) {
if ($this->_data['newItems'][$itemHash][1] != $read) {
$save = true;
$this->_data['newItems'][$itemHash][1] = $read;
}
}
}
if ($save) {
foreach (array_values($feedsHash) as $feedHash) {
if ($read == 1) {
$this->_data['feeds'][$feedHash]['nbUnread'] = 0;
} else {
$this->_data['feeds'][$feedHash]['nbUnread'] = $this->_data['feeds'][$feedHash]['nbAll'];
}
}
}
return $save;
}
public function mark($hash, $read)
{
if (empty($hash) || $hash == 'all') {
// all items
return $this->markAll($read);
} else {
if (strlen($hash) === 12) {
// an item
return $this->markItem($hash, $read);
} else {
$feedsHash = array();
if (isset($this->_data['feeds'][$hash])) {
// a feed
$feedsHash[] = $hash;
} else if (isset($this->_data['folders'][$hash])) {
// a folder
foreach ($this->_data['feeds'] as $feedHash => $feed) {
if (in_array($hash, $feed['foldersHash'])) {
$feedsHash[] = $feedHash;
}
}
}
return $this->markFeeds($feedsHash, $read);
}
}
return false;
}
public function hashType($hash)
{
$type = '';
if (empty($hash) || $hash=='all') {
$type = 'all';
} else {
if (strlen($hash) === 12) {
// should be an item
$type = 'item';
} else {
if (isset($this->_data['folders'][$hash])) {
// a folder
$type = 'folder';
} else {
if (isset($this->_data['feeds'][$hash])) {
// a feed
$type = 'feed';
} else {
$type = 'unknown';
}
}
}
}
return $type;
}
public static function sortByOrder($a, $b) {
return strnatcasecmp($a['order'], $b['order']);
}
public static function sortByTitle($a, $b) {
return strnatcasecmp($a['title'], $b['title']);
}
}
class FeedConf
{
private $_file = '';
public $login = '';
public $hash = '';
public $disableSessionProtection = false;
public $salt = '';
public $title = "Kriss feed";
public $redirector = '';
public $locale = 'en_GB';
public $shaarli = '';
public $maxItems = 100;
public $maxUpdate = 60;
public $order = 'newerFirst';
public $autoreadItem = false;
public $autoreadPage = false;
public $autoUpdate = false;
public $autohide = false;
public $autofocus = true;
public $addFavicon = false;
public $preload = false;
public $blank = false;
public $swipe = true;
public $visibility = 'private';
public $version;
public $view = 'list';
public $filter = 'unread';
public $listFeeds = 'show';
public $byPage = 10;
public $currentHash = 'all';
public $currentPage = 1;
public $lang = '';
public $menuView = 1;
public $menuListFeeds = 2;
public $menuFilter = 3;
public $menuOrder = 4;
public $menuUpdate = 5;
public $menuRead = 6;
public $menuUnread = 7;
public $menuEdit = 8;
public $menuAdd = 9;
public $menuHelp = 10;
public $menuStars = 11;
public $pagingItem = 1;
public $pagingPage = 2;
public $pagingByPage = 3;
public $pagingMarkAs = 4;
public function __construct($configFile, $version)
{
$this->_file = $configFile;
$this->version = $version;
$this->lang = Intl::$lang;
// Loading user config
if (file_exists($this->_file)) {
include_once $this->_file;
} else {
$this->_install();
}
Session::$disableSessionProtection = $this->disableSessionProtection;
if ($this->addFavicon) {
/* favicon dir */
if (!is_dir(INC_DIR)) {
if (!@mkdir(INC_DIR, 0755)) {
FeedPage::$pb->assign('message', sprintf(Intl::msg('Can not create %s directory, check permissions'), INC_DIR));
FeedPage::$pb->renderPage('message');
}
}
if (!is_dir(FAVICON_DIR)) {
if (!@mkdir(FAVICON_DIR, 0755)) {
FeedPage::$pb->assign('message', sprintf(Intl::msg('Can not create %s directory, check permissions'), FAVICON_DIR));
FeedPage::$pb->renderPage('message');
}
}
}
if ($this->isLogged()) {
unset($_SESSION['view']);
unset($_SESSION['listFeeds']);
unset($_SESSION['filter']);
unset($_SESSION['order']);
unset($_SESSION['byPage']);
unset($_SESSION['lang']);
}
$view = $this->getView();
$listFeeds = $this->getListFeeds();
$filter = $this->getFilter();
$order = $this->getOrder();
$byPage = $this->getByPage();
$lang = $this->getLang();
if ($this->view != $view
|| $this->listFeeds != $listFeeds
|| $this->filter != $filter
|| $this->order != $order
|| $this->byPage != $byPage
|| $this->lang != $lang
) {
$this->view = $view;
$this->listFeeds = $listFeeds;
$this->filter = $filter;
$this->order = $order;
$this->byPage = $byPage;
$this->lang = $lang;
$this->write();
}
if (!$this->isLogged()) {
$_SESSION['view'] = $view;
$_SESSION['listFeeds'] = $listFeeds;
$_SESSION['filter'] = $filter;
$_SESSION['order'] = $order;
$_SESSION['byPage'] = $byPage;
$_SESSION['lang'] = $lang;
}
Intl::$lang = $this->lang;
}
private function _install()
{
if (!empty($_POST['setlogin']) && !empty($_POST['setpassword'])) {
$this->setSalt(sha1(uniqid('', true).'_'.mt_rand()));
$this->setLogin($_POST['setlogin']);
$this->setHash($_POST['setpassword']);
$this->write();
FeedPage::$pb->assign('pagetitle', 'KrISS feed installation');
FeedPage::$pb->assign('class', 'text-success');
FeedPage::$pb->assign('message', Intl::msg('Your simple and smart (or stupid) feed reader is now configured.'));
FeedPage::$pb->assign('referer', MyTool::getUrl().'?import');
FeedPage::$pb->assign('button', Intl::msg('Continue'));
FeedPage::$pb->renderPage('message');
} else {
FeedPage::$pb->assign('pagetitle', Intl::msg('KrISS feed installation'));
FeedPage::$pb->assign('token', Session::getToken());
FeedPage::$pb->renderPage('install');
}
exit();
}
public function hydrate(array $data)
{
foreach ($data as $key => $value) {
// get setter
$method = 'set'.ucfirst($key);
// if setter exists just call it
// (php is not case-sensitive with functions)
if (method_exists($this, $method)) {
$this->$method($value);
}
}
$this->write();
}
public function getLang()
{
$lang = $this->lang;
if (isset($_GET['lang'])) {
$lang = $_GET['lang'];
} else if (isset($_SESSION['lang'])) {
$lang = $_SESSION['lang'];
}
if (!in_array($lang, array_keys(Intl::$langList))) {
$lang = $this->lang;
}
return $lang;
}
public function getView()
{
$view = $this->view;
if (isset($_GET['view'])) {
if ($_GET['view'] == 'expanded') {
$view = 'expanded';
}
if ($_GET['view'] == 'list') {
$view = 'list';
}
} else if (isset($_SESSION['view'])) {
$view = $_SESSION['view'];
}
return $view;
}
public function getFilter()
{
$filter = $this->filter;
if (isset($_GET['filter'])) {
if ($_GET['filter'] == 'unread') {
$filter = 'unread';
}
if ($_GET['filter'] == 'all') {
$filter = 'all';
}
} else if (isset($_SESSION['filter'])) {
$filter = $_SESSION['filter'];
}
return $filter;
}
public function getListFeeds()
{
$listFeeds = $this->listFeeds;
if (isset($_GET['listFeeds'])) {
if ($_GET['listFeeds'] == 'show') {
$listFeeds = 'show';
}
if ($_GET['listFeeds'] == 'hide') {
$listFeeds = 'hide';
}
} else if (isset($_SESSION['listFeeds'])) {
$listFeeds = $_SESSION['listFeeds'];
}
return $listFeeds;
}
public function getByPage()
{
$byPage = $this->byPage;
if (isset($_GET['byPage']) && is_numeric($_GET['byPage']) && $_GET['byPage'] > 0) {
$byPage = $_GET['byPage'];
} else if (isset($_SESSION['byPage'])) {
$byPage = $_SESSION['byPage'];
}
return $byPage;
}
public function getOrder()
{
$order = $this->order;
if (isset($_GET['order'])) {
if ($_GET['order'] === 'newerFirst') {
$order = 'newerFirst';
}
if ($_GET['order'] === 'olderFirst') {
$order = 'olderFirst';
}
} else if (isset($_SESSION['order'])) {
$order = $_SESSION['order'];
}
return $order;
}
public function getCurrentHash()
{
$currentHash = $this->currentHash;
if (isset($_GET['currentHash'])) {
$currentHash = preg_replace('/[^a-zA-Z0-9-_@]/', '', substr(trim($_GET['currentHash'], '/'), 0, 6));
}
if (empty($currentHash)) {
$currentHash = 'all';
}
return $currentHash;
}
public function getCurrentPage()
{
$currentPage = $this->currentPage;
if (isset($_GET['page']) && !empty($_GET['page'])) {
$currentPage = (int) $_GET['page'];
} else if (isset($_GET['previousPage']) && !empty($_GET['previousPage'])) {
$currentPage = (int) $_GET['previousPage'] - 1;
if ($currentPage < 1) {
$currentPage = 1;
}
} else if (isset($_GET['nextPage']) && !empty($_GET['nextPage'])) {
$currentPage = (int) $_GET['nextPage'] + 1;
}
return $currentPage;
}
public function setDisableSessionProtection($disableSessionProtection)
{
$this->disableSessionProtection = $disableSessionProtection;
}
public function setLogin($login)
{
$this->login = $login;
}
public function setVisibility($visibility)
{
$this->visibility = $visibility;
}
public function setHash($pass)
{
$this->hash = sha1($pass.$this->login.$this->salt);
}
public function setSalt($salt)
{
$this->salt = $salt;
}
public function setTitle($title)
{
$this->title = $title;
}
public function setLocale($locale)
{
$this->locale = $locale;
}
public function setRedirector($redirector)
{
$this->redirector = $redirector;
}
public function setAutoreadPage($autoreadPage)
{
$this->autoreadPage = $autoreadPage;
}
public function setAutoUpdate($autoUpdate)
{
$this->autoUpdate = $autoUpdate;
}
public function setAutoreadItem($autoreadItem)
{
$this->autoreadItem = $autoreadItem;
}
public function setAutohide($autohide)
{
$this->autohide = $autohide;
}
public function setAutofocus($autofocus)
{
$this->autofocus = $autofocus;
}
public function setAddFavicon($addFavicon)
{
$this->addFavicon = $addFavicon;
}
public function setPreload($preload)
{
$this->preload = $preload;
}
public function setShaarli($url)
{
$this->shaarli = $url;
}
public function setMaxUpdate($max)
{
$this->maxUpdate = $max;
}
public function setMaxItems($max)
{
$this->maxItems = $max;
}
public function setOrder($order)
{
$this->order = $order;
}
public function setBlank($blank)
{
$this->blank = $blank;
}
public function setSwipe($swipe)
{
$this->swipe = $swipe;
}
public function getMenu()
{
$menu = array();
if ($this->menuView != 0) {
$menu['menuView'] = $this->menuView;
}
if ($this->menuListFeeds != 0) {
$menu['menuListFeeds'] = $this->menuListFeeds;
}
if ($this->menuFilter != 0) {
$menu['menuFilter'] = $this->menuFilter;
}
if ($this->menuOrder != 0) {
$menu['menuOrder'] = $this->menuOrder;
}
if ($this->menuUpdate != 0) {
$menu['menuUpdate'] = $this->menuUpdate;
}
if ($this->menuRead != 0) {
$menu['menuRead'] = $this->menuRead;
}
if ($this->menuUnread != 0) {
$menu['menuUnread'] = $this->menuUnread;
}
if ($this->menuEdit != 0) {
$menu['menuEdit'] = $this->menuEdit;
}
if ($this->menuAdd != 0) {
$menu['menuAdd'] = $this->menuAdd;
}
if ($this->menuHelp != 0) {
$menu['menuHelp'] = $this->menuHelp;
}
if ($this->menuStars != 0) {
$menu['menuStars'] = $this->menuStars;
}
asort($menu);
return $menu;
}
public function getPaging()
{
$paging = array();
if ($this->pagingItem != 0) {
$paging['pagingItem'] = $this->pagingItem;
}
if ($this->pagingPage != 0) {
$paging['pagingPage'] = $this->pagingPage;
}
if ($this->pagingByPage != 0) {
$paging['pagingByPage'] = $this->pagingByPage;
}
if ($this->pagingMarkAs != 0) {
$paging['pagingMarkAs'] = $this->pagingMarkAs;
}
asort($paging);
return $paging;
}
public function setMenuView($menuView)
{
$this->menuView = $menuView;
}
public function setMenuListFeeds($menuListFeeds)
{
$this->menuListFeeds = $menuListFeeds;
}
public function setMenuFilter($menuFilter)
{
$this->menuFilter = $menuFilter;
}
public function setMenuOrder($menuOrder)
{
$this->menuOrder = $menuOrder;
}
public function setMenuUpdate($menuUpdate)
{
$this->menuUpdate = $menuUpdate;
}
public function setMenuRead($menuRead)
{
$this->menuRead = $menuRead;
}
public function setMenuUnread($menuUnread)
{
$this->menuUnread = $menuUnread;
}
public function setMenuEdit($menuEdit)
{
$this->menuEdit = $menuEdit;
}
public function setMenuAdd($menuAdd)
{
$this->menuAdd = $menuAdd;
}
public function setMenuHelp($menuHelp)
{
$this->menuHelp = $menuHelp;
}
public function setMenuStars($menuStars)
{
$this->menuStars = $menuStars;
}
public function setPagingItem($pagingItem)
{
$this->pagingItem = $pagingItem;
}
public function setPagingPage($pagingPage)
{
$this->pagingPage = $pagingPage;
}
public function setPagingByPage($pagingByPage)
{
$this->pagingByPage = $pagingByPage;
}
public function setPagingMarkAs($pagingMarkAs)
{
$this->pagingMarkAs = $pagingMarkAs;
}
public function isLogged()
{
global $argv;
if (Session::isLogged()
|| $this->visibility === 'public'
|| (isset($_GET['cron'])
&& $_GET['cron'] === sha1($this->salt.$this->hash))
|| (isset($argv)
&& count($argv) >= 3
&& $argv[1] == 'update'
&& $argv[2] == sha1($this->salt.$this->hash))) {
return true;
}
return false;
}
public function write()
{
if ($this->isLogged() || !is_file($this->_file)) {
$data = array('login', 'hash', 'salt', 'title', 'redirector', 'shaarli',
'byPage', 'order', 'visibility', 'filter', 'view','locale',
'maxItems', 'autoreadItem', 'autoreadPage', 'maxUpdate',
'autohide', 'autofocus', 'listFeeds', 'autoUpdate', 'menuView',
'menuListFeeds', 'menuFilter', 'menuOrder', 'menuUpdate',
'menuRead', 'menuUnread', 'menuEdit', 'menuAdd', 'menuHelp', 'menuStars',
'pagingItem', 'pagingPage', 'pagingByPage', 'addFavicon', 'preload',
'pagingMarkAs', 'disableSessionProtection', 'blank', 'swipe', 'lang');
$out = ''.$key.' = '.var_export($this->$key, true).";\n";
}
$out .= '?>';
if (!@file_put_contents($this->_file, $out)) {
die("Can't write to ".CONFIG_FILE." check permissions");
}
}
}
}
class FeedPage
{
public static $pb; // PageBuilder
public static $var = array();
public static function init($var)
{
FeedPage::$var = $var;
}
public static function add_feedTpl()
{
extract(FeedPage::$var);
?>
kfc->isLogged() ){ ?> data-stars="1">
$value1 ){ $counter1++; ?>
-
-
-
-
updateFeedsHash($feedsHash, $forceUpdate, 'html');?>
$name,
'class' => $class
);
}
public static function load($lang) {
self::$lazy = true;
if (file_exists(self::$dir.'/'.self::$domain.'/'.$lang.'.po')) {
self::$messages[$lang] = self::compress(self::read(self::$dir.'/'.self::$domain.'/'.$lang.'.po'));
} else if (class_exists('Intl_'.$lang)) {
call_user_func_array(
array('Intl_'.$lang, 'init'),
array(&self::$messages)
);
}
return isset(self::$messages[$lang])?self::$messages[$lang]:array();
}
public static function compress($hash) {
foreach ($hash as $hashId => $hashArray) {
$hash[$hashId] = $hashArray['msgstr'];
}
return $hash;
}
public static function msg($string, $context = "")
{
if (!self::$lazy) {
self::load(self::$lang);
}
return self::n_msg($string, '', 0, $context);
}
public static function n_msg($string, $plural, $count, $context = "")
{
if (!self::$lazy) {
self::load(self::$lang);
}
// TODO extract Plural-Forms from po file
// https://www.gnu.org/savannah-checkouts/gnu/gettext/manual/html_node/Plural-forms.html
$count = $count > 1 ? 1 : 0;
if (isset(self::$messages[self::$lang][$string][$count])) {
return self::$messages[self::$lang][$string][$count];
}
if ($count != 0) {
return $plural;
}
return $string;
}
public static function read($pofile)
{
$handle = fopen( $pofile, 'r' );
$hash = array();
$fuzzy = false;
$tcomment = $ccomment = $reference = null;
$entry = $entryTemp = array();
$state = null;
$just_new_entry = false; // A new entry has ben just inserted
while(!feof($handle)) {
$line = trim( fgets($handle) );
if($line==='') {
if($just_new_entry) {
// Two consecutive blank lines
continue;
}
// A new entry is found!
$hash[] = $entry;
$entry = array();
$state= null;
$just_new_entry = true;
continue;
}
$just_new_entry = false;
$split = preg_split('/\s/ ', $line, 2 );
$key = $split[0];
$data = isset($split[1])? $split[1]:null;
switch($key) {
case '#,': //flag
$entry['fuzzy'] = in_array('fuzzy', preg_split('/,\s*/', $data) );
$entry['flags'] = $data;
break;
case '#': //translation-comments
$entryTemp['tcomment'] = $data;
$entry['tcomment'] = $data;
break;
case '#.': //extracted-comments
$entryTemp['ccomment'] = $data;
break;
case '#:': //reference
$entryTemp['reference'][] = addslashes($data);
$entry['reference'][] = addslashes($data);
break;
case '#|': //msgid previous-untranslated-string
// start a new entry
break;
case '#@': // ignore #@ default
$entry['@'] = $data;
break;
// old entry
case '#~':
$key = explode(' ', $data );
$entry['obsolete'] = true;
switch( $key[0] )
{
case 'msgid': $entry['msgid'] = trim($data,'"');
break;
case 'msgstr': $entry['msgstr'][] = trim($data,'"');
break;
default: break;
}
continue 2;
break;
case 'msgctxt' :
// context
case 'msgid' :
// untranslated-string
case 'msgid_plural' :
// untranslated-string-plural
$state = $key;
$entry[$state] = $data;
break;
case 'msgstr' :
// translated-string
$state = 'msgstr';
$entry[$state][] = $data;
break;
default :
if( strpos($key, 'msgstr[') !== FALSE ) {
// translated-string-case-n
$state = 'msgstr';
$entry[$state][] = $data;
} else {
// continued lines
//echo "O NDE ELSE:".$state.':'.$entry['msgid'];
switch($state) {
case 'msgctxt' :
case 'msgid' :
case 'msgid_plural' :
//$entry[$state] .= "\n" . $line;
if(is_string($entry[$state])) {
// Convert it to array
$entry[$state] = array( $entry[$state] );
}
$entry[$state][] = $line;
break;
case 'msgstr' :
//Special fix where msgid is ""
if($entry['msgid']=="\"\"") {
$entry['msgstr'][] = trim($line,'"');
} else {
//$entry['msgstr'][sizeof($entry['msgstr']) - 1] .= "\n" . $line;
$entry['msgstr'][]= trim($line,'"');
}
break;
default :
throw new Exception('Parse ERROR!');
return FALSE;
}
}
break;
}
}
fclose($handle);
// add final entry
if($state == 'msgstr') {
$hash[] = $entry;
}
// Cleanup data, merge multiline entries, reindex hash for ksort
$temp = $hash;
$entries = array ();
foreach($temp as $entry) {
foreach($entry as & $v) {
$v = self::clean($v);
if($v === FALSE) {
// parse error
return FALSE;
}
}
$id = is_array($entry['msgid'])? implode('',$entry['msgid']):$entry['msgid'];
$entries[ $id ] = $entry;
}
return $entries;
}
public static function clean($x)
{
if(is_array($x)) {
foreach($x as $k => $v) {
$x[$k] = self::clean($v);
}
} else {
// Remove " from start and end
if($x == '') {
return '';
}
if($x[0]=='"') {
$x = substr($x, 1, -1);
}
$x = stripcslashes( $x );
}
return $x;
}
}
class MyTool
{
// http://php.net/manual/en/function.libxml-set-streams-context.php
static $opts;
static $redirects = 20;
const ERROR_UNKNOWN_CODE = 1;
const ERROR_LOCATION = 2;
const ERROR_TOO_MANY_REDIRECTS = 3;
const ERROR_NO_CURL = 4;
public static function loadUrl($url)
{
$redirects = self::$redirects;
$opts = self::$opts;
$header = '';
$code = '';
$data = '';
$error = '';
if (in_array('curl', get_loaded_extensions())) {
$ch = curl_init($url);
if (!empty($opts)) {
if (!empty($opts['http']['timeout'])) {
curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, $opts['http']['timeout']);
curl_setopt($ch, CURLOPT_TIMEOUT, $opts['http']['timeout']);
}
if (!empty($opts['http']['user_agent'])) {
curl_setopt($ch, CURLOPT_USERAGENT, $opts['http']['user_agent']);
}
if (!empty($opts['http']['headers'])) {
curl_setopt($ch, CURLOPT_HTTPHEADER, $opts['http']['headers']);
}
}
curl_setopt($ch, CURLOPT_ENCODING, '');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_BINARYTRANSFER, true);
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_HEADER, true);
if ((!ini_get('open_basedir') && !ini_get('safe_mode')) || $redirects < 1) {
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, $redirects > 0);
curl_setopt($ch, CURLOPT_MAXREDIRS, $redirects);
$data = curl_exec($ch);
} else {
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, false);
curl_setopt($ch, CURLOPT_FORBID_REUSE, false);
do {
$data = curl_exec($ch);
if (curl_errno($ch)) {
break;
}
$code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
if ($code != 301 && $code != 302 && $code!=303 && $code!=307) {
break;
}
$headerSize = curl_getinfo($ch, CURLINFO_HEADER_SIZE);
$header = substr($data, 0, $headerSize);
if (!preg_match('/^(?:Location|URI): ([^\r\n]*)[\r\n]*$/im', $header, $matches)) {
$error = self::ERROR_LOCATION;
break;
}
curl_setopt($ch, CURLOPT_URL, $matches[1]);
} while (--$redirects);
if (!$redirects) {
$error = self::ERROR_TOO_MANY_REDIRECTS;
}
}
$headerSize = curl_getinfo($ch, CURLINFO_HEADER_SIZE);
$header = substr($data, 0, $headerSize);
$code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
$data = substr($data, $headerSize);
$error = curl_error($ch);
curl_close($ch);
} else {
$context = stream_context_create($opts);
if ($stream = fopen($url, 'r', false, $context)) {
$data = stream_get_contents($stream);
$status = $http_response_header[0];
$code = explode(' ', $status);
if (count($code)>1) {
$code = $code[1];
} else {
$code = '';
$error = self::ERROR_UNKNOWN_CODE;
}
$header = implode("\r\n", $http_response_header);
fclose($stream);
if (substr($data,0,1) != '<') {
$decoded = gzdecode($data);
if (substr($decoded,0,1) == '<') {
$data = $decoded;
}
}
} else {
$error = self::ERROR_NO_CURL;
}
}
return array(
'header' => $header,
'code' => $code,
'data' => $data,
'error' => self::getError($error),
);
}
public static function getError($error)
{
switch ($error) {
case self::ERROR_UNKNOWN_CODE:
return Intl::msg('Http code not valid');
break;
case self::ERROR_LOCATION:
return Intl::msg('Location not found');
break;
case self::ERROR_TOO_MANY_REDIRECTS:
return Intl::msg('Too many redirects');
break;
case self::ERROR_NO_CURL:
return Intl::msg('Error when loading without curl');
break;
default:
return $error;
break;
}
}
public static function initPhp()
{
define('START_TIME', microtime(true));
if (phpversion() < 5) {
die("Argh you don't have PHP 5 !");
}
error_reporting(E_ALL);
ini_set('display_errors', 1);
function stripslashesDeep($value) {
return is_array($value)
? array_map('stripslashesDeep', $value)
: stripslashes($value);
}
if (version_compare(PHP_VERSION, '7.4', '<')) {
if (get_magic_quotes_gpc()) {
$_POST = array_map('stripslashesDeep', $_POST);
$_GET = array_map('stripslashesDeep', $_GET);
$_COOKIE = array_map('stripslashesDeep', $_COOKIE);
}
}
ob_start();
}
public static function isUrl($url)
{
// http://neo22s.com/check-if-url-exists-and-is-online-php/
$pattern='|^http(s)?://[a-z0-9-]+(.[a-z0-9-]+)*(:[0-9]+)?(/.*)?$|i';
return preg_match($pattern, $url);
}
public static function isEmail($email)
{
$pattern = "/^[A-Z0-9._%-]+@[A-Z0-9.-]+\.[A-Z]{2, 4}$/i";
return (preg_match($pattern, $email));
}
public static function formatBBCode($text)
{
$replace = array(
'/\[m\](.+?)\[\/m\]/is'
=> '/* moderate */',
'/\[b\](.+?)\[\/b\]/is'
=> '
$1',
'/\[i\](.+?)\[\/i\]/is'
=> '
$1',
'/\[s\](.+?)\[\/s\]/is'
=> '
$1',
'/\[u\](.+?)\[\/u\]/is'
=> '
$1',
'/\[url\](.+?)\[\/url]/is'
=> '
$1',
'/\[url=(\w+:\/\/[^\]]+)\](.+?)\[\/url]/is'
=> '
$2',
'/\[quote\](.+?)\[\/quote\]/is'
=> '
$1
',
'/\[code\](.+?)\[\/code\]/is'
=> '
$1
',
'/\[([^[]+)\|([^[]+)\]/is'
=> '
$1'
);
$text = preg_replace(
array_keys($replace),
array_values($replace),
$text
);
return $text;
}
public static function formatText($text)
{
$text = preg_replace_callback(
'/
(.*?)<\/code_html>/is',
create_function(
'$matches',
'return htmlspecialchars($matches[1]);'
),
$text
);
$text = preg_replace_callback(
'/(.*?)<\/code_php>/is',
create_function(
'$matches',
'return highlight_string("", true);'
),
$text
);
$text = preg_replace('/
/is', '', $text);
$text = preg_replace(
'#(^|\s)([a-z]+://([^\s\w/]?[\w/])*)(\s|$)#im',
'\\1\\2\\4',
$text
);
$text = preg_replace(
'#(^|\s)wp:?([a-z]{2}|):([\w]+)#im',
'\\1\\3',
$text
);
$text = str_replace(
'http://.wikipedia.org/wiki/',
'http://www.wikipedia.org/wiki/',
$text
);
$text = str_replace('\wp:', 'wp:', $text);
$text = str_replace('\http:', 'http:', $text);
$text = MyTool::formatBBCode($text);
$text = nl2br($text);
return $text;
}
public static function getUrl()
{
$base = isset($GLOBALS['BASE_URL'])?$GLOBALS['BASE_URL']:'';
if (!empty($base)) {
return $base;
}
$https = (!empty($_SERVER['HTTPS'])
&& (strtolower($_SERVER['HTTPS']) == 'on'))
|| (isset($_SERVER["SERVER_PORT"])
&& $_SERVER["SERVER_PORT"] == '443'); // HTTPS detection.
$serverport = (!isset($_SERVER["SERVER_PORT"])
|| $_SERVER["SERVER_PORT"] == '80'
|| ($https && $_SERVER["SERVER_PORT"] == '443')
? ''
: ':' . $_SERVER["SERVER_PORT"]);
$scriptname = str_replace('/index.php', '/', $_SERVER["SCRIPT_NAME"]);
if (!isset($_SERVER["SERVER_NAME"])) {
return $scriptname;
}
return 'http' . ($https ? 's' : '') . '://'
. $_SERVER["SERVER_NAME"] . $serverport . $scriptname;
}
public static function rrmdir($dir)
{
if (is_dir($dir) && ($d = @opendir($dir))) {
while (($file = @readdir($d)) !== false) {
if ( $file != '.' && $file != '..' ) {
unlink($dir . '/' . $file);
}
}
}
}
public static function humanBytes($bytes)
{
$siPrefix = array( 'bytes', 'KB', 'MB', 'GB', 'TB', 'EB', 'ZB', 'YB' );
$base = 1024;
$class = min((int) log($bytes, $base), count($siPrefix) - 1);
$val = sprintf('%1.2f', $bytes / pow($base, $class));
return $val . ' ' . $siPrefix[$class];
}
public static function returnBytes($val)
{
$val = trim($val);
$last = strtolower($val[strlen($val)-1]);
$value = intval($val);
switch($last)
{
case 'g': $value *= 1024;
case 'm': $value *= 1024;
case 'k': $value *= 1024;
}
return $value;
}
public static function getMaxFileSize()
{
$sizePostMax = MyTool::returnBytes(ini_get('post_max_size'));
$sizeUploadMax = MyTool::returnBytes(ini_get('upload_max_filesize'));
// Return the smaller of two:
return min($sizePostMax, $sizeUploadMax);
}
public static function smallHash($text)
{
$t = rtrim(base64_encode(hash('crc32', $text, true)), '=');
// Get rid of characters which need encoding in URLs.
$t = str_replace('+', '-', $t);
$t = str_replace('/', '_', $t);
$t = str_replace('=', '@', $t);
return $t;
}
public static function renderJson($data)
{
header('Cache-Control: no-cache, must-revalidate');
header('Expires: Sat, 26 Jul 1997 05:00:00 GMT');
header('Content-type: application/json; charset=UTF-8');
echo json_encode($data);
exit();
}
public static function grabToLocal($url, $file, $force = false)
{
if ((!file_exists($file) || $force) && in_array('curl', get_loaded_extensions())){
$ch = curl_init ($url);
curl_setopt($ch, CURLOPT_HEADER, false);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_BINARYTRANSFER, true);
$raw = curl_exec($ch);
if (curl_getinfo($ch, CURLINFO_HTTP_CODE) == 200) {
$fp = fopen($file, 'x');
fwrite($fp, $raw);
fclose($fp);
}
curl_close ($ch);
}
}
public static function redirect($rurl = '')
{
if ($rurl === '') {
// if (!empty($_SERVER['HTTP_REFERER']) && strcmp(parse_url($_SERVER['HTTP_REFERER'],PHP_URL_HOST),$_SERVER['SERVER_NAME'])==0)
$rurl = (empty($_SERVER['HTTP_REFERER'])?'?':$_SERVER['HTTP_REFERER']);
if (isset($_POST['returnurl'])) {
$rurl = $_POST['returnurl'];
}
}
// prevent loop
if (empty($rurl) || parse_url($rurl, PHP_URL_QUERY) === $_SERVER['QUERY_STRING']) {
$rurl = MyTool::getUrl();
}
if (substr($rurl, 0, 1) !== '?') {
$ref = MyTool::getUrl();
if (substr($rurl, 0, strlen($ref)) !== $ref) {
$rurl = $ref;
}
}
header('Location: '.$rurl);
exit();
}
}
class Opml
{
public static function importOpml($kfData)
{
$feeds = $kfData['feeds'];
$folders = $kfData['folders'];
$filename = $_FILES['filetoupload']['name'];
$filesize = $_FILES['filetoupload']['size'];
$data = file_get_contents($_FILES['filetoupload']['tmp_name']);
$overwrite = isset($_POST['overwrite']);
if (\PHP_VERSION_ID < 80000) {
$prev = libxml_disable_entity_loader(true);
}
$opml = new DOMDocument('1.0', 'UTF-8');
$importCount=0;
if ($opml->loadXML($data)) {
$body = $opml->getElementsByTagName('body');
$xmlArray = Opml::getArrayFromXml($body->item(0));
$array = Opml::convertOpmlArray($xmlArray['outline']);
foreach ($array as $hashUrl => $arrayInfo) {
$title = '';
if (isset($arrayInfo['title'])) {
$title = $arrayInfo['title'];
} else if (isset($arrayInfo['text'])) {
$title = $arrayInfo['text'];
}
$foldersHash = array();
if (isset($arrayInfo['folders'])) {
foreach ($arrayInfo['folders'] as $folder) {
$folderTitle = html_entity_decode(
$folder,
ENT_QUOTES,
'UTF-8'
);
$folderHash = MyTool::smallHash($folderTitle);
if (!isset($folders[$folderHash])) {
$folders[$folderHash] = array('title' => $folderTitle, 'isOpen' => true);
}
$foldersHash[] = $folderHash;
}
}
$timeUpdate = 'auto';
$lastUpdate = 0;
$xmlUrl = '';
if (isset($arrayInfo['xmlUrl'])) {
$xmlUrl = $arrayInfo['xmlUrl'];
}
$htmlUrl = '';
if (isset($arrayInfo['htmlUrl'])) {
$htmlUrl = $arrayInfo['htmlUrl'];
}
$description = '';
if (isset($arrayInfo['description'])) {
$description = $arrayInfo['description'];
}
if (empty($title)) {
$host = parse_url($xmlUrl)['host'];
$title = empty($host) ? '-' : $host;
}
// create new feed
if (!empty($xmlUrl)) {
$oldFeed = array('nbUnread' => 0, 'nbAll' => 0);
if (isset($feeds[$hashUrl])) {
$oldFeed['nbUnread'] = $feeds[$hashUrl]['nbUnread'];
$oldFeed['nbAll'] = $feeds[$hashUrl]['nbAll'];
}
$currentFeed = array(
'title'
=>
html_entity_decode($title, ENT_QUOTES, 'UTF-8'),
'description'
=>
html_entity_decode($description, ENT_QUOTES, 'UTF-8'),
'htmlUrl'
=>
html_entity_decode($htmlUrl, ENT_QUOTES, 'UTF-8'),
'xmlUrl'
=>
html_entity_decode($xmlUrl, ENT_QUOTES, 'UTF-8'),
'nbUnread' => $oldFeed['nbUnread'],
'nbAll' => $oldFeed['nbAll'],
'foldersHash' => $foldersHash,
'timeUpdate' => $timeUpdate,
'lastUpdate' => $lastUpdate);
if ($overwrite || !isset($feeds[$hashUrl])) {
$feeds[$hashUrl] = $currentFeed;
$importCount++;
}
}
}
FeedPage::$pb->assign('class', 'text-success');
FeedPage::$pb->assign('message', sprintf(Intl::msg('File %s (%s) was successfully processed: %d links imported'),htmlspecialchars($filename),MyTool::humanBytes($filesize), $importCount));
FeedPage::$pb->assign('button', Intl::msg('Continue'));
FeedPage::$pb->assign('referer', MyTool::getUrl());
FeedPage::$pb->renderPage('message', false);
$kfData['feeds'] = $feeds;
$kfData['folders'] = $folders;
return $kfData;
} else {
FeedPage::$pb->assign('message', sprintf(Intl::msg('File %s (%s) has an unknown file format. Check encoding, try to remove accents and try again. Nothing was imported.'),htmlspecialchars($filename),MyTool::humanBytes($filesize)));
FeedPage::$pb->renderPage('message');
}
if (\PHP_VERSION_ID < 80000) {
libxml_disable_entity_loader($prev);
}
}
public static function generateOpml($feeds, $folders)
{
$withoutFolder = array();
$withFolder = array();
// get a new representation of data using folders as key
foreach ($feeds as $hashUrl => $arrayInfo) {
if (empty($arrayInfo['foldersHash'])) {
$withoutFolder[] = $hashUrl;
} else {
foreach ($arrayInfo['foldersHash'] as $folderHash) {
$withFolder[$folderHash][] = $hashUrl;
}
}
}
$opmlData = new DOMDocument('1.0', 'UTF-8');
// we want a nice output
$opmlData->formatOutput = true;
// opml node creation
$opml = $opmlData->createElement('opml');
$opmlVersion = $opmlData->createAttribute('version');
$opmlVersion->value = '1.0';
$opml->appendChild($opmlVersion);
// head node creation
$head = $opmlData->createElement('head');
$title = $opmlData->createElement('title', 'KrISS Feed');
$head->appendChild($title);
$opml->appendChild($head);
// body node creation
$body = $opmlData->createElement('body');
// without folder outline node
foreach ($withoutFolder as $hashUrl) {
$outline = $opmlData->createElement('outline');
$outlineTitle = $opmlData->createAttribute('title');
$outlineTitle->value = htmlspecialchars(
$feeds[$hashUrl]['title']
);
$outline->appendChild($outlineTitle);
$outlineText = $opmlData->createAttribute('text');
$outlineText->value
= htmlspecialchars($feeds[$hashUrl]['title']);
$outline->appendChild($outlineText);
if (!empty($feeds[$hashUrl]['description'])) {
$outlineDescription
= $opmlData->createAttribute('description');
$outlineDescription->value
= htmlspecialchars($feeds[$hashUrl]['description']);
$outline->appendChild($outlineDescription);
}
$outlineXmlUrl = $opmlData->createAttribute('xmlUrl');
$outlineXmlUrl->value
= htmlspecialchars($feeds[$hashUrl]['xmlUrl']);
$outline->appendChild($outlineXmlUrl);
$outlineHtmlUrl = $opmlData->createAttribute('htmlUrl');
$outlineHtmlUrl->value = htmlspecialchars(
$feeds[$hashUrl]['htmlUrl']
);
$outline->appendChild($outlineHtmlUrl);
$outlineType = $opmlData->createAttribute('type');
$outlineType->value = 'rss';
$outline->appendChild($outlineType);
$body->appendChild($outline);
}
// with folder outline node
foreach ($withFolder as $folderHash => $arrayHashUrl) {
$outline = $opmlData->createElement('outline');
$outlineText = $opmlData->createAttribute('text');
$outlineText->value = htmlspecialchars($folders[$folderHash]['title']);
$outline->appendChild($outlineText);
foreach ($arrayHashUrl as $hashUrl) {
$outlineKF = $opmlData->createElement('outline');
$outlineTitle = $opmlData->createAttribute('title');
$outlineTitle->value
= htmlspecialchars($feeds[$hashUrl]['title']);
$outlineKF->appendChild($outlineTitle);
$outlineText = $opmlData->createAttribute('text');
$outlineText->value
= htmlspecialchars($feeds[$hashUrl]['title']);
$outlineKF->appendChild($outlineText);
if (!empty($feeds[$hashUrl]['description'])) {
$outlineDescription
= $opmlData->createAttribute('description');
$outlineDescription->value = htmlspecialchars(
$feeds[$hashUrl]['description']
);
$outlineKF->appendChild($outlineDescription);
}
$outlineXmlUrl = $opmlData->createAttribute('xmlUrl');
$outlineXmlUrl->value
= htmlspecialchars($feeds[$hashUrl]['xmlUrl']);
$outlineKF->appendChild($outlineXmlUrl);
$outlineHtmlUrl = $opmlData->createAttribute('htmlUrl');
$outlineHtmlUrl->value
= htmlspecialchars($feeds[$hashUrl]['htmlUrl']);
$outlineKF->appendChild($outlineHtmlUrl);
$outlineType = $opmlData->createAttribute('type');
$outlineType->value = 'rss';
$outlineKF->appendChild($outlineType);
$outline->appendChild($outlineKF);
}
$body->appendChild($outline);
}
$opml->appendChild($body);
$opmlData->appendChild($opml);
return $opmlData->saveXML();
}
public static function exportOpml($feeds, $folders)
{
// generate opml file
header('Content-Type: text/xml; charset=utf-8');
header(
'Content-disposition: attachment; filename=kriss_feed_'
. strval(date('Ymd_His')) . '.opml'
);
echo self::generateOpml($feeds, $folders);
exit();
}
public static function getArrayFromXml($node)
{
$array = false;
if ($node->hasAttributes()) {
foreach ($node->attributes as $attr) {
$array[$attr->nodeName] = $attr->nodeValue;
}
}
if ($node->hasChildNodes()) {
if ($node->childNodes->length == 1) {
$array[$node->firstChild->nodeName]
= $node->firstChild->nodeValue;
} else {
foreach ($node->childNodes as $childNode) {
if ($childNode->nodeType != XML_TEXT_NODE) {
$array[$childNode->nodeName][]
= Opml::getArrayFromXml($childNode);
}
}
}
}
return $array;
}
public static function convertOpmlArray($array, $listFolders = array())
{
$newArray = array();
for ($i = 0, $len = count($array); $i < $len; $i++) {
if (isset($array[$i]['outline'])
&& (isset($array[$i]['text'])
|| isset($array[$i]['title']))
) {
// here is a folder
if (isset($array[$i]['text'])) {
$listFolders[] = $array[$i]['text'];
} else {
$listFolders[] = $array[$i]['title'];
}
$newArray = array_merge(
$newArray,
Opml::convertOpmlArray(
$array[$i]['outline'],
$listFolders
)
);
array_pop($listFolders);
} else {
if (isset($array[$i]['xmlUrl'])) {
// here is a feed
$xmlUrl = MyTool::smallHash($array[$i]['xmlUrl']);
if (isset($newArray[$xmlUrl])) {
//feed already exists
foreach ($listFolders as $val) {
// add folder to the feed
if (!in_array(
$val,
$newArray[$xmlUrl]['folders']
)) {
$newArray[$xmlUrl]['folders'][] = $val;
}
}
} else {
// here is a new feed
foreach ($array[$i] as $attr => $val) {
$newArray[$xmlUrl][$attr] = $val;
}
$newArray[$xmlUrl]['folders'] = $listFolders;
}
}
}
}
return $newArray;
}
}
class PageBuilder
{
private $pageClass;
public $tpl;
public $var = array();
public function __construct($pageClass)
{
$this->pageClass = $pageClass;
}
public function assign($variable, $value = null)
{
if (is_array($variable)) {
$this->var += $variable;
} else {
$this->var[$variable] = $value;
}
}
public function renderPage($page, $exit = true)
{
$this->assign('template', $page);
$method = $page.'Tpl';
if (method_exists($this->pageClass, $method)) {
$classPage = new $this->pageClass;
$classPage->init($this->var);
ob_start();
$classPage->$method();
ob_end_flush();
} else {
$this->draw($page);
}
if ($exit) {
exit();
}
return true;
}
}
class Plugin
{
public static $dir = "plugins";
public static $hooks = array();
public static function init() {
$arrayPlugins = glob(self::$dir. '/*.php');
if(is_array($arrayPlugins)) {
foreach($arrayPlugins as $plugin) {
include $plugin;
}
}
}
public static function listAll() {
$list = array();
self::callHook('Plugin_registry', array(&$list));
return $list;
}
public static function addHook($hookName, $functionName, $priority = 10) {
self::$hooks[$hookName][$priority][] = $functionName;
}
public static function callHook($hookName, $hookArguments = array()) {
if(isset(self::$hooks[$hookName])) {
ksort(self::$hooks[$hookName]);
foreach (self::$hooks[$hookName] as $hooks) {
foreach($hooks as $functionName) {
call_user_func_array($functionName, $hookArguments);
}
}
}
}
}
class Rss
{
const UNKNOWN = 0;
const RSS = 1;
const ATOM = 2;
public static $feedFormat = array(
'title' => array('>title'),
'description' => array('>description', '>subtitle'),
'htmlUrl' => array('>link', '>link[rel=self][href]', '>link[href]', '>id')
);
public static $itemFormat = array(
'author' => array('>author>name', '>author', '>dc:creator', 'feed>author>name', '>dc:author', '>creator'),
'content' => array('>content:encoded', '>content', '>description', '>summary', '>subtitle', '>media:group>media:description'),
'description' => array('>description', '>summary', '>subtitle', '>content', '>content:encoded'),
'via' => array('>guid', '>id'),
'link' => array('>feedburner:origLink', '>link[rel=alternate][href]', '>link[href]', '>link', '>guid', '>id'),
'time' => array('>pubDate', '>updated', '>lastBuildDate', '>published', '>dc:date', '>date', '>created', '>modified'),
'title' => array('>title'),
'enclosure' => array('>enclosure*[url]', '>media:group>media:content*[url]'),
'thumbnail' => array('>media:thumbnail[url]'),
);
public static function isValidNodeAttrs($node, $attrs)
{
foreach ($attrs as $attr) {
$val = '';
if (strpos($attr, '=') !== false) {
list($attr, $val) = explode('=', $attr);
}
if (!$node->hasAttribute($attr)
|| (!empty($val) && $node->getAttribute($attr) !== $val)) {
return false;
}
}
return true;
}
public static function filterNodeListByName($nodes, $name)
{
$res = array();
for ($i = 0; $i < $nodes->length; $i++) {
if ($nodes->item($i)->tagName === $name) {
$res[] = $nodes->item($i);
}
}
return $res;
}
public static function getNodesName($node, $name)
{
if (strpos($name, ':') !== false) {
list(, $localname) = explode(':', $name);
$nodes = $node->getElementsByTagNameNS('*', $localname);
} else {
$nodes = $node->getElementsByTagName($name);
}
return self::filterNodeListByName($nodes, $name);
}
public static function getElement($node, $selectors)
{
$res = '';
$selector = array_shift($selectors);
$attributes = explode('[', str_replace(']','',$selector));
$name = array_shift($attributes);
if (substr($name, -1) == "*") {
$name = substr($name, 0, -1);
$res = array();
}
$nodes = self::getNodesName($node, $name);
foreach ($nodes as $currentNode) {
if ($currentNode->parentNode->isSameNode($node)
&& self::isValidNodeAttrs($currentNode, $attributes)) {
if (empty($selectors)) {
$attr = end($attributes);
if (empty($attr) || strpos($attr, '=') !== false) {
if (is_array($res)) {
$res[] = $currentNode->textContent;
} else {
$res = $currentNode->textContent;
}
} else {
if (is_array($res)) {
$res[] = $currentNode->getAttribute($attr);
} else {
$res = $currentNode->getAttribute($attr);
}
}
} else {
return self::getElement($currentNode, $selectors);
}
}
if (!is_array($res) && !empty($res)) {
break;
}
}
if ($name == "media:description") {
$res = nl2br($res);
}
return $res;
}
public static function formatElement($dom, $element, $formats)
{
$newElement = array();
foreach ($formats as $format => $list) {
$newElement[$format] = '';
for ($i = 0, $len = count($list);
$i < $len && empty($newElement[$format]);
$i++) {
$selectors = explode('>', $list[$i]);
$selector = array_shift($selectors);
if (empty($selector)) {
$newElement[$format] = self::getElement($element, $selectors);
} else if (strpos($selector, '[') === 0) {
$attributes = explode('[', str_replace(']','',$selector));
if (self::isValidNodeAttrs($element, $attributes)) {
$newElement[$format] = self::getElement($element, $selectors);
}
} else {
$attributes = explode('[', str_replace(']','',$selector));
$name = array_shift($attributes);
$nodes = self::getNodesName($dom, $name);
foreach ($nodes as $node) {
if (self::isValidNodeAttrs($node, $attributes)) {
$newElement[$format] = self::getElement($node, $selectors);
}
if (!empty($newElement[$format])) {
break;
}
}
}
}
}
return $newElement;
}
public static function getFeed($dom)
{
$feed = new DOMNodelist;
$type = self::getType($dom);
if ($type === self::RSS) {
$feed = $dom->getElementsByTagName('channel')->item(0);
} elseif ($type === self::ATOM) {
$feed = $dom->getElementsByTagName('feed')->item(0);
}
return self::formatElement($dom, $feed, self::$feedFormat);
}
public static function getItems($dom, $nb = -1)
{
$items = new DOMNodelist;
$type = self::getType($dom);
if ($type === self::RSS) {
$items = $dom->getElementsByTagName('item');
} elseif ($type === self::ATOM) {
$items = $dom->getElementsByTagName('entry');
}
$newItems = array();
$max = ($nb === -1 ? $items->length : min($nb, $items->length));
for ($i = 0; $i < $max; $i++) {
$newItems[] = self::formatElement($dom, $items->item($i), self::$itemFormat);
}
return $newItems;
}
public static function getType($dom)
{
$type = self::UNKNOWN;
$feed = $dom->getElementsByTagName('channel');
if ($feed->item(0)) {
$type = self::RSS;
} else {
$feed = $dom->getElementsByTagName('feed');
if ($feed->item(0)) {
$type = self::ATOM;
}
}
return $type;
}
public static function loadDom($data)
{
libxml_clear_errors();
if (\PHP_VERSION_ID < 80000) {
$prev = libxml_disable_entity_loader(true);
}
set_error_handler(array('Rss', 'silenceErrors'));
$dom = new DOMDocument();
$dom->loadXML($data);
restore_error_handler();
if (\PHP_VERSION_ID < 80000) {
libxml_disable_entity_loader($prev);
}
return array(
'dom' => $dom,
'error' => self::getError(libxml_get_last_error())
);
}
public static function getError($error)
{
$return = '';
if ($error !== false) {
switch ($error->level) {
case LIBXML_ERR_WARNING:
$return = "Warning XML $error->code: ";
break;
case LIBXML_ERR_ERROR:
$return = "Error XML $error->code: ";
break;
case LIBXML_ERR_FATAL:
$return = "Fatal Error XML $error->code: ";
break;
}
$return .= $return.trim($error->message);
}
return $return;
}
public static function silenceErrors($num, $str)
{
// No-op
}
}
class Session
{
// Personnalize PHP session name
public static $sessionName = '';
// If the user does not access any page within this time,
// his/her session is considered expired (3600 sec. = 1 hour)
public static $inactivityTimeout = 3600;
// If you get disconnected often or if your IP address changes often.
// Let you disable session cookie hijacking protection
public static $disableSessionProtection = false;
// Ban IP after this many failures.
public static $banAfter = 4;
// Ban duration for IP address after login failures (in seconds).
// (1800 sec. = 30 minutes)
public static $banDuration = 1800;
// File storage for failures and bans. If empty, no ban management.
public static $banFile = '';
public static function init()
{
$lifetime = 0;
self::setCookie($lifetime);
// Use cookies to store session.
ini_set('session.use_cookies', 1);
// Force cookies for session (phpsessionID forbidden in URL)
ini_set('session.use_only_cookies', 1);
if (session_status() !== PHP_SESSION_ACTIVE) {
// Prevent php to use sessionID in URL if cookies are disabled.
ini_set('session.use_trans_sid', false);
if (!empty(self::$sessionName)) {
session_name(self::$sessionName);
}
session_start();
if (!empty($_SESSION['longlastingsession'])) {
$lifetime = time()+$_SESSION['longlastingsession'];
}
self::setCookie($lifetime);
}
}
public static function setCookie($lifetime = null)
{
$cookie = session_get_cookie_params();
// Do not change lifetime
if ($lifetime === null) {
$lifetime = $cookie['lifetime'];
}
// Force cookie path
$path = '';
if (dirname($_SERVER['SCRIPT_NAME']) !== '/') {
$path = dirname($_SERVER["SCRIPT_NAME"]).'/';
}
// Use default domain
$domain = $cookie['domain'];
if (isset($_SERVER['HTTP_HOST'])) {
$domain = $_SERVER['HTTP_HOST'];
}
// remove port from domain : http://php.net/manual/en/function.setcookie.php#36202
$domain = (string)parse_url($domain, PHP_URL_HOST);
// Check if secure
$secure = false;
if (isset($_SERVER["HTTPS"]) && $_SERVER["HTTPS"] == "on") {
$secure = true;
}
if (session_status() !== PHP_SESSION_ACTIVE) {
session_set_cookie_params($lifetime, $path, $domain, $secure);
} else {
setcookie(session_name(), session_id(), $lifetime, $path, $domain, $secure);
}
}
private static function _allIPs()
{
$ip = $_SERVER["REMOTE_ADDR"];
$ip.= isset($_SERVER['HTTP_X_FORWARDED_FOR']) ? '_'.$_SERVER['HTTP_X_FORWARDED_FOR'] : '';
$ip.= isset($_SERVER['HTTP_CLIENT_IP']) ? '_'.$_SERVER['HTTP_CLIENT_IP'] : '';
return $ip;
}
public static function login (
$login,
$password,
$loginTest,
$passwordTest,
$pValues = array())
{
self::banInit();
if (self::banCanLogin()) {
if ($login === $loginTest && $password === $passwordTest) {
self::banLoginOk();
// Generate unique random number to sign forms (HMAC)
$_SESSION['uid'] = sha1(uniqid('', true).'_'.mt_rand());
$_SESSION['ip'] = self::_allIPs();
$_SESSION['username'] = $login;
// Set session expiration.
$_SESSION['expires_on'] = time() + self::$inactivityTimeout;
foreach ($pValues as $key => $value) {
$_SESSION[$key] = $value;
}
return true;
}
self::banLoginFailed();
}
return false;
}
public static function logout()
{
unset($_SESSION['uid'], $_SESSION['ip'], $_SESSION['expires_on'], $_SESSION['longlastingsession']);
}
public static function isLogged()
{
if (!isset ($_SESSION['uid'])
|| (self::$disableSessionProtection === false
&& $_SESSION['ip'] !== self::_allIPs())
|| time() >= $_SESSION['expires_on']) {
self::logout();
return false;
}
// User accessed a page : Update his/her session expiration date.
$_SESSION['expires_on'] = time() + self::$inactivityTimeout;
if (!empty($_SESSION['longlastingsession'])) {
$_SESSION['expires_on'] += $_SESSION['longlastingsession'];
}
return true;
}
public static function getToken($salt = '')
{
if (!isset($_SESSION['tokens'])) {
$_SESSION['tokens']=array();
}
// We generate a random string and store it on the server side.
$rnd = sha1(uniqid('', true).'_'.mt_rand().$salt);
$_SESSION['tokens'][$rnd]=1;
return $rnd;
}
public static function isToken($token)
{
if (isset($_SESSION['tokens'][$token])) {
unset($_SESSION['tokens'][$token]); // Token is used: destroy it.
return true; // Token is ok.
}
return false; // Wrong token, or already used.
}
public static function banLoginFailed()
{
if (self::$banFile !== '') {
$ip = $_SERVER["REMOTE_ADDR"];
$gb = $GLOBALS['IPBANS'];
if (!isset($gb['FAILURES'][$ip])) {
$gb['FAILURES'][$ip] = 0;
}
$gb['FAILURES'][$ip]++;
if ($gb['FAILURES'][$ip] > (self::$banAfter - 1)) {
$gb['BANS'][$ip]= time() + self::$banDuration;
}
$GLOBALS['IPBANS'] = $gb;
file_put_contents(self::$banFile, "");
}
}
public static function banLoginOk()
{
if (self::$banFile !== '') {
$ip = $_SERVER["REMOTE_ADDR"];
$gb = $GLOBALS['IPBANS'];
unset($gb['FAILURES'][$ip]); unset($gb['BANS'][$ip]);
$GLOBALS['IPBANS'] = $gb;
file_put_contents(self::$banFile, "");
}
}
public static function banInit()
{
if (self::$banFile !== '') {
if (!is_file(self::$banFile)) {
file_put_contents(self::$banFile, "array(), 'BANS'=>array()), true).";\n?>");
}
include self::$banFile;
}
}
public static function banCanLogin()
{
if (self::$banFile !== '') {
$ip = $_SERVER["REMOTE_ADDR"];
$gb = $GLOBALS['IPBANS'];
if (isset($gb['BANS'][$ip])) {
// User is banned. Check if the ban has expired:
if ($gb['BANS'][$ip] <= time()) {
// Ban expired, user can try to login again.
unset($gb['FAILURES'][$ip]);
unset($gb['BANS'][$ip]);
file_put_contents(self::$banFile, "");
return true; // Ban has expired, user can login.
}
return false; // User is banned.
}
}
return true; // User is not banned.
}
}
class Star extends Feed
{
public $starItemFile;
public function __construct($starFile, $starItemFile, $kfc)
{
parent::__construct($starFile, '', $kfc);
$this->starItemFile = $starItemFile;
if (is_file($this->starItemFile)) {
include_once $this->starItemFile;
}
}
public function initData()
{
$data = array();
$data['feeds'] = array();
$data['items'] = array();
$GLOBALS['starredItems'] = array();
$this->setData($data);
}
public function markItem($itemHash, $starred, $feed = false, $item = false) {
$save = false;
$feeds = $this->getFeeds();
$feedHash = substr($itemHash, 0, 6);
$items = $this->getItems();
if (isset($items[$itemHash]) && $starred === 0) {
$save = true;
unset($items[$itemHash]);
unset($GLOBALS['starredItems'][$itemHash]);
if (isset($feeds[$feedHash])){
unset($feeds[$feedHash]['items'][$itemHash]);
$feeds[$feedHash]['nbAll']--;
if ($feeds[$feedHash]['nbAll'] <= 0) {
unset($feeds[$feedHash]);
}
}
} else if (!isset($items[$itemHash]) && $starred === 1) {
// didn't exists, want to star it
$save = true;
$items[$itemHash] = [time(), 0];
if (!isset($feeds[$feedHash])){
$feed['nbAll'] = 0;
$feed['items'] = array();
// remove useless feed information
unset($feed['timeUpdate']);
unset($feed['nbUnread']);
unset($feed['lastUpdate']);
unset($feed['foldersHash']);
unset($feed['error']);
$feeds[$feedHash] = $feed;
}
$feeds[$feedHash]['items'][$itemHash] = $item;
$feeds[$feedHash]['nbAll']++;
$GLOBALS['starredItems'][$itemHash] = 1;
}
if ($save) {
arsort($items);
$this->setItems($items);
$this->setFeeds($feeds);
}
return $save;
}
public function writeStarredItems()
{
if ($this->kfc->isLogged()) {
$data = array('starredItems');
$out = 'starItemFile, $out)) {
die("Can't write to ".$this->starItemFile." check permissions");
}
}
}
public function writeData()
{
parent::writeData();
$this->writeStarredItems();
}
}
MyTool::$opts = array(
'http' => array(
'headers' => [],
'timeout' => 4,
'user_agent' => 'KrISS feed agent '.FEED_VERSION.' by Tontof.net http://tontof.net/kriss/feed',
)
);
Plugin::init();
if (!class_exists('Intl_de')) {
class Intl_de {
public static function init(&$messages) {
$messages['de'] = unserialize(gzinflate(base64_decode("
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")));
}
}
Intl::addLang('de', 'Deutsch', 'flag-de');
}
if (!class_exists('Intl_fr')) {
class Intl_fr {
public static function init(&$messages) {
$messages['fr'] = unserialize(gzinflate(base64_decode("
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")));
}
}
Intl::addLang('fr', 'Français', 'flag-fr');
}
// Check if php version is correct
MyTool::initPHP();
// Initialize Session
Session::$sessionName = 'kriss';
Session::$banFile = BAN_FILE;
Session::init();
// Initialize internationalization
Intl::addLang('en_GB', 'English (Great Britain)', 'flag-gb');
Intl::addLang('en_US', 'English (America)', 'flag-us');
Intl::init();
$ref = MyTool::getUrl();
$referer = (empty($_SERVER['HTTP_REFERER'])?'?':$_SERVER['HTTP_REFERER']);
if (substr($referer, 0, strlen($ref)) !== $ref) {
$referer = $ref;
}
if (isset($_GET['file'])) {
$gmtTime = gmdate('D, d M Y H:i:s', filemtime(__FILE__)) . ' GMT';
$etag = '"'.md5($gmtTime).'"';
header("Cache-Control:");
header("Pragma:");
header("Last-Modified: $gmtTime");
header("ETag: $etag");
$if_modified_since = isset($_SERVER['HTTP_IF_MODIFIED_SINCE']) ? $_SERVER['HTTP_IF_MODIFIED_SINCE'] : false;
$if_none_match = isset($_SERVER['HTTP_IF_NONE_MATCH']) ? $_SERVER['HTTP_IF_NONE_MATCH'] : false;
if (($if_none_match && $if_none_match == $etag) ||
($if_modified_since && $if_modified_since == $gmtTime)) {
header('HTTP/1.1 304 Not Modified');
exit();
}
if ($_GET['file'] == 'favicon.ico') {
header('Content-Type: image/vnd.microsoft.icon');
$favicon = '
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
';
echo base64_decode($favicon);
} else if ($_GET['file'] == 'style.css') {
header('Content-type: text/css');
?>
article,
footer,
header,
nav,
section {
display: block;
}
html, button {
font-size: 100%;
}
body {
font-family: Helvetica, Arial, sans-serif;
}
html, body, .full-height {
margin: 0;
padding: 0;
height: 100%;
overflow: auto;
}
img {
max-width: 100%;
height: auto;
vertical-align: middle;
border: 0;
}
a {
color: #666;
text-decoration: none;
}
a:hover {
color: #000;
text-decoration: underline;
}
small {
font-size: 85%;
}
strong {
font-weight: bold;
}
em {
font-style: italic;
}
cite {
font-style: normal;
}
code {
font-family: "Courier New", monospace;
font-size: 12px;
color: #333333;
border-radius: 3px;
padding: 2px 4px;
color: #d14;
background-color: #f7f7f9;
border: 1px solid #e1e1e8;
}
.table {
width: 100%;
}
.table th,
.table td {
padding: 8px;
line-height: 20px;
text-align: left;
vertical-align: top;
border-top: 1px solid #dddddd;
}
.table th {
font-weight: bold;
}
.table-striped tbody > tr:nth-child(odd) > td,
.table-striped tbody > tr:nth-child(odd) > th {
background-color: #f9f9f9;
}
.muted {
color: #999999;
}
.text-warning {
color: #c09853;
}
.text-error {
color: #b94a48;
}
.text-info {
color: #3a87ad;
}
.text-success {
color: #468847;
}
.text-center {
text-align: center;
}
.collapse {
position: relative;
height: 0;
overflow: hidden;
}
.collapse.in {
height: auto;
}
.row-fluid .span12 {
width: 99%;
}
.row-fluid .span9 {
width: 75%;
}
.row-fluid .span6 {
width: 49%;
}
.row-fluid .span4 {
width: 33%;
}
.row-fluid .span3 {
width: 24%;
}
.row-fluid .offset4 {
margin-left: 33%;
}
.row-fluid .offset3 {
margin-left: 24%;
}
.container {
margin: auto;
padding: 10px;
}
.well {
border: 1px solid black;
border-radius: 10px;
padding: 10px;
margin-bottom: 10px;
}
/**
* Form
*/
.form-horizontal .control-label {
display: block;
float: left;
width: 20%;
padding-top: 5px;
text-align: right;
}
.form-horizontal .controls {
margin-left: 22%;
width: 78%;
}
label {
display: block;
}
input[type="text"],
input[type="password"] {
width: 90%;
position: relative;
display: inline-block;
line-height: 20px;
height: 20px;
padding: 5px;
margin-left: -1px;
border: 1px solid #444;
vertical-align: middle;
margin-bottom: 0;
border-radius: 0;
}
input[readonly="readonly"]{
opacity: 0.4;
}
.input-mini {
width: 24px !important;
}
button::-moz-focus-inner,
input::-moz-focus-inner {
padding: 0;
border: 0;
}
.control-group {
clear: both;
margin-bottom: 10px;
}
.help-block {
color: #666;
display: block;
font-size: 90%;
margin-bottom: 10px;
}
/**
* Menu
*/
.navbar {
background-color: #fafafa;
border: 1px solid #444;
border-radius: 4px;
}
.nav-collapse.collapse {
height: auto;
overflow: visible;
float: left;
}
.navbar .brand {
padding: 5px;
font-size: 18px;
display: block;
float: left;
}
.navbar .btn-navbar {
padding: 5px !important;
margin: 5px !important;
font-size: 18px;
display: none;
float: right;
}
.navbar .nav {
display: block;
float: left;
}
ul.nav {
padding: 0;
margin: 0;
list-style: none;
}
.navbar .nav > li {
float: left;
}
.navbar .nav > li > a {
display: block;
padding: 10px 15px;
}
.menu-ico {
display: none;
}
/**
* Paging
*/
#paging-up,
#paging-down {
margin: 10px;
}
#paging-up ul,
#paging-down ul {
padding: 0;
margin: 0;
list-style: none;
}
.input-append,
.input-prepend,
.btn-group {
position: relative;
display: inline-block;
font-size: 0;
white-space: nowrap;
}
.btn, button {
display: inline-block;
line-height: 20px;
font-size: 14px;
text-align: center;
border: 1px solid #444;
background-color: #ddd;
vertical-align: middle;
padding: 5px;
margin: 0;
margin-left: -1px;
min-width: 20px;
border-radius: 0;
}
.btn-break {
display: none;
}
ul.inline > li,
ol.inline > li {
float: left;
padding-right: 5px;
padding-left: 5px;
}
/**
* List of feeds
*/
#list-feeds ul {
padding: 0;
margin: 0;
list-style: none;
}
li.feed {
margin-left: 4px;
padding: 1px 0;
}
li.feed:hover {
background-color: #eee;
}
li.feed.has-unread {
font-weight: bold;
}
.item-favicon {
float: left;
margin-right: 2px;
}
li.folder > h4 {
border: 1px solid #444;
border-radius: 4px;
padding: 2px;
margin: 0;
}
li.folder > h5 {
border: 1px solid #444;
border-radius: 4px;
padding: 2px;
margin: 2px 0;
}
li.folder > h4:hover,
li.folder > h5:hover {
background-color: #eee;
}
.mark-as {
float: right;
}
/**
* List of items
*/
#list-items {
padding: 0;
margin: 0;
list-style: none;
}
li.item-list {
border-bottom: 1px solid #ddd;
}
.current, .current-feed, .current-folder > h5, .current-folder > h4 {
background-color: #eee;
}
.current .item-title {
font-weight: bold;
}
dl {
margin: 0 !important;
}
dt,
dd {
line-height: 20px;
}
.dl-horizontal dt {
float: left;
width: 18%;
overflow: hidden;
clear: left;
text-align: right;
text-overflow: ellipsis;
white-space: nowrap;
}
.dl-horizontal dd {
margin-left: 20%;
}
.item-info {
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.item-link {
color: #000;
}
.read {
opacity: 0.4;
}
.autohide-feed,.autohide-folder {
display: none;
}
#main-container {
float: right;
}
#minor-container {
margin-left: 0;
}
.clear {
clear: both;
}
#menu-toggle {
color: #000 !important;
}
#status {
font-size: 85%;
}
.item-toggle-plus {
float: right;
}
.item-content {
margin: 10px 0;
}
.item-info-end {
float: right;
}
.folder-toggle:focus, .folder-toggle:hover, .folder-toggle:active, .item-toggle:focus, .item-toggle:hover, .item-toggle:active, .mark-as:hover, .mark-as:active {
text-decoration: none;
}
.folder-toggle-open, .item-toggle-open, .item-close {
display: none;
}
.folder-toggle-close, .item-toggle-close, .item-open {
display: block;
}
.label,
.badge {
padding: 2px 4px;
font-size: 11px;
line-height: 14px;
font-weight: bold;
color: #ffffff;
background-color: #999999;
border-radius: 3px;
}
.label-expanded {
padding: 6px;
}
/* Large desktop */
@media (min-width: 1200px) {
}
/* Portrait tablet to landscape and desktop */
@media (min-width: 768px) and (max-width: 979px) {
.hidden-desktop {
display: inherit !important;
}
.visible-desktop {
display: none !important ;
}
.visible-tablet {
display: inherit !important;
}
.hidden-tablet {
display: none !important;
}
}
@media (min-width: 768px) {
.nav-collapse.collapse {
height: auto !important;
overflow: visible !important;
}
}
/* Landscape phone to portrait tablet */
@media (max-width: 767px) {
input[type="text"],
input[type="password"] {
padding: 10px 0 !important;
}
.btn {
padding: 10px !important;
}
.label {
border-radius: 10px !important;
padding: 5px;
}
.item-top > .label,
.item-shaarli > .label,
.item-starred > .label,
.item-mark-as > .label {
display: block;
float: left;
margin: 0 5px;
padding: 0px 24px;
line-height: 44px;
}
.item-title {
line-height: 44px;
}
.item-link {
clear: both;
}
li.feed, li.folder > h4, li.folder > h5{
padding: 10px 0;
}
.row-fluid .span12,
.row-fluid .span9,
.row-fluid .span6,
.row-fluid .span4,
.row-fluid .span3 {
width: 99%;
}
.row-fluid .offset4,
.row-fluid .offset3 {
margin-left: 0;
}
#main-container {
float: none;
margin: auto;
}
#minor-container {
margin: auto;
}
.hidden-desktop {
display: inherit !important;
}
.visible-desktop {
display: none !important;
}
.visible-phone {
display: inherit !important;
}
.hidden-phone {
display: none !important;
}
html, body, .full-height {
height: auto;
}
.navbar .container {
width: auto;
}
.nav-collapse.collapse {
float: none;
clear: both;
}
.nav-collapse .nav,
.nav-collapse .nav > li {
float: none;
}
.nav-collapse .nav > li > a {
display: block;
padding: 10px;
}
.nav-collapse .btn {
font-weight: normal;
}
.nav-collapse .nav > li > a:hover,
.nav-collapse .nav > li > a:focus {
background-color: #f2f2f2;
}
.nav-collapse.collapse {
height: 0;
overflow: hidden;
}
.navbar .btn-navbar {
display: block;
}
.dl-horizontal dt {
float: none;
width: auto;
clear: none;
text-align: left;
padding: 10px;
}
.dl-horizontal dd {
clear: both;
padding: 10px;
margin-left: 0;
}
}
/* Landscape phones and down */
@media (max-width: 480px) {
ul.inline {
width: 100%;
}
ul.inline > li {
width: 100%;
padding: 0;
margin: 0;
}
.btn-group {
width: 100%;
margin: 5px auto;
}
.btn-group .btn {
width: 100%;
padding: 10px 0 !important;
margin: auto;
}
.btn-break {
display: block;
}
.btn2 {
width: 50% !important;
}
.btn3 {
width: 33.333% !important;
}
.paging-by-page {
width: 100%;
}
.paging-by-page > input {
padding-left: 0;
padding-right: 0;
}
.item-toggle-plus {
padding: 10px;
}
.item-info {
white-space: normal;
}
.item-description {
display: none;
}
.form-horizontal .control-label {
float: none;
width: auto;
text-align: center;
}
.form-horizontal .controls {
margin: auto;
}
.form-horizontal {
text-align: center;
}
.item-top > .label,
.item-shaarli > .label,
.item-starred > .label,
.item-mark-as > .label {
display: inline-block;
float: none;
margin: 2px 5px;
padding: 0px 24px;
line-height: 44px;
text-align: center;
}
.item-info-end {
float: none;
height: 60px;
overflow-x: auto;
overflow-y: hidden;
white-space: nowrap;
}
}
/* feed icon inspired from peculiar by Lucian Marin - lucianmarin.com */
/* https://github.com/lucianmarin/peculiar */
.ico {
position: relative;
width: 16px;
height: 16px;
display: inline-block;
margin-right: 4px;
margin-left: 4px;
}
.ico-feed-dot {
background-color: #000;
width: 4px;
height: 4px;
border-radius: 3px;
position: absolute;
bottom: 2px;
left: 2px;
}
.ico-feed-circle-1 {
border: #000 2px solid;
border-bottom-color: transparent;
border-left-color: transparent;
width: 6px;
height: 6px;
border-radius: 6px;
position: absolute;
bottom: 0;
left: 0;
}
.ico-feed-circle-2 {
border: #000 2px solid;
border-bottom-color: transparent;
border-left-color: transparent;
width: 9px;
height: 9px;
border-radius: 4px 7px;
position: absolute;
bottom: 0;
left: 0;
}
.ico-b-disc {
background-color: #000;
border-radius:8px;
width: 16px;
height: 16px;
position: absolute;
top:0;
left:0;
}
.ico-w-line-h {
background-color: #fff;
width: 8px;
height: 2px;
border-radius: 1px;
position: absolute;
top:7px;
left: 4px;
}
.ico-w-line-v {
background-color: #fff;
width: 2px;
height: 8px;
border-radius: 1px;
position: absolute;
top: 4px;
left: 7px;
}
.ico-w-circle {
border: #fff 2px solid;
width: 8px;
height: 8px;
border-radius: 8px;
position: absolute;
bottom: 2px;
left: 2px;
}
.ico-toggle-item {
float: right;
}
.menu-ico {
text-decoration: none !important;
}
.menu-ico:before {
content: "";
speak: none;
display: none;
text-decoration: none !important;
}
.ico-star:before {
content: "\2605";
}
.ico-unstar:before {
content: "\2606";
}
.ico-update:before {
content: "\21BA";
}
.ico-add-feed:before {
content: "\271A";
}
.ico-home:before {
content: "\23CF";
}
.ico-help:before {
content: "\2048";
}
.ico-edit:before {
content: "\2318";
}
.ico-config:before {
content: "\273F";
}
.ico-order-older:before {
content: "\25BC";
}
.ico-order-newer:before {
content: "\25B2";
}
.ico-login:before {
content: "\2611";
}
.ico-logout:before {
content: "\2612";
}
.ico-list-feeds-hide:before {
content: "\25FB";
}
.ico-list-feeds-show:before {
content: "\25E7";
}
.ico-list:before {
content: "\2630 ";
}
.ico-expanded:before {
content: "\2B12";
}
.ico-mark-as-read:before {
content: "\25C9";
}
.ico-mark-as-unread:before {
content: "\25CE";
}
.ico-filter-all:before {
content: "\26C3";
}
.ico-filter-unread:before {
content: "\26C0";
}
/**
* flags
* http://flag-sprites.com/en_US/
* http://famfamfam.com/lab/icons/flags/
*/
#flags-sel + #flags {
display: none;
}
#flags-sel:target + #flags {
display: block;
}
#flags-sel:target #hide-flags, #flags-sel #show-flags {
display: inline-block;
}
#flags-sel:target #show-flags, #flags-sel #hide-flags {
display: none;
}
.flag {
display: inline-block;
width: 16px;
height: 11px;
background:url('data:image/png;base64,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') no-repeat
}
.flag.flag-fr {background-position: -16px 0}
.flag.flag-gb {background-position: 0 -11px}
.flag.flag-de {background-position: 0 0}
.flag.flag-us {background-position: -16px -11px}
/*jshint sub:true, evil:true */
(function () {
"use strict";
var view = '', // data-view
listFeeds = '', // data-list-feeds
filter = '', // data-filter
order = '', // data-order
autoreadItem = '', // data-autoread-item
autoreadPage = '', // data-autoread-page
autohide = '', // data-autohide
byPage = -1, // data-by-page
shaarli = '', // data-shaarli
redirector = '', // data-redirector
currentHash = '', // data-current-hash
currentPage = 1, // data-current-page
currentNbItems = 0, // data-nb-items
autoupdate = false, // data-autoupdate
autofocus = false, // data-autofocus
addFavicon = false, // data-add-favicon
preload = false, // data-preload
stars = false, // data-stars
isLogged = false, // data-is-logged
blank = false, // data-blank
swipe = '', // data-swipe
status = '',
listUpdateFeeds = [],
listItemsHash = [],
currentItemHash = '',
currentUnread = 0,
title = '',
cache = {},
intlTop = 'top',
intlShare = 'share',
intlRead = 'read',
intlUnread = 'unread',
intlStar = 'star',
intlUnstar = 'unstar',
intlFrom = 'from';
/**
* trim function
* https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/String/Trim
*/
if(!String.prototype.trim) {
String.prototype.trim = function () {
return this.replace(/^\s+|\s+$/g,'');
};
}
/**
* http://javascript.info/tutorial/bubbling-and-capturing
*/
function stopBubbling(event) {
if(event.stopPropagation) {
event.stopPropagation();
}
else {
event.cancelBubble = true;
}
}
/**
* JSON Object
* https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/JSON
*/
if (!window.JSON) {
window.JSON = {
parse: function (sJSON) { return eval("(" + sJSON + ")"); },
stringify: function (vContent) {
if (vContent instanceof Object) {
var sOutput = "";
if (vContent.constructor === Array) {
for (var nId = 0; nId < vContent.length; sOutput += this.stringify(vContent[nId]) + ",", nId++);
return "[" + sOutput.substr(0, sOutput.length - 1) + "]";
}
if (vContent.toString !== Object.prototype.toString) { return "\"" + vContent.toString().replace(/"/g, "\\$&") + "\""; }
for (var sProp in vContent) { sOutput += "\"" + sProp.replace(/"/g, "\\$&") + "\":" + this.stringify(vContent[sProp]) + ","; }
return "{" + sOutput.substr(0, sOutput.length - 1) + "}";
}
return typeof vContent === "string" ? "\"" + vContent.replace(/"/g, "\\$&") + "\"" : String(vContent);
}
};
}
/**
* https://developer.mozilla.org/en-US/docs/AJAX/Getting_Started
*/
function getXHR() {
var httpRequest = false;
if (window.XMLHttpRequest) { // Mozilla, Safari, ...
httpRequest = new XMLHttpRequest();
} else if (window.ActiveXObject) { // IE
try {
httpRequest = new ActiveXObject("Msxml2.XMLHTTP");
}
catch (e) {
try {
httpRequest = new ActiveXObject("Microsoft.XMLHTTP");
}
catch (e2) {}
}
}
return httpRequest;
}
/**
* http://www.sitepoint.com/xhrrequest-and-javascript-closures/
*/
// Constructor for generic HTTP client
function HTTPClient() {}
HTTPClient.prototype = {
url: null,
xhr: null,
callinprogress: false,
userhandler: null,
init: function(url, obj) {
this.url = url;
this.obj = obj;
this.xhr = new getXHR();
},
asyncGET: function (handler) {
// Prevent multiple calls
if (this.callinprogress) {
throw "Call in progress";
}
this.callinprogress = true;
this.userhandler = handler;
// Open an async request - third argument makes it async
this.xhr.open('GET', this.url, true);
var self = this;
// Assign a closure to the onreadystatechange callback
this.xhr.onreadystatechange = function() {
self.stateChangeCallback(self);
};
this.xhr.send(null);
},
stateChangeCallback: function(client) {
switch (client.xhr.readyState) {
// Request not yet made
case 1:
try { client.userhandler.onInit(); }
catch (e) { /* Handler method not defined */ }
break;
// Contact established with server but nothing downloaded yet
case 2:
try {
// Check for HTTP status 200
if ( client.xhr.status != 200 ) {
client.userhandler.onError(
client.xhr.status,
client.xhr.statusText
);
// Abort the request
client.xhr.abort();
// Call no longer in progress
client.callinprogress = false;
}
}
catch (e) { /* Handler method not defined */ }
break;
// Called multiple while downloading in progress
case 3:
// Notify user handler of download progress
try {
// Get the total content length
// -useful to work out how much has been downloaded
var contentLength;
try {
contentLength = client.xhr.getResponseHeader("Content-Length");
}
catch (e) { contentLength = NaN; }
// Call the progress handler with what we've got
client.userhandler.onProgress(
client.xhr.responseText,
contentLength
);
}
catch (e) { /* Handler method not defined */ }
break;
// Download complete
case 4:
try {
client.userhandler.onSuccess(client.xhr.responseText, client.obj);
}
catch (e) { /* Handler method not defined */ }
finally { client.callinprogress = false; }
break;
}
}
};
/**
* Handler
*/
var ajaxHandler = {
onInit: function() {},
onError: function(status, statusText) {},
onProgress: function(responseText, length) {},
onSuccess: function(responseText, noFocus) {
var result = JSON.parse(responseText);
if (result['logout'] && isLogged) {
alert('You have been disconnected');
}
if (result['item']) {
cache['item-' + result['item']['itemHash']] = result['item'];
loadDivItem(result['item']['itemHash'], noFocus);
}
if (result['page']) {
updateListItems(result['page']);
setCurrentItem();
if (preload) {
preloadItems();
}
}
if (result['read']) {
markAsRead(result['read']);
}
if (result['unread']) {
markAsUnread(result['unread']);
}
if (result['update']) {
updateNewItems(result['update']);
}
}
};
/**
* http://stackoverflow.com/questions/4652734/return-html-from-a-user-selection/4652824#4652824
*/
function getSelectionHtml() {
var html = '';
if (typeof window.getSelection != 'undefined') {
var sel = window.getSelection();
if (sel.rangeCount) {
var container = document.createElement('div');
for (var i = 0, len = sel.rangeCount; i < len; ++i) {
container.appendChild(sel.getRangeAt(i).cloneContents());
}
html = container.innerHTML;
}
} else if (typeof document.selection != 'undefined') {
if (document.selection.type == 'Text') {
html = document.selection.createRange().htmlText;
}
}
return html;
}
/**
* Some javascript snippets
*/
function removeChildren(elt) {
while (elt.hasChildNodes()) {
elt.removeChild(elt.firstChild);
}
}
function removeElement(elt) {
if (elt && elt.parentNode) {
elt.parentNode.removeChild(elt);
}
}
function addClass(elt, cls) {
if (elt) {
elt.className = (elt.className + ' ' + cls).trim();
}
}
function removeClass(elt, cls) {
if (elt) {
elt.className = (' ' + elt.className + ' ').replace(cls, ' ').trim();
}
}
function hasClass(elt, cls) {
if (elt && (' ' + elt.className + ' ').indexOf(' ' + cls + ' ') > -1) {
return true;
}
return false;
}
/**
* Add redirector to link
*/
function anonymize(elt) {
if (redirector !== '') {
var domain, a_to_anon = elt.getElementsByTagName("a");
for (var i = 0; i < a_to_anon.length; i++) {
domain = a_to_anon[i].href.replace('http://','').replace('https://','').split(/[/?#]/)[0];
if (domain !== window.location.host) {
if (redirector !== 'noreferrer') {
a_to_anon[i].href = redirector+a_to_anon[i].href;
} else {
a_to_anon[i].setAttribute('rel', 'noreferrer');
}
}
}
}
}
function initAnonyme() {
if (redirector !== '') {
var i = 0, elements = document.getElementById('list-items');
elements = elements.getElementsByTagName('div');
for (i = 0; i < elements.length; i += 1) {
if (hasClass(elements[i], 'item-content')) {
anonymize(elements[i]);
}
}
}
}
/**
* Replace collapse bootstrap function
*/
function collapseElement(element) {
if (element !== null) {
var targetElement = document.getElementById(
element.getAttribute('data-target').substring(1)
);
if (hasClass(targetElement, 'in')) {
removeClass(targetElement, 'in');
targetElement.style.height = 0;
} else {
addClass(targetElement, 'in');
targetElement.style.height = 'auto';
}
}
}
function collapseClick(event) {
event = event || window.event;
stopBubbling(event);
collapseElement(this);
}
function initCollapse(list) {
var i = 0;
for (i = 0; i < list.length; i += 1) {
if (list[i].hasAttribute('data-toggle') && list[i].hasAttribute('data-target')) {
addEvent(list[i], 'click', collapseClick);
}
}
}
/**
* Shaarli functions
*/
function htmlspecialchars_decode(string) {
return string
.replace(/</g, '<')
.replace(/>/g, '>')
.replace(/"/g, '"')
.replace(/&/g, '&')
.replace(/*39;/g, "'")
.replace(/ /g, " ");
}
function shaarliItem(itemHash) {
var domainUrl, url, domainVia, via, title, sel, element;
element = document.getElementById('item-div-'+itemHash);
if (element.childNodes.length > 1) {
title = getTitleItem(itemHash);
url = getUrlItem(itemHash);
via = getViaItem(itemHash);
if (redirector != 'noreferrer') {
url = url.replace(redirector,'');
via = via.replace(redirector,'');
}
domainUrl = url.replace('http://','').replace('https://','').split(/[/?#]/)[0];
domainVia = via.replace('http://','').replace('https://','').split(/[/?#]/)[0];
if (domainUrl !== domainVia) {
via = 'via ' + via;
} else {
via = '';
}
sel = getSelectionHtml();
if (sel !== '') {
sel = '«' + sel + '»';
}
if (shaarli !== '') {
window.open(
shaarli
.replace('${url}', encodeURIComponent(htmlspecialchars_decode(url)))
.replace('${title}', encodeURIComponent(htmlspecialchars_decode(title)))
.replace('${via}', encodeURIComponent(htmlspecialchars_decode(via)))
.replace('${sel}', encodeURIComponent(htmlspecialchars_decode(sel))),
'_blank',
'height=390, width=600, menubar=no, toolbar=no, scrollbars=yes, status=no, dialog=1'
);
} else {
alert('Please configure your share link first');
}
} else {
loadDivItem(itemHash);
alert('Sorry ! This item is not loaded, try again !');
}
}
function shaarliCurrentItem() {
shaarliItem(currentItemHash);
}
function shaarliClickItem(event) {
event = event || window.event;
stopBubbling(event);
shaarliItem(getItemHash(this));
return false;
}
/**
* Folder functions
*/
function getFolder(element) {
var folder = null;
while (folder === null && element !== null) {
if (element.tagName === 'LI' && element.id.indexOf('folder-') === 0) {
folder = element;
}
element = element.parentNode;
}
return folder;
}
function getLiParentByClassName(element, classname) {
var li = null;
while (li === null && element !== null) {
if (element.tagName === 'LI' && hasClass(element, classname)) {
li = element;
}
element = element.parentNode;
}
if (classname === 'folder' && li.id === 'all-subscriptions') {
li = null;
}
return li;
}
function getFolderHash(element) {
var folder = getFolder(element);
if (folder !== null) {
return folder.id.replace('folder-','');
}
return null;
}
function toggleFolder(folderHash) {
var i, listElements, url, client;
listElements = document.getElementById('folder-' + folderHash);
listElements = listElements.getElementsByTagName('h5');
if (listElements.length > 0) {
listElements = listElements[0].getElementsByTagName('span');
for (i = 0; i < listElements.length; i += 1) {
if (hasClass(listElements[i], 'folder-toggle-open')) {
removeClass(listElements[i], 'folder-toggle-open');
addClass(listElements[i], 'folder-toggle-close');
} else if (hasClass(listElements[i], 'folder-toggle-close')) {
removeClass(listElements[i], 'folder-toggle-close');
addClass(listElements[i], 'folder-toggle-open');
}
}
}
url = '?toggleFolder=' + folderHash + '&ajax';
client = new HTTPClient();
client.init(url);
try {
client.asyncGET(ajaxHandler);
} catch (e) {
alert(e);
}
}
function toggleClickFolder(event) {
event = event || window.event;
stopBubbling(event);
toggleFolder(getFolderHash(this));
return false;
}
function initLinkFolders(listFolders) {
var i = 0;
for (i = 0; i < listFolders.length; i += 1) {
if (listFolders[i].hasAttribute('data-toggle') && listFolders[i].hasAttribute('data-target')) {
listFolders[i].onclick = toggleClickFolder;
}
}
}
function getListLinkFolders() {
var i = 0,
listFolders = [],
listElements = document.getElementById('list-feeds');
if (listElements) {
listElements = listElements.getElementsByTagName('a');
for (i = 0; i < listElements.length; i += 1) {
listFolders.push(listElements[i]);
}
}
return listFolders;
}
/**
* MarkAs functions
*/
function toggleMarkAsLinkItem(itemHash) {
var i, item = getItem(itemHash), listLinks;
if (item !== null) {
listLinks = item.getElementsByTagName('a');
for (i = 0; i < listLinks.length; i += 1) {
if (hasClass(listLinks[i], 'item-mark-as')) {
if (listLinks[i].href.indexOf('unread=') > -1) {
listLinks[i].href = listLinks[i].href.replace('unread=','read=');
listLinks[i].firstChild.innerHTML = intlRead;
} else {
listLinks[i].href = listLinks[i].href.replace('read=','unread=');
listLinks[i].firstChild.innerHTML = intlUnread;
}
}
}
}
}
function getUnreadLabelItems(itemHash) {
var i, listLinks, regex = new RegExp('read=' + itemHash.substr(0,6)), items = [];
listLinks = getListLinkFolders();
for (i = 0; i < listLinks.length; i += 1) {
if (regex.test(listLinks[i].href)) {
items.push(listLinks[i].children[0]);
}
}
return items;
}
function addToUnreadLabel(unreadLabelItem, value) {
var unreadLabel = -1;
if (unreadLabelItem !== null) {
unreadLabel = parseInt(unreadLabelItem.innerHTML, 10) + value;
unreadLabelItem.innerHTML = unreadLabel;
}
return unreadLabel;
}
function getUnreadLabel(folder) {
var element = null;
if (folder !== null) {
element = folder.getElementsByClassName('label')[0];
}
return element;
}
function markAsItem(itemHash) {
var item, url, client, indexItem, i, unreadLabelItems, nb, feed, folder, addRead = 1;
item = getItem(itemHash);
if (item !== null) {
unreadLabelItems = getUnreadLabelItems(itemHash);
if (!hasClass(item, 'read')) {
addRead = -1;
}
for (i = 0; i < unreadLabelItems.length; i += 1) {
nb = addToUnreadLabel(unreadLabelItems[i], addRead);
if (nb === 0) {
feed = getLiParentByClassName(unreadLabelItems[i], 'feed');
removeClass(feed, 'has-unread');
if (autohide) {
addClass(feed, 'autohide-feed');
}
}
folder = getLiParentByClassName(unreadLabelItems[i], 'folder');
nb = addToUnreadLabel(getUnreadLabel(folder), addRead);
if (nb === 0 && autohide) {
addClass(folder, 'autohide-folder');
}
}
addToUnreadLabel(getUnreadLabel(document.getElementById('all-subscriptions')), addRead);
if (hasClass(item, 'read')) {
url = '?unread=' + itemHash;
removeClass(item, 'read');
toggleMarkAsLinkItem(itemHash);
} else {
url = '?read=' + itemHash;
addClass(item, 'read');
toggleMarkAsLinkItem(itemHash);
if (filter === 'unread') {
url += '¤tHash=' + currentHash +
'&page=' + currentPage +
'&last=' + listItemsHash[listItemsHash.length - 1];
removeElement(item);
indexItem = listItemsHash.indexOf(itemHash);
listItemsHash.splice(listItemsHash.indexOf(itemHash), 1);
if (listItemsHash.length <= byPage) {
appendItem(listItemsHash[listItemsHash.length - 1]);
}
setCurrentItem(listItemsHash[indexItem]);
}
}
} else {
url = '?currentHash=' + currentHash +
'&page=' + currentPage;
}
client = new HTTPClient();
client.init(url + '&ajax');
try {
client.asyncGET(ajaxHandler);
} catch (e) {
alert(e);
}
}
function markAsCurrentItem() {
markAsItem(currentItemHash);
}
function markAsClickItem(event) {
event = event || window.event;
stopBubbling(event);
markAsItem(getItemHash(this));
return false;
}
function toggleMarkAsStarredLinkItem(itemHash) {
var i, item = getItem(itemHash), listLinks, url = '';
if (item !== null) {
listLinks = item.getElementsByTagName('a');
for (i = 0; i < listLinks.length; i += 1) {
if (hasClass(listLinks[i], 'item-starred')) {
url = listLinks[i].href;
if (listLinks[i].href.indexOf('unstar=') > -1) {
listLinks[i].href = listLinks[i].href.replace('unstar=','star=');
listLinks[i].firstChild.innerHTML = intlStar;
} else {
listLinks[i].href = listLinks[i].href.replace('star=','unstar=');
listLinks[i].firstChild.innerHTML = intlUnstar;
}
}
}
}
return url;
}
function markAsStarredCurrentItem() {
markAsStarredItem(currentItemHash);
}
function markAsStarredItem(itemHash) {
var url, client, indexItem;
url = toggleMarkAsStarredLinkItem(itemHash);
if (url.indexOf('unstar=') > -1 && stars) {
removeElement(getItem(itemHash));
indexItem = listItemsHash.indexOf(itemHash);
listItemsHash.splice(listItemsHash.indexOf(itemHash), 1);
if (listItemsHash.length <= byPage) {
appendItem(listItemsHash[listItemsHash.length - 1]);
}
setCurrentItem(listItemsHash[indexItem]);
url += '&page=' + currentPage;
}
if (url !== '') {
client = new HTTPClient();
client.init(url + '&ajax');
try {
client.asyncGET(ajaxHandler);
} catch (e) {
alert(e);
}
}
}
function markAsStarredClickItem(event) {
event = event || window.event;
stopBubbling(event);
markAsStarredItem(getItemHash(this));
return false;
}
function markAsRead(itemHash) {
setNbUnread(currentUnread - 1);
}
function markAsUnread(itemHash) {
setNbUnread(currentUnread + 1);
}
/**
* Div item functions
*/
function loadDivItem(itemHash, noFocus) {
var element, url, client, cacheItem;
element = document.getElementById('item-div-'+itemHash);
if (element.childNodes.length <= 1) {
cacheItem = getCacheItem(itemHash);
if (cacheItem !== null) {
setDivItem(element, cacheItem);
if(!noFocus) {
setItemFocus(element);
}
removeCacheItem(itemHash);
} else {
url = '?'+(stars?'stars&':'')+'currentHash=' + currentHash +
'¤t=' + itemHash +
'&ajax';
client = new HTTPClient();
client.init(url, noFocus);
try {
client.asyncGET(ajaxHandler);
} catch (e) {
alert(e);
}
}
}
}
function toggleItem(itemHash) {
var i, listElements, element, targetElement;
if (view === 'expanded') {
return;
}
if (currentItemHash != itemHash) {
closeCurrentItem();
}
// looking for ico + or -
listElements = document.getElementById('item-toggle-' + itemHash);
listElements = listElements.getElementsByTagName('span');
for (i = 0; i < listElements.length; i += 1) {
if (hasClass(listElements[i], 'item-toggle-open')) {
removeClass(listElements[i], 'item-toggle-open');
addClass(listElements[i], 'item-toggle-close');
} else if (hasClass(listElements[i], 'item-toggle-close')) {
removeClass(listElements[i], 'item-toggle-close');
addClass(listElements[i], 'item-toggle-open');
}
}
element = document.getElementById('item-toggle-'+itemHash);
targetElement = document.getElementById(
element.getAttribute('data-target').substring(1)
);
if (element.href.indexOf('&open') > -1) {
element.href = element.href.replace('&open','');
addClass(targetElement, 'well');
setCurrentItem(itemHash);
loadDivItem(itemHash);
} else {
element.href = element.href + '&open';
removeClass(targetElement, 'well');
}
}
function toggleCurrentItem() {
toggleItem(currentItemHash);
collapseElement(document.getElementById('item-toggle-' + currentItemHash));
}
function toggleClickItem(event) {
event = event || window.event;
stopBubbling(event);
toggleItem(getItemHash(this));
return false;
}
/**
* Item management functions
*/
function getItem(itemHash) {
return document.getElementById('item-' + itemHash);
}
function getTitleItem(itemHash) {
var i = 0, element = document.getElementById('item-div-'+itemHash), listElements = element.getElementsByTagName('a'), title = '';
for (i = 0; title === '' && i < listElements.length; i += 1) {
if (hasClass(listElements[i], 'item-link')) {
title = listElements[i].innerHTML;
}
}
return title;
}
function getUrlItem(itemHash) {
var i = 0, element = document.getElementById('item-'+itemHash), listElements = element.getElementsByTagName('a'), url = '';
for (i = 0; url === '' && i < listElements.length; i += 1) {
if (hasClass(listElements[i], 'item-link')) {
url = listElements[i].href;
}
}
return url;
}
function getViaItem(itemHash) {
var i = 0, element = document.getElementById('item-div-'+itemHash), listElements = element.getElementsByTagName('a'), via = '';
for (i = 0; via === '' && i < listElements.length; i += 1) {
if (hasClass(listElements[i], 'item-via')) {
via = listElements[i].href;
}
}
return via;
}
function getLiItem(element) {
var item = null;
while (item === null && element !== null) {
if (element.tagName === 'LI' && element.id.indexOf('item-') === 0) {
item = element;
}
element = element.parentNode;
}
return item;
}
function getItemHash(element) {
var item = getLiItem(element);
if (item !== null) {
return item.id.replace('item-','');
}
return null;
}
function getCacheItem(itemHash) {
if (typeof cache['item-' + itemHash] !== 'undefined') {
return cache['item-' + itemHash];
}
return null;
}
function removeCacheItem(itemHash) {
if (typeof cache['item-' + itemHash] !== 'undefined') {
delete cache['item-' + itemHash];
}
}
function isCurrentUnread() {
var item = getItem(currentItemHash);
if (hasClass(item, 'read')) {
return false;
}
return true;
}
function setDivItem(div, item) {
var markAs = intlRead, starred = intlStar, target = ' target="_blank"', linkMarkAs = 'read', linkStarred = 'star';
if (item['read'] == 1) {
markAs = intlUnread;
linkMarkAs = 'unread';
}
if (item['starred'] == 1) {
starred = intlUnstar;
linkStarred = 'unstar';
}
if (!blank) {
target = '';
}
div.innerHTML = '' +
'' +
'' +
item['time']['expanded'] +
'
' +
'' +
'' +
'' +
'' +
'' +
'';
initLinkItems(div.getElementsByTagName('a'));
initCollapse(div.getElementsByTagName('a'));
anonymize(div);
}
function setLiItem(li, item) {
var markAs = intlRead, target = ' target="_blank"';
if (item['read'] == 1) {
markAs = intlUnread;
}
if (!blank) {
target = '';
}
li.innerHTML = ' ' +
'' +
'' +
'' +
'' +
'' +
item['time']['list'] +
'' +
'' +
'- ' +
(addFavicon?
'' +
'' +
'':'' ) +
'' +
'' +
item['author'] +
'' +
'' +
'
' +
'- ' +
'' +
(stars?'':'' + markAs + ' ') +
'' +
item['title'] +
' ' +
'' +
'' +
'' +
item['description'] +
' ' +
'' +
'
' +
'
' +
'';
initCollapse(li.getElementsByTagName('a'));
initLinkItems(li.getElementsByTagName('a'));
anonymize(li);
}
function createLiItem(item) {
var li = document.createElement('li'),
div = document.createElement('div');
div.id = 'item-div-'+item['itemHash'];
div.className= 'item collapse'+(view === 'expanded' ? ' in well' : '');
li.id = 'item-'+item['itemHash'];
if (view === 'list') {
li.className = 'item-list';
setLiItem(li, item);
} else {
li.className = 'item-expanded';
setDivItem(div, item);
}
li.className += (item['read'] === 1)?' read':'';
li.appendChild(div);
return li;
}
/**
* List items management functions
*/
function getListItems() {
return document.getElementById('list-items');
}
function updateListItems(itemsList) {
var i;
for (i = 0; i < itemsList.length; i++) {
if (listItemsHash.indexOf(itemsList[i]['itemHash']) === -1 && listItemsHash.length <= byPage) {
cache['item-' + itemsList[i]['itemHash']] = itemsList[i];
listItemsHash.push(itemsList[i]['itemHash']);
if (listItemsHash.length <= byPage) {
appendItem(itemsList[i]['itemHash']);
}
}
}
}
function appendItem(itemHash) {
var listItems = getListItems(),
item = getCacheItem(itemHash),
li;
if (item !== null) {
li = createLiItem(item);
listItems.appendChild(li);
removeCacheItem(itemHash);
}
}
function getListLinkItems() {
var i = 0,
listItems = [],
listElements = document.getElementById('list-items');
listElements = listElements.getElementsByTagName('a');
for (i = 0; i < listElements.length; i += 1) {
listItems.push(listElements[i]);
}
return listItems;
}
function initListItemsHash() {
var i,
listElements = document.getElementById('list-items');
listElements = listElements.getElementsByTagName('li');
for (i = 0; i < listElements.length; i += 1) {
if (hasClass(listElements[i], 'item-list') || hasClass(listElements[i], 'item-expanded')) {
if (hasClass(listElements[i], 'current')) {
currentItemHash = getItemHash(listElements[i]);
}
listItemsHash.push(listElements[i].id.replace('item-',''));
}
}
}
function initLinkItems(listItems) {
var i = 0;
for (i = 0; i < listItems.length; i += 1) {
if (hasClass(listItems[i], 'item-toggle')) {
listItems[i].onclick = toggleClickItem;
}
if (hasClass(listItems[i], 'item-mark-as')) {
listItems[i].onclick = markAsClickItem;
}
if (hasClass(listItems[i], 'item-starred')) {
listItems[i].onclick = markAsStarredClickItem;
}
if (hasClass(listItems[i], 'item-shaarli')) {
listItems[i].onclick = shaarliClickItem;
}
}
}
function initListItems() {
var url, client;
url = '?currentHash=' + currentHash +
'&page=' + currentPage +
'&last=' + listItemsHash[listItemsHash.length -1] +
'&ajax' +
(stars?'&stars':'');
client = new HTTPClient();
client.init(url);
try {
client.asyncGET(ajaxHandler);
} catch (e) {
alert(e);
}
}
function preloadItems()
{
// Pre-fetch items from top to bottom
for(var i = 0, len = listItemsHash.length; i < len; ++i)
{
loadDivItem(listItemsHash[i], true);
}
}
/**
* Update
*/
function setStatus(text) {
if (text === '') {
document.getElementById('status').innerHTML = status;
} else {
document.getElementById('status').innerHTML = text;
}
}
function getTimeMin() {
return Math.round((new Date().getTime()) / 1000 / 60);
}
function updateFeed(feedHashIndex) {
var i = 0, url, client, feedHash = '';
if (feedHashIndex !== '') {
setStatus('updating ' + listUpdateFeeds[feedHashIndex][1]);
feedHash = listUpdateFeeds[feedHashIndex][0];
listUpdateFeeds[feedHashIndex][2] = getTimeMin();
}
url = '?update'+(feedHash === ''?'':'='+feedHash)+'&ajax';
client = new HTTPClient();
client.init(url);
try {
client.asyncGET(ajaxHandler);
} catch (e) {
alert(e);
}
}
function updateNextFeed() {
var i = 0, nextTimeUpdate = 0, currentMin, diff, minDiff = -1, feedToUpdateIndex = '', minFeedToUpdateIndex = '';
if (listUpdateFeeds.length !== 0) {
currentMin = getTimeMin();
for (i = 0; feedToUpdateIndex === '' && i < listUpdateFeeds.length; i++) {
diff = currentMin - listUpdateFeeds[i][2];
if (diff >= listUpdateFeeds[i][3]) {
//need update
feedToUpdateIndex = i;
} else {
if (minDiff === -1 || diff < minDiff) {
minDiff = diff;
minFeedToUpdateIndex = i;
}
}
}
if (feedToUpdateIndex === '') {
feedToUpdateIndex = minFeedToUpdateIndex;
}
updateFeed(feedToUpdateIndex);
} else {
updateFeed('');
}
}
function updateTimeout() {
var i = 0, nextTimeUpdate = 0, currentMin, diff, minDiff = -1, feedToUpdateIndex = '';
if (listUpdateFeeds.length !== 0) {
currentMin = getTimeMin();
for (i = 0; minDiff !== 0 && i < listUpdateFeeds.length; i++) {
diff = currentMin - listUpdateFeeds[i][2];
if (diff >= listUpdateFeeds[i][3]) {
//need update
minDiff = 0;
} else {
if (minDiff === -1 || (listUpdateFeeds[i][3] - diff) < minDiff) {
minDiff = listUpdateFeeds[i][3] - diff;
}
}
}
window.setTimeout(updateNextFeed, minDiff * 1000 * 60 + 200);
}
}
function updateNewItems(result) {
var i = 0, list, currentMin, folder, feed, unreadLabelItems, nbItems;
setStatus('');
if (result !== false) {
if (result['feeds']) {
// init list of feeds information for update
listUpdateFeeds = result['feeds'];
currentMin = getTimeMin();
for (i = 0; i < listUpdateFeeds.length; i++) {
listUpdateFeeds[i][2] = currentMin - listUpdateFeeds[i][2];
}
}
if (result.newItems && result.newItems.length > 0) {
nbItems = result.newItems.length;
currentNbItems += nbItems;
setNbUnread(currentUnread + nbItems);
addToUnreadLabel(getUnreadLabel(document.getElementById('all-subscriptions')), nbItems);
unreadLabelItems = getUnreadLabelItems(result.newItems[0].substr(0,6));
for (i = 0; i < unreadLabelItems.length; i += 1) {
feed = getLiParentByClassName(unreadLabelItems[i], 'feed');
folder = getLiParentByClassName(feed, 'folder');
addClass(feed, 'has-unread');
if (autohide) {
removeClass(feed, 'autohide-feed');
removeClass(folder, 'autohide-folder');
}
addToUnreadLabel(getUnreadLabel(feed), nbItems);
addToUnreadLabel(getUnreadLabel(folder), nbItems);
}
}
updateTimeout();
}
}
function initUpdate() {
window.setTimeout(updateNextFeed, 1000);
}
/**
* Navigation
*/
function setItemFocus(item) {
if(autofocus) {
// First, let the browser do some rendering
// Indeed, the div might not be visible yet, so there is no scroll
setTimeout(function()
{
// Dummy implementation
var container = document.getElementById('main-container'),
scrollPos = container.scrollTop,
itemPos = item.offsetTop,
temp = item;
while(temp.offsetParent != document.body) {
temp = temp.offsetParent;
itemPos += temp.offsetTop;
}
var current = itemPos - scrollPos;
// Scroll only if necessary
// current < 0: Happens when asking for previous item and displayed item is filling the screen
// Or check if item bottom is outside screen
if(current < 0 || current + item.offsetHeight > container.clientHeight) {
container.scrollTop = itemPos;
}
}, 0);
window.location.hash = '#item-' + currentItemHash;
}
}
function previousClickPage(event) {
event = event || window.event;
stopBubbling(event);
previousPage();
return false;
}
function nextClickPage(event) {
event = event || window.event;
stopBubbling(event);
nextPage();
return false;
}
function nextPage() {
currentPage = currentPage + 1;
if (currentPage > Math.ceil(currentNbItems / byPage)) {
currentPage = Math.ceil(currentNbItems / byPage);
}
if (listItemsHash.length === 0) {
currentPage = 1;
}
listItemsHash = [];
initListItems();
removeChildren(getListItems());
}
function previousPage() {
currentPage = currentPage - 1;
if (currentPage < 1) {
currentPage = 1;
}
listItemsHash = [];
initListItems();
removeChildren(getListItems());
}
function previousClickItem(event) {
event = event || window.event;
stopBubbling(event);
previousItem();
return false;
}
function nextClickItem(event) {
event = event || window.event;
stopBubbling(event);
nextItem();
return false;
}
function nextItem() {
var nextItemIndex = listItemsHash.indexOf(currentItemHash) + 1, nextCurrentItemHash;
closeCurrentItem();
if (autoreadItem && isCurrentUnread()) {
markAsCurrentItem();
if (filter == 'unread') {
nextItemIndex -= 1;
}
}
if (nextItemIndex < 0) { nextItemIndex = 0; }
if (nextItemIndex < listItemsHash.length) {
nextCurrentItemHash = listItemsHash[nextItemIndex];
}
if (nextItemIndex >= byPage) {
nextPage();
} else {
setCurrentItem(nextCurrentItemHash);
}
}
function previousItem() {
var previousItemIndex = listItemsHash.indexOf(currentItemHash) - 1, previousCurrentItemHash;
if (previousItemIndex < listItemsHash.length && previousItemIndex >= 0) {
previousCurrentItemHash = listItemsHash[previousItemIndex];
}
closeCurrentItem();
if (previousItemIndex < 0) {
previousPage();
} else {
setCurrentItem(previousCurrentItemHash);
}
}
function closeCurrentItem() {
var element = document.getElementById('item-toggle-' + currentItemHash);
if (element && view === 'list') {
var targetElement = document.getElementById(
element.getAttribute('data-target').substring(1)
);
if (element.href.indexOf('&open') < 0) {
element.href = element.href + '&open';
removeClass(targetElement, 'well');
collapseElement(element);
}
var i = 0,
listElements = element.getElementsByTagName('span');
// looking for ico + or -
for (i = 0; i < listElements.length; i += 1) {
if (hasClass(listElements[i], 'item-toggle-open')) {
removeClass(listElements[i], 'item-toggle-open');
addClass(listElements[i], 'item-toggle-close');
}
}
}
}
function setCurrentItem(itemHash) {
var currentItemIndex;
if (itemHash !== currentItemHash) {
removeClass(document.getElementById('item-'+currentItemHash), 'current');
removeClass(document.getElementById('item-div-'+currentItemHash), 'current');
if (typeof itemHash !== 'undefined') {
currentItemHash = itemHash;
}
currentItemIndex = listItemsHash.indexOf(currentItemHash);
if (currentItemIndex === -1) {
if (listItemsHash.length > 0) {
currentItemHash = listItemsHash[0];
} else {
currentItemHash = '';
}
} else {
if (currentItemIndex >= byPage) {
currentItemHash = listItemsHash[byPage - 1];
}
}
if (currentItemHash !== '') {
var item = document.getElementById('item-'+currentItemHash),
itemDiv = document.getElementById('item-div-'+currentItemHash);
addClass(item, 'current');
addClass(itemDiv, 'current');
setItemFocus(itemDiv);
updateItemButton();
}
}
updatePageButton();
}
function openCurrentItem(blank) {
var url;
url = getUrlItem(currentItemHash);
if (blank) {
window.location.href = url;
} else {
window.open(url);
}
}
// http://code.jquery.com/mobile/1.1.0/jquery.mobile-1.1.0.js (swipe)
function checkMove(e) {
// More than this horizontal displacement, and we will suppress scrolling.
var scrollSupressionThreshold = 10,
// More time than this, and it isn't a swipe.
durationThreshold = 500,
// Swipe horizontal displacement must be more than this.
horizontalDistanceThreshold = 30,
// Swipe vertical displacement must be less than this.
verticalDistanceThreshold = 75;
if (e.targetTouches.length == 1) {
var touch = e.targetTouches[0],
start = { time: ( new Date() ).getTime(),
coords: [ touch.pageX, touch.pageY ] },
stop;
var moveHandler = function ( e ) {
if ( !start ) {
return;
}
if (e.targetTouches.length == 1) {
var touch = e.targetTouches[0];
stop = { time: ( new Date() ).getTime(),
coords: [ touch.pageX, touch.pageY ] };
}
};
if (swipe) {
addEvent(window, 'touchmove', moveHandler);
addEvent(window, 'touchend', function (e) {
removeEvent(window, 'touchmove', moveHandler);
if ( start && stop ) {
if ( stop.time - start.time < durationThreshold &&
Math.abs( start.coords[ 0 ] - stop.coords[ 0 ] ) > horizontalDistanceThreshold &&
Math.abs( start.coords[ 1 ] - stop.coords[ 1 ] ) < verticalDistanceThreshold
) {
if ( start.coords[0] > stop.coords[ 0 ] ) {
nextItem();
}
else {
previousItem();
}
}
start = stop = undefined;
}
});
}
}
}
function checkKey(e) {
var code;
if (!e) e = window.event;
if (e.keyCode) code = e.keyCode;
else if (e.which) code = e.which;
if (!e.ctrlKey && !e.altKey) {
switch(code) {
case 32: // 'space'
toggleCurrentItem();
break;
case 65: // 'A'
if (window.confirm('Mark all current as read ?')) {
window.location.href = '?read=' + currentHash;
}
break;
case 67: // 'C'
window.location.href = '?config';
break;
case 69: // 'E'
window.location.href = (currentHash===''?'?edit':'?edit='+currentHash);
break;
case 70: // 'F'
if (listFeeds =='show') {
window.location.href = (currentHash===''?'?':'?currentHash='+currentHash+'&')+'listFeeds=hide';
} else {
window.location.href = (currentHash===''?'?':'?currentHash='+currentHash+'&')+'listFeeds=show';
}
break;
case 72: // 'H'
window.location.href = document.getElementById('nav-home').href;
break;
case 74: // 'J'
nextItem();
toggleCurrentItem();
break;
case 75: // 'K'
previousItem();
toggleCurrentItem();
break;
case 77: // 'M'
if (e.shiftKey) {
markAsCurrentItem();
toggleCurrentItem();
} else {
markAsCurrentItem();
}
break;
case 39: // right arrow
case 78: // 'N'
if (e.shiftKey) {
nextPage();
} else {
nextItem();
}
break;
case 79: // 'O'
if (e.shiftKey) {
openCurrentItem(true);
} else {
openCurrentItem(false);
}
break;
case 37: // left arrow
case 80 : // 'P'
if (e.shiftKey) {
previousPage();
} else {
previousItem();
}
break;
case 82: // 'R'
window.location.reload(true);
break;
case 83: // 'S'
shaarliCurrentItem();
break;
case 84: // 'T'
toggleCurrentItem();
break;
case 85: // 'U'
window.location.href = (currentHash===''?'?update':'?currentHash=' + currentHash + '&update='+currentHash);
break;
case 86: // 'V'
if (view == 'list') {
window.location.href = (currentHash===''?'?':'?currentHash='+currentHash+'&')+'view=expanded';
} else {
window.location.href = (currentHash===''?'?':'?currentHash='+currentHash+'&')+'view=list';
}
break;
case 90: // 'z'
for (var i=0;i Use Current Favicon', function() { setOriginalFavicon(false); } );
GM_registerMenuCommand( 'GReader Favicon Alerts > Use Original Favicon', function() { setOriginalFavicon(true); } );
}
(function FaviconAlerts() {
var self = this;
this.construct = function() {
this.head = document.getElementsByTagName('head')[0];
this.pixelMaps = {numbers: {0:[[1,1,1],[1,0,1],[1,0,1],[1,0,1],[1,1,1]],1:[[0,1,0],[1,1,0],[0,1,0],[0,1,0],[1,1,1]],2:[[1,1,1],[0,0,1],[1,1,1],[1,0,0],[1,1,1]],3:[[1,1,1],[0,0,1],[0,1,1],[0,0,1],[1,1,1]],4:[[0,0,1],[0,1,1],[1,0,1],[1,1,1],[0,0,1]],5:[[1,1,1],[1,0,0],[1,1,1],[0,0,1],[1,1,1]],6:[[0,1,1],[1,0,0],[1,1,1],[1,0,1],[1,1,1]],7:[[1,1,1],[0,0,1],[0,0,1],[0,1,0],[0,1,0]],8:[[1,1,1],[1,0,1],[1,1,1],[1,0,1],[1,1,1]],9:[[1,1,1],[1,0,1],[1,1,1],[0,0,1],[1,1,0]],'+':[[0,0,0],[0,1,0],[1,1,1],[0,1,0],[0,0,0]],'k':[[1,0,1],[1,1,0],[1,1,0],[1,0,1],[1,0,1]]}};
this.timer = setInterval(this.poll, 500);
this.poll();
return true;
};
this.drawUnreadCount = function(unread, callback) {
if(!self.textedCanvas) {
self.textedCanvas = [];
}
if(!self.textedCanvas[unread]) {
self.getUnreadCanvas(function(iconCanvas) {
var textedCanvas = document.createElement('canvas');
textedCanvas.height = textedCanvas.width = iconCanvas.width;
var ctx = textedCanvas.getContext('2d');
ctx.drawImage(iconCanvas, 0, 0);
ctx.fillStyle = '#b7bfc9';
ctx.strokeStyle = '#7792ba';
ctx.strokeWidth = 1;
var count = unread.length;
if(count > 4) {
unread = '1k+';
count = unread.length;
}
var bgHeight = self.pixelMaps.numbers[0].length;
var bgWidth = 0;
var padding = count < 4 ? 1 : 0;
var topMargin = 0;
for(var index = 0; index < count; index++) {
bgWidth += self.pixelMaps.numbers[unread[index]][0].length;
if(index < count-1) {
bgWidth += padding;
}
}
bgWidth = bgWidth > textedCanvas.width-4 ? textedCanvas.width-4 : bgWidth;
ctx.fillRect(textedCanvas.width-bgWidth-4,topMargin,bgWidth+4,bgHeight+4);
var digit;
var digitsWidth = bgWidth;
for(index = 0; index < count; index++) {
digit = unread[index];
if (self.pixelMaps.numbers[digit]) {
var map = self.pixelMaps.numbers[digit];
var height = map.length;
var width = map[0].length;
ctx.fillStyle = '#2c3323';
for (var y = 0; y < height; y++) {
for (var x = 0; x < width; x++) {
if(map[y][x]) {
ctx.fillRect(14- digitsWidth + x, y+topMargin+2, 1, 1);
}
}
}
digitsWidth -= width + padding;
}
}
ctx.strokeRect(textedCanvas.width-bgWidth-3.5,topMargin+0.5,bgWidth+3,bgHeight+3);
self.textedCanvas[unread] = textedCanvas;
callback(self.textedCanvas[unread]);
});
callback(self.textedCanvas[unread]);
}
};
this.getIcon = function(callback) {
self.getUnreadCanvas(function(canvas) {
callback(canvas.toDataURL('image/png'));
});
};
this.getIconSrc = function() {
var links = document.getElementsByTagName('link');
for (var i = 0; i < links.length; i++) {
if (links[i].rel === 'icon') {
return links[i].href;
}
}
return false;
};
this.getUnreadCanvas = function(callback) {
if(!self.unreadCanvas) {
self.unreadCanvas = document.createElement('canvas');
self.unreadCanvas.height = self.unreadCanvas.width = 16;
var ctx = self.unreadCanvas.getContext('2d');
var img = new Image();
img.addEventListener('load', function() {
ctx.drawImage(img, 0, 0);
callback(self.unreadCanvas);
}, true);
// if(GM_getValue('originalFavicon', false)) {
// img.src = self.icons.original;
// } else {
// img.src = self.icons.current;
// }
// img.src = 'inc/favicon.ico';
img.src = self.getIconSrc();
} else {
callback(self.unreadCanvas);
}
};
this.getUnreadCount = function() {
matches = self.getSearchText().match(/\((.*)\)/);
return matches ? matches[1] : false;
};
this.getUnreadCountIcon = function(callback) {
var unread = self.getUnreadCount();
self.drawUnreadCount(unread, function(icon) {
if(icon) {
callback(icon.toDataURL('image/png'));
}
});
};
this.getSearchText = function() {
var elt = document.getElementById('nb-unread');
if (!elt) {
elt = document.getElementById('nb-starred');
}
if (elt) {
return 'Kriss feed (' + parseInt(elt.innerHTML, 10) + ')';
}
return '';
};
this.poll = function() {
if(self.getUnreadCount() != "0") {
self.getUnreadCountIcon(function(icon) {
self.setIcon(icon);
});
} else {
self.getIcon(function(icon) {
self.setIcon(icon);
});
}
};
this.setIcon = function(icon) {
var links = self.head.getElementsByTagName('link');
for (var i = 0; i < links.length; i++)
if ((links[i].rel == 'shortcut icon' || links[i].rel=='icon') &&
links[i].href != icon)
self.head.removeChild(links[i]);
else if(links[i].href == icon)
return;
var newIcon = document.createElement('link');
newIcon.type = 'image/png';
newIcon.rel = 'shortcut icon';
newIcon.href = icon;
self.head.appendChild(newIcon);
// Chrome hack for updating the favicon
//var shim = document.createElement('iframe');
//shim.width = shim.height = 0;
//document.body.appendChild(shim);
//shim.src = 'icon';
//document.body.removeChild(shim);
};
this.toString = function() { return '[object FaviconAlerts]'; };
return this.construct();
}());
assign('base', MyTool::getUrl());
$pb->assign('version', FEED_VERSION);
$pb->assign('pagetitle', 'KrISS feed');
$pb->assign('referer', $referer);
$pb->assign('langs', Intl::$langList);
$pb->assign('lang', Intl::$langList[Intl::$lang]);
$pb->assign('query_string', isset($_SERVER['QUERY_STRING'])?$_SERVER['QUERY_STRING']:'');
if (!is_dir(DATA_DIR)) {
if (!@mkdir(DATA_DIR, 0755)) {
$pb->assign('message', sprintf(Intl::msg('Can not create %s directory, check permissions'), DATA_DIR));
$pb->renderPage('message');
}
@chmod(DATA_DIR, 0755);
if (!is_file(DATA_DIR.'/.htaccess')) {
if (!@file_put_contents(
DATA_DIR.'/.htaccess',
"Allow from none\nDeny from all\n"
)) {
$pb->assign('message', sprintf(Intl::msg('Can not protect %s directory with .htaccess, check permissions'), DATA_DIR));
$pb->renderPage('message');
}
}
}
// XSRF protection with token
if (!empty($_POST)) {
if (!Session::isToken($_POST['token'])) {
$pb->assign('message', Intl::msg('Wrong token'));
$pb->renderPage('message');
}
unset($_SESSION['tokens']);
}
$kfc = new FeedConf(CONFIG_FILE, FEED_VERSION);
$kf = new Feed(DATA_FILE, CACHE_DIR, $kfc);
$ks = new Star(STAR_FILE, ITEM_FILE, $kfc);
// autosave opml
if (Session::isLogged()) {
if (!is_file(OPML_FILE)) {
$kf->loadData();
file_put_contents(OPML_FILE, Opml::generateOpml($kf->getFeeds(), $kf->getFolders()));
} else {
if (filemtime(OPML_FILE) < time() - UPDATECHECK_INTERVAL) {
$kf->loadData();
rename(OPML_FILE, OPML_FILE_SAVE);
file_put_contents(OPML_FILE, Opml::generateOpml($kf->getFeeds(), $kf->getFolders()));
}
}
}
// List or Expanded ?
$view = $kfc->view;
// show or hide list of feeds ?
$listFeeds = $kfc->listFeeds;
// All or Unread ?
$filter = $kfc->filter;
// newerFirst or olderFirst
$order = $kfc->order;
// number of item by page
$byPage = $kfc->byPage;
// Hash : 'all', feed hash or folder hash
$currentHash = $kfc->getCurrentHash();
// Query
$query = '?';
if (isset($_GET['stars'])) {
$query .= 'stars&';
}
if (!empty($currentHash) and $currentHash !== 'all') {
$query .= 'currentHash='.$currentHash.'&';
}
$pb->assign('view', $view);
$pb->assign('listFeeds', $listFeeds);
$pb->assign('filter', $filter);
$pb->assign('order', $order);
$pb->assign('byPage', $byPage);
$pb->assign('currentHash', $currentHash);
$pb->assign('query', htmlspecialchars($query));
$pb->assign('redirector', $kfc->redirector);
$pb->assign('shaarli', htmlspecialchars($kfc->shaarli));
$pb->assign('autoreadItem', $kfc->autoreadItem);
$pb->assign('autoreadPage', $kfc->autoreadPage);
$pb->assign('autohide', $kfc->autohide);
$pb->assign('autofocus', $kfc->autofocus);
$pb->assign('autoupdate', $kfc->autoUpdate);
$pb->assign('addFavicon', $kfc->addFavicon);
$pb->assign('preload', $kfc->preload);
$pb->assign('blank', $kfc->blank);
$pb->assign('swipe', $kfc->swipe);
$pb->assign('kf', $kf);
$pb->assign('isLogged', $kfc->isLogged());
$pb->assign('pagetitle', strip_tags($kfc->title));
if (isset($_GET['login'])) {
// Login
if (!empty($_POST['login'])
&& !empty($_POST['password'])
) {
if (Session::login(
$kfc->login,
$kfc->hash,
$_POST['login'],
sha1($_POST['password'].$_POST['login'].$kfc->salt)
)) {
if (!empty($_POST['longlastingsession'])) {
// (31536000 seconds = 1 year)
$_SESSION['longlastingsession'] = 31536000;
} else {
// when browser closes
$_SESSION['longlastingsession'] = 0;
}
session_regenerate_id(true);
MyTool::redirect();
}
if (Session::banCanLogin()) {
$pb->assign('message', Intl::msg('Login failed!'));
} else {
$pb->assign('message', Intl::msg('I said: NO. You are banned for the moment. Go away.'));
}
$pb->renderPage('message');
} else {
$pb->assign('pagetitle', Intl::msg('Sign in').' - '.strip_tags($kfc->title));
$pb->assign('token', Session::getToken());
$pb->renderPage('login');
}
} elseif (isset($_GET['logout'])) {
//Logout
Session::logout();
MyTool::redirect();
} elseif (isset($_GET['password']) && $kfc->isLogged()) {
if (isset($_POST['save'])) {
if ($kfc->hash === sha1($_POST['oldpassword'].$kfc->login.$kfc->salt)) {
$kfc->setHash($_POST['newpassword']);
$kfc->write();
MyTool::redirect();
}
} elseif (isset($_POST['cancel'])) {
MyTool::redirect();
}
$pb->assign('pagetitle', Intl::msg('Change your password').' - '.strip_tags($kfc->title));
$pb->assign('token', Session::getToken());
$pb->renderPage('change_password');
} elseif (isset($_GET['ajax'])) {
if (isset($_GET['stars'])) {
$filter = 'all';
$kf = $ks;
}
$kf->loadData();
$needSave = false;
$needStarSave = false;
$result = array();
if (!$kfc->isLogged()) {
$result['logout'] = true;
}
if (isset($_GET['current'])) {
$result['item'] = $kf->getItem($_GET['current'], false);
$result['item']['itemHash'] = $_GET['current'];
}
if (isset($_GET['read'])) {
$needSave = $kf->mark($_GET['read'], 1);
if ($needSave && $kfc->isLogged()) {
$result['read'] = $_GET['read'];
}
}
if (isset($_GET['unread'])) {
$needSave = $kf->mark($_GET['unread'], 0);
if ($needSave && $kfc->isLogged()) {
$result['unread'] = $_GET['unread'];
}
}
if (isset($_GET['star']) && !isset($_GET['stars'])) {
$hash = $_GET['star'];
$item = $kf->loadItem($hash, false);
$feed = $kf->getFeed(substr($hash, 0, 6));
$ks->loadData();
$needStarSave = $ks->markItem($_GET['star'], 1, $feed, $item);
if ($needStarSave) {
$result['star'] = $hash;
}
}
if (isset($_GET['unstar'])) {
$hash = $_GET['unstar'];
$ks->loadData();
$needStarSave = $ks->markItem($hash, 0);
if ($needStarSave) {
$result['unstar'] = $hash;
}
}
if (isset($_GET['toggleFolder'])) {
$needSave = $kf->toggleFolder($_GET['toggleFolder']);
}
if (isset($_GET['page'])) {
$listItems = $kf->getItems($currentHash, $filter);
$currentPage = $_GET['page'];
$index = ($currentPage - 1) * $byPage;
$results = array_slice($listItems, $index, $byPage + 1, true);
$result['page'] = array();
$firstIndex = -1;
if (isset($_GET['last'])) {
$firstIndex = array_search($_GET['last'], array_keys($results));
if ($firstIndex === false) {
$firstIndex = -1;
}
}
$i = 0;
foreach (array_slice($results, $firstIndex + 1, count($results) - $firstIndex - 1, true) as $itemHash => $item) {
if (isset($_GET['stars'])) {
$result['page'][$i] = $kf->getItem($itemHash);
} else {
$result['page'][$i] = $kf->getItem($itemHash, false);
$result['page'][$i]['read'] = $item[1];
}
$i++;
}
}
if (isset($_GET['update'])) {
if ($kfc->isLogged()) {
if (empty($_GET['update'])) {
$result['update']['feeds'] = array();
$feedsHash = $kf->orderFeedsForUpdate(array_keys($kf->getFeeds()));
foreach ($feedsHash as $feedHash) {
$feed = $kf->getFeed($feedHash);
$result['update']['feeds'][] = array($feedHash, $feed['title'], (int) ((time() - $feed['lastUpdate']) / 60), $kf->getTimeUpdate($feed));
}
} else {
$feed = $kf->getFeed($_GET['update']);
$info = $kf->updateChannel($_GET['update']);
if (empty($info['error'])) {
$info['error'] = $feed['description'];
}
$info['newItems'] = array_keys($info['newItems']);
$result['update'] = $info;
}
} else {
$result['update'] = false;
}
}
if ($needSave) {
$kf->writeData();
}
if ($needStarSave) {
$ks->writeData();
}
MyTool::renderJson($result);
} elseif (isset($_GET['help']) && ($kfc->isLogged() || $kfc->visibility === 'protected')) {
$pb->assign('pagetitle', Intl::msg('Help').' - '.strip_tags($kfc->title));
$pb->renderPage('help');
} elseif ((isset($_GET['update'])
&& ($kfc->isLogged()
|| (isset($_GET['cron'])
&& $_GET['cron'] === sha1($kfc->salt.$kfc->hash))))
|| (isset($argv)
&& count($argv) >= 3
&& $argv[1] == 'update'
&& $argv[2] == sha1($kfc->salt.$kfc->hash))) {
// Update
$kf->loadData();
$forceUpdate = false;
if (isset($_GET['force'])) {
$forceUpdate = true;
}
$feedsHash = array();
$hash = 'all';
if (isset($_GET['update'])) {
$hash = $_GET['update'];
}
// type : 'feed', 'folder', 'all', 'item'
$type = $kf->hashType($hash);
switch($type) {
case 'feed':
$feedsHash[] = $hash;
break;
case 'folder':
$feedsHash = $kf->getFeedsHashFromFolderHash($hash);
break;
case 'all':
case '':
$feedsHash = array_keys($kf->getFeeds());
break;
case 'item':
default:
break;
}
$pb->assign('currentHash', $hash);
if (isset($_GET['cron']) || isset($argv) && count($argv) >= 3) {
$kf->updateFeedsHash($feedsHash, $forceUpdate);
} else {
$pb->assign('feedsHash', $feedsHash);
$pb->assign('forceUpdate', $forceUpdate);
$pb->assign('pagetitle', Intl::msg('Update').' - '.strip_tags($kfc->title));
$pb->renderPage('update');
}
} elseif (isset($_GET['plugins']) && $kfc->isLogged()) {
$pb->assign('pagetitle', Intl::msg('Plugins management').' - '.strip_tags($kfc->title));
$pb->assign('plugins', Plugin::listAll());
$pb->renderPage('plugins');
} elseif (isset($_GET['config']) && $kfc->isLogged()) {
// Config
if (isset($_POST['save'])) {
if (isset($_POST['disableSessionProtection'])) {
$_POST['disableSessionProtection'] = '1';
} else {
$_POST['disableSessionProtection'] = '0';
}
$kfc->hydrate($_POST);
MyTool::redirect();
} elseif (isset($_POST['cancel'])) {
MyTool::redirect();
} else {
$menu = $kfc->getMenu();
$paging = $kfc->getPaging();
$pb->assign('page', 'config');
$pb->assign('pagetitle', Intl::msg('Configuration').' - '.strip_tags($kfc->title));
$pb->assign('kfctitle', htmlspecialchars($kfc->title));
$pb->assign('kfcredirector', htmlspecialchars($kfc->redirector));
$pb->assign('kfcshaarli', htmlspecialchars($kfc->shaarli));
$pb->assign('kfclocale', htmlspecialchars($kfc->locale));
$pb->assign('kfcmaxitems', htmlspecialchars($kfc->maxItems));
$pb->assign('kfcmaxupdate', htmlspecialchars($kfc->maxUpdate));
$pb->assign('kfcvisibility', htmlspecialchars($kfc->visibility));
$pb->assign('kfccron', sha1($kfc->salt.$kfc->hash));
$pb->assign('kfcautoreaditem', (int) $kfc->autoreadItem);
$pb->assign('kfcautoreadpage', (int) $kfc->autoreadPage);
$pb->assign('kfcautoupdate', (int) $kfc->autoUpdate);
$pb->assign('kfcautohide', (int) $kfc->autohide);
$pb->assign('kfcautofocus', (int) $kfc->autofocus);
$pb->assign('kfcaddfavicon', (int) $kfc->addFavicon);
$pb->assign('kfcpreload', (int) $kfc->preload);
$pb->assign('kfcblank', (int) $kfc->blank);
$pb->assign('kfcswipe', (int) $kfc->swipe);
$pb->assign('kfcdisablesessionprotection', (int) $kfc->disableSessionProtection);
$pb->assign('kfcmenu', $menu);
$pb->assign('kfcpaging', $paging);
$pb->assign('token', Session::getToken());
$pb->assign('scriptfilename', $_SERVER["SCRIPT_FILENAME"]);
$pb->renderPage('config');
}
} elseif (isset($_GET['import']) && $kfc->isLogged()) {
// Import
if (isset($_POST['import'])) {
// If file is too big, some form field may be missing.
if ((!isset($_FILES))
|| (isset($_FILES['filetoupload']['size'])
&& $_FILES['filetoupload']['size']==0)
) {
$rurl = empty($_SERVER['HTTP_REFERER'])
? '?'
: $_SERVER['HTTP_REFERER'];
$pb->assign('message', sprintf(Intl::msg('The file you are trying to upload is probably bigger than what this webserver can accept (%s). Please upload in smaller chunks.'), MyTool::humanBytes(MyTool::getMaxFileSize())));
$pb->assign('referer', $rurl);
$pb->renderPage('message');
}
$kf->loadData();
$kf->setData(Opml::importOpml($kf->getData()));
$kf->sortFeeds();
$kf->writeData();
exit;
} else if (isset($_POST['cancel'])) {
MyTool::redirect();
} else {
$pb->assign('pagetitle', Intl::msg('Import').' - '.strip_tags($kfc->title));
$pb->assign('maxsize', MyTool::getMaxFileSize());
$pb->assign('humanmaxsize', MyTool::humanBytes(MyTool::getMaxFileSize()));
$pb->assign('token', Session::getToken());
$pb->renderPage('import');
}
} elseif (isset($_GET['export']) && $kfc->isLogged()) {
// Export
$kf->loadData();
Opml::exportOpml($kf->getFeeds(), $kf->getFolders());
} elseif (isset($_GET['add']) && $kfc->isLogged()) {
// Add feed
$kf->loadData();
if (isset($_POST['newfeed']) && !empty($_POST['newfeed'])) {
$addc = $kf->addChannel($_POST['newfeed']);
if (empty($addc['error'])) {
// Add success
$folders = array();
if (!empty($_POST['folders'])) {
foreach ($_POST['folders'] as $hashFolder) {
$folders[] = $hashFolder;
}
}
if (!empty($_POST['newfolder'])) {
$newFolderHash = MyTool::smallHash($_POST['newfolder']);
$kf->addFolder($_POST['newfolder'], $newFolderHash);
$folders[] = $newFolderHash;
}
$hash = MyTool::smallHash($_POST['newfeed']);
$kf->editFeed($hash, '', '', $folders, '', '');
$kf->sortFeeds();
$kf->writeData();
MyTool::redirect('?currentHash='.$hash);
} else {
// Add fail
$pb->assign('message', $addc['error']);
$pb->renderPage('message');
}
}
$newfeed = '';
if (isset($_GET['newfeed'])) {
$newfeed = htmlspecialchars($_GET['newfeed']);
}
$pb->assign('page', 'add');
$pb->assign('pagetitle', Intl::msg('Add a new feed').' - '.strip_tags($kfc->title));
$pb->assign('newfeed', $newfeed);
$pb->assign('folders', $kf->getFolders());
$pb->assign('token', Session::getToken());
$pb->renderPage('add_feed');
} elseif (isset($_GET['toggleFolder']) && $kfc->isLogged()) {
$kf->loadData();
$kf->toggleFolder($_GET['toggleFolder']);
$kf->writeData();
MyTool::redirect($query);
} elseif ((isset($_GET['read'])
|| isset($_GET['unread']))
&& $kfc->isLogged()) {
// mark all as read : item, feed, folder, all
$kf->loadData();
$read = 1;
if (isset($_GET['read'])) {
$hash = $_GET['read'];
$read = 1;
} else {
$hash = $_GET['unread'];
$read = 0;
}
$needSave = $kf->mark($hash, $read);
if ($needSave) {
$kf->writeData();
}
// type : 'feed', 'folder', 'all', 'item'
$type = $kf->hashType($hash);
if ($type === 'item') {
$query .= 'current='.$hash;
} else {
if ($filter === 'unread' && $read === 1) {
$query = '?';
}
}
MyTool::redirect($query);
} elseif ((isset($_GET['star'])
|| isset($_GET['unstar']))
&& $kfc->isLogged()) {
// mark all as starred : item, feed, folder, all
$kf->loadData();
$ks->loadData();
$starred = 1;
if (isset($_GET['star'])) {
$hash = $_GET['star'];
$starred = 1;
$item = $kf->loadItem($hash, false);
$feed = $kf->getFeed(substr($hash, 0, 6));
$needSave = $ks->markItem($hash, $starred, $feed, $item);
} else {
$hash = $_GET['unstar'];
$starred = 0;
$needSave = $ks->markItem($hash, $starred);
}
if ($needSave) {
$ks->writeData();
}
// type : 'feed', 'folder', 'all', 'item'
$type = $kf->hashType($hash);
if ($type === 'item') {
$query .= 'current='.$hash;
}
MyTool::redirect($query);
} elseif (isset($_GET['stars']) && $kfc->isLogged()) {
$ks->loadData();
$GLOBALS['starredItems'] = $ks->getItems();
$listItems = $ks->getItems($currentHash, 'all');
$listHash = array_keys($listItems);
$currentItemHash = '';
if (isset($_GET['current']) && !empty($_GET['current'])) {
$currentItemHash = $_GET['current'];
}
if (isset($_GET['next']) && !empty($_GET['next'])) {
$currentItemHash = $_GET['next'];
}
if (isset($_GET['previous']) && !empty($_GET['previous'])) {
$currentItemHash = $_GET['previous'];
}
if (empty($currentItemHash)) {
$currentPage = $kfc->getCurrentPage();
$index = ($currentPage - 1) * $byPage;
} else {
$index = array_search($currentItemHash, $listHash);
if (isset($_GET['next'])) {
if ($index < count($listHash)-1) {
$index++;
}
}
if (isset($_GET['previous'])) {
if ($index > 0) {
$index--;
}
}
}
if ($index < count($listHash)) {
$currentItemHash = $listHash[$index];
} else {
$index = count($listHash) - 1;
}
// pagination
$currentPage = (int) ($index/$byPage)+1;
if ($currentPage <= 0) {
$currentPage = 1;
}
$begin = ($currentPage - 1) * $byPage;
$maxPage = (count($listItems) <= $byPage) ? '1' : ceil(count($listItems) / $byPage);
$nbItems = count($listItems);
// list items
$listItems = array_slice($listItems, $begin, $byPage, true);
// type : 'feed', 'folder', 'all', 'item'
$currentHashType = $kf->hashType($currentHash);
$hashView = '';
switch($currentHashType){
case 'all':
$hashView = ''.$nbItems.' '.Intl::msg('starred items').'';
break;
case 'feed':
$hashView = 'Feed ('.$kf->getFeedTitle($currentHash).'): '.''.$nbItems.' '.Intl::msg('starred items').'';
break;
case 'folder':
$hashView = 'Folder ('.$kf->getFolderTitle($currentHash).'): '.$nbItems.' '.Intl::msg('starred items').'';
break;
default:
$hashView = ''.$nbItems.' '.Intl::msg('starred items').'';
break;
}
$menu = $kfc->getMenu();
$paging = $kfc->getPaging();
$pb->assign('menu', $menu);
$pb->assign('paging', $paging);
$pb->assign('currentHashType', $currentHashType);
$pb->assign('currentHashView', $hashView);
$pb->assign('currentPage', (int) $currentPage);
$pb->assign('maxPage', (int) $maxPage);
$pb->assign('currentItemHash', $currentItemHash);
$pb->assign('nbItems', $nbItems);
$pb->assign('items', $listItems);
if ($listFeeds == 'show') {
$pb->assign('feedsView', $ks->getFeedsView());
}
$pb->assign('kf', $ks);
$pb->assign('pagetitle', Intl::msg('Starred items').' - '.strip_tags($kfc->title));
$pb->renderPage('index');
} elseif (isset($_GET['edit']) && $kfc->isLogged()) {
// Edit feed, folder, all
$kf->loadData();
$pb->assign('page', 'edit');
$pb->assign('pagetitle', Intl::msg('Edit').' - '.strip_tags($kfc->title));
$pb->assign('token', Session::getToken());
$hash = substr(trim($_GET['edit'], '/'), 0, 6);
// type : 'feed', 'folder', 'all', 'item'
$type = $kf->hashType($currentHash);
$type = $kf->hashType($hash);
switch($type) {
case 'feed':
if (isset($_POST['save'])) {
$title = $_POST['title'];
$description = $_POST['description'];
$htmlUrl = $_POST['htmlUrl'];
$folders = array();
if (!empty($_POST['folders'])) {
foreach ($_POST['folders'] as $hashFolder) {
$folders[] = $hashFolder;
}
}
if (!empty($_POST['newfolder'])) {
$newFolderHash = MyTool::smallHash($_POST['newfolder']);
$kf->addFolder($_POST['newfolder'], $newFolderHash);
$folders[] = $newFolderHash;
}
$timeUpdate = $_POST['timeUpdate'];
$kf->editFeed($hash, $title, $description, $folders, $timeUpdate, $htmlUrl);
$kf->writeData();
MyTool::redirect();
} elseif (isset($_POST['delete'])) {
$kf->removeFeed($hash);
$kf->writeData();
MyTool::redirect('?');
} elseif (isset($_POST['cancel'])) {
MyTool::redirect();
} else {
$feed = $kf->getFeed($hash);
if (!empty($feed)) {
$lastUpdate = 'need update';
if (!$kf->needUpdate($feed)) {
$diff = (int) (time() - $feed['lastUpdate']);
$lastUpdate =
(int) ($diff / 60) . ' m ' . (int) ($diff % 60) . ' s';
}
$pb->assign('feed', $feed);
$pb->assign('folders', $kf->getFolders());
$pb->assign('lastUpdate', $lastUpdate);
$pb->renderPage('edit_feed');
} else {
MyTool::redirect();
}
}
break;
case 'folder':
if (isset($_POST['save'])) {
$oldFolderTitle = $kf->getFolderTitle($hash);
$newFolderTitle = $_POST['foldertitle'];
if ($oldFolderTitle !== $newFolderTitle) {
$kf->renameFolder($hash, $newFolderTitle);
$kf->writeData();
}
if (empty($newFolderTitle)) {
MyTool::redirect('?');
} else {
MyTool::redirect('?currentHash='.MyTool::smallHash($newFolderTitle));
}
} elseif (isset($_POST['cancel'])) {
MyTool::redirect();
} else {
$folderTitle = $kf->getFolderTitle($hash);
$pb->assign('foldertitle', htmlspecialchars($folderTitle));
$pb->renderPage('edit_folder');
}
break;
case 'all':
if (isset($_POST['save'])) {
foreach (array_keys($_POST) as $key) {
if (strpos($key, 'order-folder-') !== false) {
$folderHash = str_replace('order-folder-', '', $key);
$kf->orderFolder($folderHash, (int) $_POST[$key]);
}
}
$feedsHash = array();
if (isset($_POST['feeds'])) {
foreach ($_POST['feeds'] as $feedHash) {
$feedsHash[] = $feedHash;
}
}
foreach ($feedsHash as $feedHash) {
$feed = $kf->getFeed($feedHash);
$addFoldersHash = $feed['foldersHash'];
if (!empty($_POST['addfolders'])) {
foreach ($_POST['addfolders'] as $folderHash) {
if (!in_array($folderHash, $addFoldersHash)) {
$addFoldersHash[] = $folderHash;
}
}
}
if (!empty($_POST['addnewfolder'])) {
$newFolderHash = MyTool::smallHash($_POST['addnewfolder']);
$kf->addFolder($_POST['addnewfolder'], $newFolderHash);
$addFoldersHash[] = $newFolderHash;
}
$removeFoldersHash = array();
if (!empty($_POST['removefolders'])) {
foreach ($_POST['removefolders'] as $folderHash) {
$removeFoldersHash[] = $folderHash;
}
}
$addFoldersHash = array_diff($addFoldersHash, $removeFoldersHash);
$kf->editFeed($feedHash, '', '', $addFoldersHash, '', '');
}
$kf->sortFolders();
$kf->writeData();
MyTool::redirect();
} elseif (isset($_POST['delete'])) {
foreach ($_POST['feeds'] as $feedHash) {
$kf->removeFeed($feedHash);
}
$kf->writeData();
MyTool::redirect();
} elseif (isset($_POST['cancel'])) {
MyTool::redirect();
} else {
$folders = $kf->getFolders();
$listFeeds = $kf->getFeeds();
$pb->assign('folders', $folders);
$pb->assign('listFeeds', $listFeeds);
$pb->renderPage('edit_all');
}
break;
case 'item':
default:
MyTool::redirect();
break;
}
} elseif (isset($_GET['shaarli'])) {
$kf->loadData();
$item = $kf->getItem($_GET['shaarli'], false);
$shaarli = $kfc->shaarli;
if (!empty($shaarli)) {
// remove sel used with javascript
$shaarli = str_replace('${sel}', '', $shaarli);
$url = htmlspecialchars_decode($item['link']);
$via = htmlspecialchars_decode($item['via']);
$title = htmlspecialchars_decode($item['title']);
if (parse_url($url, PHP_URL_HOST) !== parse_url($via, PHP_URL_HOST)) {
$via = 'via '.$via;
} else {
$via = '';
}
$shaarli = str_replace('${url}', urlencode($url), $shaarli);
$shaarli = str_replace('${title}', urlencode($title), $shaarli);
$shaarli = str_replace('${via}', urlencode($via), $shaarli);
header('Location: '.$shaarli);
} else {
$pb->assign('message', Intl::msg('Please configure your share link first'));
$pb->renderPage('message');
}
} else {
if (($kfc->isLogged() || $kfc->visibility === 'protected') && !isset($_GET['password']) && !isset($_GET['help']) && !isset($_GET['update']) && !isset($_GET['config']) && !isset($_GET['import']) && !isset($_GET['export']) && !isset($_GET['add']) && !isset($_GET['toggleFolder']) && !isset($_GET['read']) && !isset($_GET['unread']) && !isset($_GET['edit'])) {
$ks->loadData();
$GLOBALS['starredItems'] = $ks->getItems();
$kf->loadData();
if ($kf->updateItems()) {
$kf->writeData();
}
$listItems = $kf->getItems($currentHash, $filter);
$listHash = array_keys($listItems);
$currentItemHash = '';
if (isset($_GET['current']) && !empty($_GET['current'])) {
$currentItemHash = $_GET['current'];
}
if (isset($_GET['next']) && !empty($_GET['next'])) {
$currentItemHash = $_GET['next'];
if ($kfc->autoreadItem) {
if ($kf->mark($currentItemHash, 1)) {
if ($filter == 'unread') {
unset($listItems[$currentItemHash]);
}
$kf->writeData();
}
}
}
if (isset($_GET['previous']) && !empty($_GET['previous'])) {
$currentItemHash = $_GET['previous'];
}
if (empty($currentItemHash)) {
$currentPage = $kfc->getCurrentPage();
$index = ($currentPage - 1) * $byPage;
} else {
$index = array_search($currentItemHash, $listHash);
if (isset($_GET['next'])) {
if ($index < count($listHash)-1) {
$index++;
}
}
if (isset($_GET['previous'])) {
if ($index > 0) {
$index--;
}
}
}
if ($index < count($listHash)) {
$currentItemHash = $listHash[$index];
} else {
$index = count($listHash) - 1;
}
$unread = 0;
foreach ($listItems as $itemHash => $item) {
if ($item[1] === 0) {
$unread++;
}
}
// pagination
$currentPage = (int) ($index/$byPage)+1;
if ($currentPage <= 0) {
$currentPage = 1;
}
$begin = ($currentPage - 1) * $byPage;
$maxPage = (count($listItems) <= $byPage) ? '1' : ceil(count($listItems) / $byPage);
$nbItems = count($listItems);
// list items
$listItems = array_slice($listItems, $begin, $byPage, true);
// type : 'feed', 'folder', 'all', 'item'
$currentHashType = $kf->hashType($currentHash);
$hashView = '';
switch($currentHashType){
case 'all':
$hashView = ''.$unread.' '.Intl::msg('unread items').'';
break;
case 'feed':
$hashView = 'Feed ('.$kf->getFeedTitle($currentHash).'): '.''.$unread.' '.Intl::msg('unread items').'';
break;
case 'folder':
$hashView = 'Folder ('.$kf->getFolderTitle($currentHash).'): '.$unread.' '.Intl::msg('unread items').'';
break;
default:
$hashView = ''.$unread.' '.Intl::msg('unread items').'';
break;
}
$menu = $kfc->getMenu();
$paging = $kfc->getPaging();
$pb->assign('menu', $menu);
$pb->assign('paging', $paging);
$pb->assign('currentHashType', $currentHashType);
$pb->assign('currentHashView', $hashView);
$pb->assign('currentPage', (int) $currentPage);
$pb->assign('maxPage', (int) $maxPage);
$pb->assign('currentItemHash', $currentItemHash);
$pb->assign('nbItems', $nbItems);
$pb->assign('items', $listItems);
if ($listFeeds == 'show') {
$pb->assign('feedsView', $kf->getFeedsView());
}
$pb->assign('pagetitle', strip_tags($kfc->title));
$pb->renderPage('index');
} else {
$pb->assign('pagetitle', Intl::msg('Sign in').' - '.strip_tags($kfc->title));
if (!empty($_SERVER['QUERY_STRING'])) {
$pb->assign('referer', MyTool::getUrl().'?'.$_SERVER['QUERY_STRING']);
}
$pb->assign('token', Session::getToken());
$pb->renderPage('login');
}
}