├── .gitignore ├── LICENSE ├── README.md ├── add-single.php ├── add-tag.php ├── add.php ├── autoloader.php ├── classes ├── favicon.php ├── fof-prefs.php ├── pdolog.php └── svghistogram.php ├── data └── cookies.txt.dist ├── delete.php ├── favicon.php ├── feed-action.php ├── feed-detail.php ├── fof-asset-custom.php.dist ├── fof-asset.php ├── fof-config.php.dist ├── fof-custom.css.dist ├── fof-db.php ├── fof-install.php ├── fof-main.php ├── fof-render.php ├── fof.css ├── fof.js ├── footer.php ├── header.php ├── image ├── feed-icon.png ├── grippy.png ├── spinner.gif ├── tag-icon.svg ├── throbber.gif └── warn.gif ├── img.php ├── index.php ├── install.php ├── item.php ├── items.php ├── library ├── SimplePie.php ├── SimplePie │ ├── Author.php │ ├── Cache.php │ ├── Cache │ │ ├── Base.php │ │ ├── DB.php │ │ ├── File.php │ │ ├── Memcache.php │ │ ├── Memcached.php │ │ ├── MySQL.php │ │ └── Redis.php │ ├── Caption.php │ ├── Category.php │ ├── Content │ │ └── Type │ │ │ └── Sniffer.php │ ├── Copyright.php │ ├── Core.php │ ├── Credit.php │ ├── Decode │ │ └── HTML │ │ │ └── Entities.php │ ├── Enclosure.php │ ├── Exception.php │ ├── File.php │ ├── HTTP │ │ └── Parser.php │ ├── IRI.php │ ├── Item.php │ ├── Locator.php │ ├── Misc.php │ ├── Net │ │ └── IPv6.php │ ├── Parse │ │ └── Date.php │ ├── Parser.php │ ├── Rating.php │ ├── Registry.php │ ├── Restriction.php │ ├── Sanitize.php │ ├── Source.php │ ├── XML │ │ └── Declaration │ │ │ └── Parser.php │ └── gzdecode.php └── urljoin.php ├── login.php ├── logout.php ├── microsummary.php ├── opml.php ├── plugins ├── adv_autotag.ini ├── adv_autotag.php ├── autotag.ini ├── autotag.php ├── fix_svg_dimensions.ini ├── fix_svg_dimensions.php ├── images_by_proxy.ini ├── images_by_proxy.php ├── item_targets.ini ├── item_targets.php ├── mediaplayer.swf ├── mini_podcast.png ├── permalink.ini ├── permalink.php ├── place_audio.png ├── place_video.png ├── plain.ini ├── plain.php ├── reddit.ini ├── reddit.php ├── remove_media_autoplay.ini ├── remove_media_autoplay.php ├── share-off.gif ├── share-on.gif ├── sharing.ini ├── sharing.php ├── text_content.ini ├── text_content.php ├── twitter.ini ├── twitter.php ├── vimeo.ini ├── vimeo.php ├── wordpress.ini ├── wordpress.php ├── wordpress.png ├── youtube.ini └── youtube.php ├── prefs.php ├── set-prefs.php ├── shared.php ├── sidebar.php ├── uninstall.php ├── update-quiet.php ├── update-single.php ├── update.php ├── view-action.php └── websub.php /.gitignore: -------------------------------------------------------------------------------- 1 | cache/* 2 | fof-config.php 3 | fof-custom.css 4 | fof.log 5 | fof-install.log 6 | *.db 7 | *~ 8 | cookies.txt 9 | .idea 10 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # About 2 | 3 | FeedOnFeeds is a lightweight server-based **RSS aggregator** and reader, 4 | allowing you to keep up with syndicated content (blogs, comics, and so 5 | forth) without having to keep track of what you've read. Being 6 | server-based means all of your feeds and history are kept in one 7 | place, and being lightweight means you can install it pretty much 8 | anywhere without needing a fancy dedicated server or the like. 9 | 10 | FeedOnFeeds was originally written by Steve Minutillo, 11 | this fork strives to stay up-to-date with changes to PHP while improving 12 | security, usability, multiuser support, and overall design. 13 | 14 | For a complete list of contributors see the end of this file. 15 | 16 | ## Features 17 | 18 | * subscribe to RSS feeds, reddits or YouTube channels 19 | * automatic updating for new entries 20 | * tagging of items or feeds 21 | * blacklist for unwanted item content 22 | * automatic item purging based on similarity (e.g. for getting rid of reposts) 23 | * different sidebar layouts 24 | * multi user capable 25 | * public compilations to share tagged content with others 26 | * search stored feed entries 27 | * Support for [WebSub](https://en.wikipedia.org/wiki/WebSub) push notifications 28 | 29 | ### YouTube integration 30 | 31 | You can also use FeedOnFeeds for keeping track of your favourite YouTube 32 | channels without having to register with Google. Just activate the integrated 33 | **YouTube plugin** and add subscriptions to the channels you want to follow. 34 | 35 | ### Reddit integration 36 | 37 | While reddit doesn't advertise its RSS feeds you can still subscribe to reddits 38 | or users. An integrated **Reddit plugin** also tries to optimise the tiny 39 | thumbnail images served by the feed by replacing them with their original 40 | images if possible. 41 | 42 | # Installation 43 | 44 | ## Requirements 45 | 46 | FeedOnFeeds requires: 47 | 48 | * A web server running *PHP* 49 | * Access to a PDO-capable database (*MySQL* and *SQLite* are currently supported, 50 | and more are easy to add) 51 | * Specific features may require specific PHP extensions; it is highly 52 | recommended (but not required) that you have *Xlib*, *cURL*, and *iconv*. 53 | 54 | To install, simply download a snapshot or clone from your favorite git 55 | repository. Then copy `fof-config.php.dist` to `fof-config.php` and edit 56 | it as appropriate for your setup. If you're on shared hosting, be sure 57 | to point `FOF_DATA_PATH` to somewhere in your home directory. 58 | 59 | After that, point a web browser to `install.php`. For example, if you've 60 | installed FeedOnFeeds at `http://example.com/fof`, go to 61 | `http://example.com/fof/install.php` and then everything should be set 62 | up automatically. 63 | 64 | ### Which database backend should I use? 65 | 66 | Most of the time, SQLite is what you want. You should only consider MySQL if 67 | you're going to support many concurrent users (i.e. 10 or more people using the 68 | app simultaneously), as SQLite has much lower administrative overhead and makes 69 | it easier to port your data to a new webhost. This is the case even if you 70 | already have a working MySQL installation, and using SQLite will not interfere 71 | with your existing MySQL in any way. 72 | 73 | ## Upgrading 74 | 75 | Upgrading to a newer FeedOnFeeds usually just involves downloading a 76 | new snapshot or issuing a `git pull`, and then pointing a browser at `install.php` 77 | again. 78 | 79 | ## Setting up automatic updates 80 | 81 | FeedOnFeeds works best if you have it set to automatically update your feeds 82 | on-the-fly. The best way to do this is to set up a cron job like 83 | so: 84 | 85 | * * * * * curl http://example.com/fof/update-quiet.php 86 | 87 | Don't worry about updates occurring too frequently - FeedOnFeeds will only 88 | update feeds which are due for an update. By default it will 89 | update every feed at most once an hour, but if you enable dynamic 90 | update intervals in the admin preferences, it will adjust the polling 91 | update for feeds based on their historical update frequencies. 92 | 93 | If you're on Dreamhost, you can set up your cron job from the "goodies" section 94 | of the panel, and create a new cron job with the following: 95 | 96 | * Command to run: `curl http://path/to/fof/update-quiet.php` (setting the URL 97 | appropriately) 98 | * When to run: Custom 99 | * Minutes: Every 10 minutes 100 | * Hours: Every Hour 101 | * Day of month: Every Day 102 | * Month: Every Month 103 | * Day of Week: Every Day of Week 104 | 105 | # FAQ/troubleshooting 106 | 107 | ## I'm having an error message during installation 108 | 109 | ### couldn't open logfile /path/to/fof-data/fof-install.log 110 | 111 | You need to set the value of `FOF_DATA_PATH` in `fof-config.php`. Also the file 112 | has to be writable for the web server process. You can set it to `chmod 0777`, or 113 | if you have root access you can do something like `chown -R www-data /path/to/fof-data/` 114 | with `www-data` being the user under which the web server process is running. Depending on 115 | your configuration it may also be `httpd` or something else entirely. Check your server 116 | config for the correct user name. 117 | 118 | ### Syntax error or access violation after "Cannot upgrade table: [CREATE TRIGGER ... 119 | 120 | This occurs if you're on MySQL shared hosting and you don't have access to 121 | trigger creation, which is fairly common. Find the following in `fof-config.php`: 122 | 123 | // define('SQL_NO_TRIGGERS', 1); 124 | 125 | and remove the `//` at the beginning of the line. 126 | 127 | ## How do I delete a feed? 128 | 129 | In the feeds list there's a little menu symbol (≡). Hover 130 | over it and enjoy the context menu. If you've set the UI theme to "simple," just 131 | click on the `d` in the feed list. 132 | 133 | **Note:** By unsubscribing from a feed you also delete all its tagged and starred 134 | items. 135 | 136 | ## WebSub isn't working 137 | 138 | Are you running it on a private network? WebSub can only work if your instance is visible to the Internet at large, and needs to be reachable using the hostname that you're connecting via. 139 | 140 | If you're using the nginx webserver, you need to make sure that URL paths are passed correctly; for example, this should be how you enable PHP script support in your nginx configuration file: 141 | 142 | ```nginx 143 | location ~ \.php(/|$) { 144 | include snippets/fastcgi-php.conf; 145 | 146 | # With php-fpm (or other unix sockets): 147 | fastcgi_pass unix:/var/run/php/php-fpm.sock; 148 | } 149 | ``` 150 | 151 | as opposed to the common, but incorrect, location of `\.php$`. 152 | 153 | # Legal 154 | 155 | FeedOnFeeds is distributed under the GPL. 156 | 157 | ## Contributors 158 | 159 | *ordered by date of first contribution:* 160 | 161 | * [Steve Minutillo](http://feedonfeeds.com/ "original FeedOnFeeds homepage") 162 | * [Alexander Schulze](https://github.com/RomanSixty) 163 | * mdt (discontinued github account) 164 | * [fluffy](https://github.com/fluffy-critter) 165 | * [Justin Wind](https://github.com/thylacine) 166 | * [Ian Tearle](https://github.com/iantearle) 167 | * [_darch](https://github.com/sorenpeter) 168 | -------------------------------------------------------------------------------- /add-single.php: -------------------------------------------------------------------------------- 1 | 22 | -------------------------------------------------------------------------------- /add-tag.php: -------------------------------------------------------------------------------- 1 | ... 33 | // https://www.youtube.com/channel/... 34 | // 35 | // what we need however is the channel's id, so let's try to find that out 36 | 37 | $channel_id = null; 38 | $matches = array(); 39 | 40 | if (preg_match('~youtube\.com/channel/([^/]+)~', $youtube, $matches)) { 41 | $channel_id = $matches[1]; 42 | } elseif (preg_match('~youtube\.com/c/([^/]+)~', $youtube, $matches) 43 | || preg_match('~youtube\.com/user/([^/]+)~', $youtube, $matches) 44 | || preg_match('~youtube\.com/@([^/]+)~', $youtube, $matches)) { 45 | $file = file_get_contents($youtube); 46 | 47 | if (preg_match('~"externalId":"([^"]+)"~m', $file, $matches)) { 48 | $channel_id = $matches[1]; 49 | } 50 | } 51 | 52 | if (!empty($channel_id)) { 53 | $feeds[] = 'https://www.youtube.com/feeds/videos.xml?channel_id=' . $channel_id; 54 | } 55 | } 56 | 57 | if ($url) { 58 | // maybe we have a reddit url? try to guess the corresponding rss feed 59 | $url = preg_replace('~^(https://www\.reddit\.com/(?:u|r)/[^/]+).*$~i', '$1.rss', $url); 60 | 61 | $feeds[] = $url; 62 | } 63 | 64 | if ($opml) { 65 | $sfile = new SimplePie_File($opml); 66 | if (!$sfile->success) { 67 | echo "Cannot open " . htmlentities($opml) . "
"; 68 | return false; 69 | } 70 | 71 | $content = $sfile->body; 72 | $feeds = fof_opml_to_array($content); 73 | } 74 | 75 | if (!empty($_FILES['opml_file']) && !empty($_FILES['opml_file']['tmp_name'])) { 76 | if (($content = file_get_contents($_FILES['opml_file']['tmp_name'])) === false) { 77 | echo "Cannot open uploaded file '" . htmlentities($_FILES['opml_file']['name']) . "'
"; 78 | } else { 79 | $feeds = fof_opml_to_array($content); 80 | } 81 | } 82 | 83 | $add_feed_url = (isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] == 'on') ? 'https' : 'http'; 84 | $add_feed_url .= "://" . $_SERVER["HTTP_HOST"] . $_SERVER["SCRIPT_NAME"]; 85 | ?> 86 | 87 |
88 | 89 | You can use the FoF subscribe bookmarklet to subscribe to any page with a feed. Just add it as a bookmark and then click on it when you are at a page you'd like to subscribe to! 90 | 91 |
92 | 93 |
94 |
95 | 96 | 97 | 98 |
99 |
100 | 101 |
102 | 103 | When adding feeds, mark items as unread

117 | 118 | RSS or reddit or weblog URL:

119 | 120 | YouTube channel page:

121 | 122 | OPML URL: 123 | 124 |

125 | 126 | 127 | OPML filename: 128 | 129 |
130 |
131 | 132 | urlencode($feed), 'idx' => $idx)); 138 | echo '
' 139 | . $feed . ' is waiting to add...' 140 | . "
\n"; 141 | $idx++; 142 | } 143 | 144 | ?> 145 | 176 | 177 |
'; 180 | 181 | include 'footer.php'; 182 | ?> 183 | -------------------------------------------------------------------------------- /autoloader.php: -------------------------------------------------------------------------------- 1 | path = dirname(__FILE__) . DIRECTORY_SEPARATOR . 'library'; 64 | } 65 | 66 | /** 67 | * Autoloader 68 | * 69 | * @param string $class The name of the class to attempt to load. 70 | */ 71 | public function autoload($class) { 72 | // Only load the class if it starts with "SimplePie" 73 | if (strpos($class, 'SimplePie') !== 0) { 74 | return; 75 | } 76 | 77 | $filename = $this->path . DIRECTORY_SEPARATOR . str_replace('_', DIRECTORY_SEPARATOR, $class) . '.php'; 78 | include $filename; 79 | } 80 | } -------------------------------------------------------------------------------- /classes/fof-prefs.php: -------------------------------------------------------------------------------- 1 | user_id = $user_id; 24 | 25 | $prefs = fof_db_prefs_get($user_id); 26 | if ( ! is_array($prefs)) 27 | $prefs = array(); 28 | $this->prefs = $prefs; 29 | 30 | if ($user_id != 1) 31 | { 32 | $admin_prefs = fof_db_prefs_get(1); 33 | if ( ! is_array($admin_prefs)) 34 | $admin_prefs = array(); 35 | $this->admin_prefs = $admin_prefs; 36 | } 37 | else 38 | { 39 | $this->admin_prefs = $prefs; 40 | } 41 | 42 | $this->populate_defaults(); 43 | 44 | if($user_id == 1) 45 | { 46 | $this->prefs = array_merge($this->prefs, $this->admin_prefs); 47 | } 48 | } 49 | 50 | public static function &instance() 51 | { 52 | static $instance; 53 | if(!isset($instance)) $instance = new FoF_Prefs(fof_current_user()); 54 | 55 | return $instance; 56 | } 57 | 58 | function populate_defaults() 59 | { 60 | $defaults = array( 61 | "favicons" => true, 62 | "keyboard" => false, 63 | "direction" => "desc", 64 | "howmany" => 50, 65 | "sharing" => "no", 66 | "feed_order" => "display_title", 67 | "feed_direction" => "asc", 68 | ); 69 | 70 | $admin_defaults = array( 71 | "purge" => 30, 72 | "purge_grace" => 0, 73 | "autotimeout" => 30, 74 | "manualtimeout" => 15, 75 | "logging" => false, 76 | "match_similarity" => "", 77 | "dynupdates" => false, 78 | ); 79 | 80 | $this->stuff_array($this->prefs, $defaults); 81 | $this->stuff_array($this->admin_prefs, $admin_defaults); 82 | } 83 | 84 | function stuff_array(&$array, $defaults) 85 | { 86 | foreach($defaults as $k => $v) 87 | { 88 | if(!isset($array[$k])) $array[$k] = $v; 89 | } 90 | } 91 | 92 | function get($k) 93 | { 94 | if (isset($this->prefs[$k])) 95 | return $this->prefs[$k]; 96 | return null; 97 | } 98 | 99 | function set($k, $v) 100 | { 101 | $this->prefs[$k] = $v; 102 | } 103 | 104 | function save() 105 | { 106 | fof_db_save_prefs($this->user_id, $this->prefs); 107 | } 108 | } 109 | 110 | ?> 111 | -------------------------------------------------------------------------------- /classes/pdolog.php: -------------------------------------------------------------------------------- 1 | setAttribute(PDO::ATTR_STATEMENT_CLASS, array('PDOStatementLog', array($this))); 18 | } 19 | 20 | public function query($query, $fetchMode = null, ...$fetchModeArgs) { 21 | if (empty(self::$logfn)) 22 | return parent::query($query); 23 | 24 | $time = 0.0 - microtime(true); 25 | $result = parent::query($query); 26 | $time += microtime(true); 27 | call_user_func(self::$logfn, $query, $time, ($result !== false)); 28 | return $result; 29 | } 30 | 31 | public function exec($statement) { 32 | if (empty(self::$logfn)) 33 | return parent::exec($statement); 34 | 35 | $time = 0.0 - microtime(true); 36 | $rows_affected = parent::exec($statement); 37 | $time += microtime(true); 38 | call_user_func(self::$logfn, $statement, $time, ($rows_affected !== false), $rows_affected); 39 | return $rows_affected; 40 | } 41 | } 42 | 43 | class PDOStatementLog extends PDOStatement { 44 | protected $dbh; 45 | protected $parameters; 46 | protected function __construct($dbh) { 47 | $this->dbh = $dbh; 48 | $this->parameters = array(); 49 | } 50 | 51 | public function bindParam($parameter, &$variable, $data_type=PDO::PARAM_STR, $length=null, $driver_options=null) { 52 | $this->parameters[$parameter] = $variable; 53 | parent::bindParam($parameter, $variable, $data_type, $length, $driver_options); 54 | } 55 | 56 | public function bindValue($parameter, $value, $data_type=PDO::PARAM_STR) { 57 | $this->parameters[$parameter] = $value; 58 | parent::bindValue($parameter, $value, $data_type); 59 | } 60 | 61 | public function execute($input_parameters=null) { 62 | if (empty(PDOLog::$logfn)) 63 | return parent::execute($input_parameters); 64 | 65 | $time = 0.0 - microtime(true); 66 | $result = parent::execute($input_parameters); 67 | $time += microtime(true); 68 | 69 | call_user_func(PDOLog::$logfn, $this->queryString, $time, $result, $this->rowCount(), is_null($input_parameters) ? $this->parameters : $input_parameters); 70 | return $result; 71 | } 72 | } 73 | ?> 74 | -------------------------------------------------------------------------------- /classes/svghistogram.php: -------------------------------------------------------------------------------- 1 | under GPL 5 | 6 | Derived from code copyright (C) 2002 J. David Eisenberg under GPL 7 | */ 8 | 9 | define('SVGHistogramPHI', (1 + sqrt(5)) / 2); 10 | class SVGHistogram { 11 | const PHI = SVGHistogramPHI; 12 | 13 | /** Class defaults. 14 | */ 15 | var $options = array( 16 | 'frame_color' => null, 17 | 'min_items' => '100', 18 | 'max_items' => null, 19 | 'spill_caption' => 'Not shown: %2$d items over %1$d more days', 20 | 'title' => '', 21 | 'description' => '', 22 | 'bar_width' => 20, 23 | 'bar_height' => 100, 24 | 'bar_caption' => '%1$d days ago, %2$d items', 25 | 'tick_every' => 7, 26 | 'shade_over' => null, 27 | 'shade_color' => 'red', 28 | 'label_zero' => null, 29 | 'label_x' => null, 30 | 'label_height' => 8 31 | ); 32 | 33 | /** Instance settings are derived from class defaults. 34 | */ 35 | function __construct(array $options=array()) { 36 | $this->options = array_merge($this->options, $options); 37 | } 38 | 39 | /** Convert an item's value as a percent of the maximum value into a color 40 | suitable for use in a style. 41 | $percent should be a float in the range [0,1] 42 | */ 43 | static protected function value_to_color($percent) { 44 | return 'rgb(' . implode(', ', array_map(function($n) { 45 | return round($n * 100) . '%'; 46 | }, array_fill(0, 3, 0.95 * (1.0 - $percent)))) . ')'; 47 | } 48 | 49 | /** Beginning of SVG output. 50 | */ 51 | protected function svg_header($options, $width, $height) { 52 | $header = ' 61 | '; 62 | if ( ! is_null($options['title'])) 63 | $header .= ' ' . htmlentities($options['title'], ENT_NOQUOTES|ENT_XML1) . '' . "\n"; 64 | 65 | if ( ! is_null($options['description'])) 66 | $header .= ' ' . htmlentities($options['description'], ENT_NOQUOTES|ENT_XML1) . '' . "\n"; 67 | 68 | return $header; 69 | } 70 | 71 | /** End of SVG output. 72 | */ 73 | protected function svg_footer($options, $width, $height) { 74 | $footer = '' . "\n"; 75 | 76 | return $footer; 77 | } 78 | 79 | /** Draw a frame around the graph. 80 | */ 81 | protected function svg_frame($options, $width, $height) { 82 | if (empty($options['frame_color'])) 83 | return ''; 84 | 85 | $frame .= ' ' . "\n"; 86 | $frame .= ' ' . "\n"; 87 | $frame .= ' ' . "\n"; 88 | 89 | return $frame; 90 | } 91 | 92 | /** Draw ticks on the graph. 93 | */ 94 | protected function svg_ticks($options, $width, $height) { 95 | if (empty($options['tick_every'])) 96 | return ''; 97 | 98 | $tick_spacing = $options['tick_every'] * $options['bar_width']; 99 | $tick_paths = array(); 100 | for ($i = $tick_spacing; $i < $width; $i += $tick_spacing) { 101 | $tick_paths[] = sprintf('M %d %d v %d', $i, ($options['bar_height'] - 2), 4); 102 | } 103 | $ticks = ' ' . "\n"; 104 | $ticks .= ' ' . "\n"; 105 | $ticks .= ' ' . "\n"; 106 | 107 | return $ticks; 108 | } 109 | 110 | /** Draw a shaded region over items greater than n-th. 111 | */ 112 | protected function svg_shade($options, $width, $height) { 113 | if (empty($options['shade_over'])) 114 | return ''; 115 | 116 | $shade_tick = $options['shade_over'] * $options['bar_width']; 117 | if ($shade_tick >= $width) 118 | return ''; 119 | 120 | $shade = ' ' . "\n"; 121 | $shade_path = sprintf('M %d %d v %d', $shade_tick, 0, $options['bar_height']); 122 | $shade .= ' ' . "\n"; 123 | $shade .= ' ' . "\n"; 124 | $shade .= ' ' . "\n"; 125 | 126 | return $shade; 127 | } 128 | 129 | /** Draw a bar representing an item's value on the graph. 130 | This renders the value as full bar, shaded gray in relation to the value. 131 | An alternative implementation might render a bar of varying height. 132 | $ordinality provides the offset 133 | $value should be between 0 and $max 134 | $max should be the uppermost value of the scale 135 | */ 136 | protected function svg_bar($options, $ordinality, $value, $max) { 137 | $bar = ''; 144 | if ( ! empty($options['bar_caption'])) { 145 | $title = sprintf($options['bar_caption'], $ordinality, $value); 146 | $bar .= '' . htmlentities($title, ENT_NOQUOTES|ENT_XML1) . ''; 147 | } 148 | $bar .= ''; 149 | 150 | return $bar; 151 | } 152 | 153 | /** Draw an indicator of more-but-unrendered items. 154 | */ 155 | protected function svg_spillage($options, $count, $sum, $max) { 156 | if (empty($count) 157 | || empty($options['max_items'])) 158 | return ''; 159 | 160 | $points = array(); 161 | $x = $options['max_items'] * $options['bar_width']; 162 | $points[] = $x . ',0'; 163 | $points[] = $x . ',' . $options['bar_height']; 164 | $points[] = ($x + ceil($options['bar_width'] * self::PHI)) . ',' . round($options['bar_height'] / 2); 165 | $spill = ''; 170 | if ( ! empty($options['spill_caption'])) { 171 | $title = sprintf($options['spill_caption'], $count, $sum); 172 | $spill .= '' . htmlentities($title, ENT_NOQUOTES|ENT_XML1) . ''; 173 | } 174 | $spill .= '' . "\n"; 175 | 176 | return $spill; 177 | } 178 | 179 | 180 | protected function svg_label_zero($options, $width, $height) { 181 | if (empty($options['label_zero']) 182 | || empty($options['label_height'])) 183 | return ''; 184 | 185 | $label = ' '; 186 | $label .= $options['label_zero']; 187 | $label .= '' . "\n"; 188 | 189 | return $label; 190 | } 191 | 192 | protected function svg_label_x($options, $width, $height) { 193 | if (empty($options['label_x']) 194 | || empty($options['label_height'])) 195 | return ''; 196 | 197 | $label = ' '; 198 | $label .= $options['label_x']; 199 | $label .= '' . "\n"; 200 | 201 | return $label; 202 | } 203 | 204 | 205 | /** Generate a graph from $values. 206 | */ 207 | function render($values, $options=array()) { 208 | $options = array_merge($this->options, $options); 209 | $count = count($values); 210 | $no_labels = (empty($options['label_zero']) && empty($options['label_x'])) || empty($options['label_height']); 211 | $height = $options['bar_height']; 212 | $height += $no_labels ? 2 : ($options['label_height'] + 6); 213 | $width = ($count < $options['min_items'] ? $options['min_items'] : $count) * $options['bar_width']; 214 | if ( ! empty($options['max_items'])) { 215 | $max_width = $options['max_items'] * $options['bar_width']; 216 | $width = $max_width + ceil($options['bar_width'] * self::PHI); 217 | } 218 | 219 | $graph = array(); 220 | 221 | $graph[] = $this->svg_header($options, $width, $height); 222 | 223 | $spilt_count = 0; 224 | $spilt_sum = 0; 225 | $max = $count ? max($values) : 0; 226 | foreach ($values as $ord => $val) { 227 | if ( ! empty($options['max_items']) 228 | && $ord >= $options['max_items']) { 229 | $spilt_count ++; 230 | $spilt_sum += $val; 231 | } else { 232 | $graph[] = ' ' . $this->svg_bar($options, $ord, $val, $max) . "\n"; 233 | } 234 | } 235 | $graph[] = $this->svg_spillage($options, $spilt_count, $spilt_sum, $max); 236 | $graph[] = $this->svg_frame($options, $width, $height); 237 | $graph[] = $this->svg_ticks($options, $width, $height); 238 | $graph[] = $this->svg_shade($options, $width, $height); 239 | if ( ! $no_labels) { 240 | $graph[] = ' ' . "\n"; 241 | $graph[] = $this->svg_label_zero($options, $width, $height); 242 | $graph[] = $this->svg_label_x($options, $width, $height); 243 | $graph[] = ' ' . "\n"; 244 | } 245 | $graph[] = $this->svg_footer($options, $width, $height); 246 | 247 | echo implode($graph); 248 | } 249 | } 250 | ?> 251 | -------------------------------------------------------------------------------- /data/cookies.txt.dist: -------------------------------------------------------------------------------- 1 | # Netscape HTTP Cookie File 2 | # https://curl.haxx.se/docs/http-cookies.html 3 | # This file was generated by libcurl! Edit at your own risk. 4 | 5 | -------------------------------------------------------------------------------- /delete.php: -------------------------------------------------------------------------------- 1 | 23 | 24 | Deleted. 25 | 26 | 27 | -------------------------------------------------------------------------------- /favicon.php: -------------------------------------------------------------------------------- 1 | 27 | -------------------------------------------------------------------------------- /feed-action.php: -------------------------------------------------------------------------------- 1 | 8 | * 9 | * Distributed under the GPL - see LICENSE 10 | * 11 | */ 12 | 13 | include_once 'fof-main.php'; 14 | 15 | /* mark all items in feed as read, return updated sidebar entry */ 16 | if (!empty($_POST['read_feed'])) { 17 | fof_db_mark_feed_read(fof_current_user(), $_POST['read_feed']); 18 | $feed_row = fof_get_feed(fof_current_user(), $_POST['read_feed']); 19 | echo fof_render_feed_row($feed_row); 20 | 21 | exit(); 22 | } 23 | 24 | /* update one feed, return replacement sidebar feed list content */ 25 | if (!empty($_POST['update_feedid'])) { 26 | list($count, $error) = fof_update_feed($_POST['update_feedid']); 27 | $feed_row = fof_get_feed(fof_current_user(), $_POST['update_feedid']); 28 | if (!empty($error)) { 29 | echo fof_render_feed_row($feed_row, $error); 30 | } else { 31 | echo fof_render_feed_row($feed_row); 32 | } 33 | exit(); 34 | } 35 | 36 | /* returns a script block which updates a list of the subscribed sources for a tag */ 37 | if (!empty($_POST['update_tag_sources'])) { 38 | fof_set_content_type('application/javascript'); 39 | 40 | $tag_id = fof_db_get_tag_by_name($_POST['update_tag_sources']); 41 | $subs = fof_db_subscriptions_by_tags(fof_current_user()); 42 | 43 | /* FIXME: check timeouts, like below */ 44 | 45 | if (!empty($subs[$tag_id])) { 46 | echo 'pendingUpdates(' . json_encode($subs[$tag_id]) . ');'; 47 | } 48 | 49 | exit(); 50 | } 51 | 52 | /* returns a script block which updates all updatable subscribed feeds */ 53 | if (!empty($_POST['update_subscribed_sources'])) { 54 | fof_set_content_type('application/javascript'); 55 | 56 | $now = time(); 57 | $timeout = $fof_prefs_obj->admin_prefs['manualtimeout'] * 60; 58 | 59 | $sources = array(); 60 | $statement = fof_db_get_subscriptions(fof_current_user(), true); 61 | while (($feed = fof_db_get_row($statement)) !== false) { 62 | if (($now - $feed['feed_cache_date']) < $timeout) { 63 | continue; 64 | } 65 | 66 | if ($now < $feed['feed_cache_next_attempt']) { 67 | continue; 68 | } 69 | 70 | $sources[] = $feed['feed_id']; 71 | } 72 | 73 | if (!empty($sources)) { 74 | echo 'pendingUpdates(' . json_encode($sources) . ');'; 75 | } 76 | 77 | exit(); 78 | } 79 | 80 | /* returns a list of tags for a feed, with markup */ 81 | if (!empty($_POST['subscription_tag_list'])) { 82 | fof_set_content_type(); 83 | $feed_id = $_POST['subscription_tag_list']; 84 | $feed_row = fof_get_feed(fof_current_user(), $feed_id); 85 | if (empty($feed_row)) { 86 | header('Status: 404 Not Found'); 87 | echo 'No data for feed.'; 88 | exit(); 89 | } 90 | echo "\n"; 91 | foreach ($feed_row['tags'] as $tag) { 92 | echo '
  • ' 93 | . $tag 94 | . ' ' 95 | . '[x]' 96 | . '' 97 | . '
  • ' 98 | . "\n"; 99 | } 100 | echo " "; 101 | 102 | exit(); 103 | } 104 | 105 | /* modify a user's feed subscription */ 106 | if (!empty($_POST['subscription_tag'])) { 107 | if (empty($_POST['feed'])) { 108 | header('Status: 400 Bad Request'); 109 | echo 'Incomplete data.'; 110 | exit(); 111 | } 112 | $tag = $_POST['subscription_tag']; 113 | $feed_id = $_POST['feed']; 114 | 115 | if (!empty($_POST['delete'])) { 116 | fof_untag_feed(fof_current_user(), $feed_id, $tag); 117 | } else { 118 | fof_tag_feed(fof_current_user(), $feed_id, $tag); 119 | } 120 | 121 | exit(); 122 | } 123 | 124 | /** Serve out an SVG image showing a feed's activity history. 125 | */ 126 | if (!empty($_GET['feed_history'])) { 127 | include_once 'classes/svghistogram.php'; 128 | 129 | fof_set_content_type('image/svg+xml'); 130 | 131 | $history = fof_db_feed_history($_GET['feed_history']); 132 | 133 | $options = array( 134 | 'title' => 'Feed History', 135 | 'description' => 'Items added to feed.', 136 | 'min_items' => max(31, $fof_prefs_obj->admin_prefs['purge'] + 7), 137 | 'max_items' => 366, 138 | 'label_zero' => '↑ today', 139 | 'label_x' => 'days ago →', 140 | ); 141 | if (!empty($fof_prefs_obj->admin_prefs['purge'])) { 142 | $options['shade_over'] = $fof_prefs_obj->admin_prefs['purge']; 143 | $options['max_items'] = min($options['max_items'], ceil($fof_prefs_obj->admin_prefs['purge'] * SVGHistogram::PHI)); 144 | } 145 | 146 | $graph = new SVGHistogram($options); 147 | 148 | $graph->render($history); 149 | 150 | exit(); 151 | } 152 | 153 | header('Status: 400 Bad Request'); 154 | echo 'Unknown request.'; 155 | ?> 156 | -------------------------------------------------------------------------------- /fof-asset-custom.php.dist: -------------------------------------------------------------------------------- 1 | 'images/tag-icon.png' 5 | )); 6 | ?> -------------------------------------------------------------------------------- /fof-asset.php: -------------------------------------------------------------------------------- 1 | 8 | * 9 | * Distributed under the GPL - see LICENSE 10 | * 11 | */ 12 | 13 | global $fof_asset; 14 | $fof_asset = array( 15 | 'tag_icon' => 'image/tag-icon.svg', 16 | 'feed_icon' => 'image/feed-icon.png', 17 | 'throbber_image' => 'image/throbber.gif', 18 | 'star_on_image' => 'image/star-on.gif', 19 | 'star_off_image' => 'image/star-off.gif', 20 | 'star_pend_image' => 'image/star-pending.gif', 21 | 'busy_icon' => 'image/spinner.gif', 22 | 'alert_icon' => 'image/warn.gif', 23 | ); 24 | 25 | @include_once 'fof-asset-custom.php'; 26 | -------------------------------------------------------------------------------- /fof-config.php.dist: -------------------------------------------------------------------------------- 1 | id mappings automatically 45 | 46 | // maximum number of seconds background update script can run, 0 for indefinitely 47 | define('FOF_UPDATE_TIME_LIMIT', 60 * 10); 48 | 49 | // The rest you should not need to change 50 | 51 | // DB table names 52 | //define('FOF_DB_PREFIX', "fof_"); 53 | 54 | // Find ourselves and the cache dir 55 | 56 | if (!defined('FOF_DIR')) { 57 | define('FOF_DIR', dirname(__FILE__)); 58 | } 59 | 60 | ?> 61 | -------------------------------------------------------------------------------- /fof-custom.css.dist: -------------------------------------------------------------------------------- 1 | .item .body { -moz-column-width: 400px; } 2 | 3 | .item .body img { 4 | max-width: 100%; 5 | height: auto; 6 | cursor: zoom-in; 7 | } 8 | 9 | .item .body img:hover { 10 | max-width: 1000% !important; 11 | position: relative; 12 | z-index: 5; 13 | outline: 10px solid #000; 14 | } 15 | -------------------------------------------------------------------------------- /fof-render.php: -------------------------------------------------------------------------------- 1 | ]*?>)([^<]*))|||((([^<]*))/si'; 25 | preg_match_all($pat, $full_body, $tag_matches); 26 | 27 | if (empty($tag_matches[0])) { 28 | $full_body_hl = preg_replace('/(.*?)(' . preg_quote($q, '/') . ')(.*?)/iu', "\$1\$2\$3", $full_body); 29 | } 30 | /* loop through and highlight $q value in data and recombine with tags */ 31 | else for ($i = 0; $i < count($tag_matches[0]); $i++) { 32 | /* ignore all text within these tags */ 33 | if ( 34 | (preg_match('/\$2\$3", ' ' . $tag_matches[3][$i] . ' '); 45 | $full_body_hl .= substr($holder, 1, (strlen($holder) - 2)); 46 | } 47 | } 48 | /* return tagged text */ 49 | return $full_body_hl; 50 | } 51 | 52 | /* quell warnings */ 53 | function fof_render_get_key_($array, $key, $default = NULL) { 54 | return (empty($array[$key]) ? $default : $array[$key]); 55 | } 56 | 57 | function fof_render_item($item, $include_div = true) { 58 | global $fof_asset, 59 | $fof_render_filters, 60 | $fof_domitem_filters; 61 | 62 | $feed_link = fof_render_get_key_($item, 'feed_link'); 63 | if ($feed_link == "[no link]") { 64 | $feed_link = $item['feed_url']; 65 | } 66 | 67 | $feed_title = fof_render_get_key_($item, 'display_title'); 68 | if ($feed_title == "[no title]") { 69 | $feed_title = $feed_link; 70 | } 71 | 72 | $feed_image = fof_render_get_key_($item, 'display_image', $fof_asset['feed_icon']); 73 | $feed_description = fof_render_get_key_($item, 'feed_description'); 74 | 75 | $item_link = fof_render_get_key_($item, 'item_link'); 76 | $item_id = fof_render_get_key_($item, 'item_id'); 77 | $item_title = fof_render_get_key_($item, 'item_title', '[no title]'); 78 | $item_author = fof_render_get_key_($item, 'item_author', ''); 79 | $item_content = fof_render_get_key_($item, 'item_content'); 80 | $item_read = fof_render_get_key_($item, 'item_read'); 81 | 82 | foreach ($fof_render_filters as $filter) { 83 | $item_content = $filter($item_content, $item); 84 | } 85 | 86 | $dom = fof_content_to_dom($item_content); 87 | 88 | // enable controls, if an item contains a video 89 | foreach ($dom->getElementsByTagName('video') as $video) { 90 | $video->setAttribute('controls', ''); 91 | } 92 | 93 | // also make images clickable and demand-loaded 94 | foreach ($dom->getElementsByTagName('img') as $img) { 95 | $img->setAttribute('tabindex', '0'); 96 | $img->setAttribute('loading', 'lazy'); 97 | } 98 | 99 | // run user-configured DOM filters 100 | foreach ($fof_domitem_filters as $filter) { 101 | $dom = $filter($dom, $item); 102 | } 103 | 104 | $item_content = fof_dom_to_content($dom); 105 | 106 | // Get the local datetime 107 | $prefs = fof_prefs(); 108 | $offset = fof_render_get_key_($prefs, 'tzoffset') * 60 * 60; 109 | 110 | $item_published = gmdate("Y-n-d g:ia", $item['item_published'] + $offset); 111 | $item_cached = gmdate("Y-n-d g:ia", $item['item_cached'] + $offset); 112 | $item_updated = gmdate("Y-n-d g:ia", $item['item_updated'] + $offset); 113 | 114 | if (!empty($_GET['search'])) { 115 | $item_content = do_highlight($item_content, $_GET['search'], "fof-highlight"); 116 | $item_title = do_highlight($item_title, $_GET['search'], "fof-highlight"); 117 | } 118 | 119 | $tags = fof_render_get_key_($item, 'tags', array()); 120 | 121 | $star = in_array("star", $tags) ? true : false; 122 | 123 | $unread = in_array("unread", $tags) ? true : false; 124 | 125 | $folded = in_array('folded', $tags) ? true : false; 126 | if ($include_div) { 127 | echo '
    ' . "\n"; 128 | } 129 | ?> 130 | 131 |
    132 | 133 | fold ↑ 134 | unfold ↓ 135 | mark read 136 | 137 |

    > 141 | 149 | ' . "\n"; 151 | ?> 152 | 160 | $item_title\n"; 162 | if ($item_author) { 163 | echo '' . htmlentities($item_author) . ''; 164 | } 165 | ?> 166 |

    167 | 168 | 169 | - 170 | 171 |

    172 | ' . "\n"; 175 | } 176 | 177 | ?> 178 | 179 |

    180 | 181 | on 182 |
    183 | 184 |
    185 | 186 |
    187 |
    188 | 189 | mark read 190 | add tag 191 | 192 | 193 | 194 | 195 | '; 199 | echo '[x]'; 200 | echo '' . htmlspecialchars($tag) . ''; 201 | echo '' . "\n"; 202 | } 203 | ?> 204 | 205 | ' . $widget . ""; 211 | } 212 | } 213 | ?> 214 |
    215 | \n"; 218 | } 219 | } 220 | ?> 221 | -------------------------------------------------------------------------------- /footer.php: -------------------------------------------------------------------------------- 1 |
    2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /header.php: -------------------------------------------------------------------------------- 1 | 24 | 25 | 26 | 27 | Feed on Feeds<?php if ($unread_count) { 28 | echo " ($unread_count)"; 29 | } 30 | ?> 31 | 32 | 35 | 36 | 39 | 40 | 41 | 42 | 47 | 48 | 61 | 62 | 63 | 64 | 65 | 70 | 71 |
    72 | 73 |
    74 |
    -------------------------------------------------------------------------------- /image/feed-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RomanSixty/Feed-on-Feeds/ea3f132aecbd172aa765631a2ec8ed0a127fdee9/image/feed-icon.png -------------------------------------------------------------------------------- /image/grippy.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RomanSixty/Feed-on-Feeds/ea3f132aecbd172aa765631a2ec8ed0a127fdee9/image/grippy.png -------------------------------------------------------------------------------- /image/spinner.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RomanSixty/Feed-on-Feeds/ea3f132aecbd172aa765631a2ec8ed0a127fdee9/image/spinner.gif -------------------------------------------------------------------------------- /image/tag-icon.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | image/svg+xml 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /image/throbber.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RomanSixty/Feed-on-Feeds/ea3f132aecbd172aa765631a2ec8ed0a127fdee9/image/throbber.gif -------------------------------------------------------------------------------- /image/warn.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RomanSixty/Feed-on-Feeds/ea3f132aecbd172aa765631a2ec8ed0a127fdee9/image/warn.gif -------------------------------------------------------------------------------- /img.php: -------------------------------------------------------------------------------- 1 | = 400) && strpos($header, ':')) { 31 | fof_log("image $url header: $header"); 32 | header(rtrim($header)); 33 | } 34 | return strlen($header); 35 | } 36 | 37 | $curl = curl_init($url); 38 | curl_setopt_array($curl, array( 39 | CURLOPT_REFERER => $item['item_link'], 40 | CURLOPT_HEADERFUNCTION => 'dump_headers', 41 | CURLOPT_FOLLOWLOCATION => true, 42 | CURLOPT_AUTOREFERER => true, 43 | CURLOPT_USERAGENT => $_SERVER['HTTP_USER_AGENT'], 44 | )); 45 | 46 | curl_exec($curl); 47 | fof_log(curl_error($curl)); 48 | curl_close($curl); 49 | ?> -------------------------------------------------------------------------------- /index.php: -------------------------------------------------------------------------------- 1 | 21 | -------------------------------------------------------------------------------- /install.php: -------------------------------------------------------------------------------- 1 | Trouble encountered:
    " . $e->GetMessage() . "
    Trying to continue...
    \n"; 23 | } 24 | 25 | fof_set_content_type(); 26 | 27 | ?> 28 | 29 | 30 | 31 | Feed on Feeds - Installation 32 | 33 | 34 | 35 | 62 | 63 | 64 | 65 | 70 |
    71 |
    72 |

    Feed on Feeds - Installation

    73 |
    74 |
    75 | 76 | 77 | OK! Setup complete! Login as admin, and start subscribing!'; 83 | echo '
    '; 84 | exit(); 85 | } else { 86 | echo "
    Passwords do not match!


    "; 87 | } 88 | } else { 89 | fof_log("install started"); 90 | ?> 91 |

    Checking compatibility...

    92 | =')); 97 | $compat_fatal |= fof_install_compat_notice($php_ok, "PHP", "Your PHP version is too old!", "Feed on Feeds requires at least PHP version " . VERSION_REQUIRED_PHP, 1); 98 | 99 | $compat_fatal |= fof_install_compat_notice(extension_loaded('xml'), "XML", "Your PHP installation is missing the XML extension!", "This is required by Feed on Feeds.", 1); 100 | $compat_fatal |= fof_install_compat_notice(extension_loaded('pcre'), "PCRE", "Your PHP installation is missing the PCRE extension!", "This is required by Feed on Feeds.", 1); 101 | $compat_fatal |= fof_install_compat_notice(extension_loaded('pdo'), "PDO", "Your PHP installation is missing the PDO extension!", "This is required by Feed on Feeds.", 1); 102 | 103 | $mysql_ok = extension_loaded('pdo_mysql'); 104 | $sqlite_ok = extension_loaded('pdo_sqlite'); 105 | $compat_fatal |= fof_install_compat_notice($sqlite_ok, 106 | "SQLite", 107 | "Your PHP installation does not support the SQLite database" . (defined('USE_SQLITE') ? ", but you have configured Feed on Feeds to use this database!" : "."), 108 | defined('USE_SQLITE') ? "PHP will need to support this database, or you must configure a different one." : "This is not required if another database is available.", 109 | defined('USE_SQLITE')); 110 | $compat_fatal |= fof_install_compat_notice($mysql_ok, 111 | "MySQL", 112 | "Your PHP installation does not support the MySQL database" . (defined('USE_MYSQL') ? ", but you have configured Feed on Feeds to use this database!" : "."), 113 | defined('USE_MYSQL') ? "PHP will need to support this database, or you must configure a different one." : "This is not required if another database is available.", 114 | defined('USE_MYSQL')); 115 | $compat_fatal |= fof_install_compat_notice($sqlite_ok || $mysql_ok, "PDO database", "Your PHP installation is missing a supported PDO database extension!", "This is required by Feed on Feeds.", 1); 116 | 117 | $curl_ok = (extension_loaded('curl') && version_compare(get_curl_version(), VERSION_REQUIRED_CURL, '>=')); 118 | $compat_fatal |= fof_install_compat_notice($curl_ok, "cURL", "Your PHP installation is either missing the cURL extension, or it is too old!", "cURL version " . VERSION_REQUIRED_CURL . " or later is required to be able to subscribe to https or digest authenticated feeds."); 119 | 120 | $compat_fatal |= fof_install_compat_notice(extension_loaded('zlib'), "zlib", "Your PHP installation is missing the zlib extension!", "Feed on Feeds will not be able to save bandwidth by requesting compressed feeds."); 121 | $compat_fatal |= fof_install_compat_notice(extension_loaded('iconv'), "iconv", "Your PHP installation is missing the iconv extension!", "The number of international languages that Feed on Feeds can handle will be reduced."); 122 | $compat_fatal |= fof_install_compat_notice(extension_loaded('mbstring'), "mbstring", "Your PHP installation is missing the mbstring extension!", "The number of international languages that Feed on Feeds can handle will be reduced."); 123 | 124 | $compat_fatal |= fof_install_compat_notice(class_exists('finfo'), 'fileinfo', 'Your PHP installation is missing the fileinfo extension!', 'Some feed\'s icons may not be displayed.'); 125 | 126 | if ($compat_fatal) { 127 | echo "
    "; 128 | exit(); 129 | } 130 | 131 | ?> 132 |
    Minimum requirements met! 133 |
    134 | 135 |

    Checking cache directory...

    136 | \n"; 139 | exit(); 140 | } 141 | ?> 142 |
    Data directory ready. 143 |
    144 | 145 |

    Creating tables...

    146 | 149 |
    Tables exist. 150 |
    151 | 152 |

    Updating tables...

    153 | 156 |
    Tables up to date. 157 |
    158 | 159 |

    Inserting initial data...

    160 | 163 |
    Data initialized. 164 |
    165 | 166 |

    Checking cache directory...

    167 | \n"; 170 | exit(); 171 | } 172 | ?> 173 |
    Cache directory ready. 174 |
    175 | 176 | 179 | 180 |

    Checking admin user...

    181 | 184 | 185 | You now need to choose an initial password for the 'admin' account:
    186 | 187 |
    188 | 189 | 190 | 191 |
    Password:
    Password again:
    192 | 193 |
    194 | 195 | 198 | 199 |
    'admin' account already exists. 200 |
    OK! Setup complete! Login as admin, and start subscribing!
    201 | 204 | 205 | 206 | -------------------------------------------------------------------------------- /item.php: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /items.php: -------------------------------------------------------------------------------- 1 | get('order'); 54 | } 55 | } 56 | if (empty($order)) { 57 | $order = 'desc'; 58 | } 59 | 60 | $itemcount = 0; 61 | $statement = fof_db_get_item_count(fof_current_user(), $what, $feed, $search); 62 | while (($row = fof_db_get_row($statement)) !== false) { 63 | // fof_log('feed:' . $row['feed_id'] . ' count:' . $row['count']); 64 | $itemcount += $row['count']; 65 | } 66 | 67 | $title = fof_view_title($feed, $what, $when, $which, $howmany, $search, $itemcount); 68 | 69 | // Placeholder to push content down: 70 | ?> 71 | 146 | 147 | 152 | 153 |
    154 | 155 | get('keyboard')) { 157 | ?> 158 |
    159 | Keyboard Commands 160 |
    161 |
    ?
    Toggle this display.
    162 |
    H
    Toggle folding of all visible items.
    163 |
    h
    Toggle folding of current item.
    164 |
    s
    Toggle starring of current item.
    165 |
    f
    Flag current item.
    166 |
    F
    Flag current and all previous items.
    167 |
    U
    Unflag all items.
    168 |
    j
    Scroll current item, flag if at end of item, move to next item.
    169 |
    J
    Flag current item, move to next.
    170 |
    n
    Skip to next item.
    171 |
    p
    Skip to previous item.
    172 |
    N
    Skip to last item.
    173 |
    P
    Skip to first item.
    174 |
    r
    Refresh sidebar.
    175 |
    Open current item.
    176 |
    177 |
    178 | 179 | 182 | 183 | 184 |
    185 | 186 | 187 | 188 | ' : '') . $links . ($links ? "\n" : ''); 191 | 192 | echo $links; 193 | 194 | $items = fof_get_items(fof_current_user(), $feed, $what, $when, $which, $howmany, $order, $search); 195 | 196 | if (!empty($items)) { 197 | foreach ($items as $item) { 198 | fof_render_item($item, true); 199 | } 200 | } else { 201 | echo "

    No items found.

    \n"; 202 | } 203 | 204 | ?> 205 |
    206 | 207 | 208 | -------------------------------------------------------------------------------- /library/SimplePie/Author.php: -------------------------------------------------------------------------------- 1 | name = $name; 90 | $this->link = $link; 91 | $this->email = $email; 92 | } 93 | 94 | /** 95 | * String-ified version 96 | * 97 | * @return string 98 | */ 99 | public function __toString() 100 | { 101 | // There is no $this->data here 102 | return md5(serialize($this)); 103 | } 104 | 105 | /** 106 | * Author's name 107 | * 108 | * @return string|null 109 | */ 110 | public function get_name() 111 | { 112 | if ($this->name !== null) 113 | { 114 | return $this->name; 115 | } 116 | 117 | return null; 118 | } 119 | 120 | /** 121 | * Author's link 122 | * 123 | * @return string|null 124 | */ 125 | public function get_link() 126 | { 127 | if ($this->link !== null) 128 | { 129 | return $this->link; 130 | } 131 | 132 | return null; 133 | } 134 | 135 | /** 136 | * Author's email address 137 | * 138 | * @return string|null 139 | */ 140 | public function get_email() 141 | { 142 | if ($this->email !== null) 143 | { 144 | return $this->email; 145 | } 146 | 147 | return null; 148 | } 149 | } 150 | -------------------------------------------------------------------------------- /library/SimplePie/Cache.php: -------------------------------------------------------------------------------- 1 | 'SimplePie_Cache_MySQL', 65 | 'memcache' => 'SimplePie_Cache_Memcache', 66 | 'memcached' => 'SimplePie_Cache_Memcached', 67 | 'redis' => 'SimplePie_Cache_Redis' 68 | ); 69 | 70 | /** 71 | * Don't call the constructor. Please. 72 | */ 73 | private function __construct() { } 74 | 75 | /** 76 | * Create a new SimplePie_Cache object 77 | * 78 | * @param string $location URL location (scheme is used to determine handler) 79 | * @param string $filename Unique identifier for cache object 80 | * @param string $extension 'spi' or 'spc' 81 | * @return SimplePie_Cache_Base Type of object depends on scheme of `$location` 82 | */ 83 | public static function get_handler($location, $filename, $extension) 84 | { 85 | $type = explode(':', $location, 2); 86 | $type = $type[0]; 87 | if (!empty(self::$handlers[$type])) 88 | { 89 | $class = self::$handlers[$type]; 90 | return new $class($location, $filename, $extension); 91 | } 92 | 93 | return new SimplePie_Cache_File($location, $filename, $extension); 94 | } 95 | 96 | /** 97 | * Create a new SimplePie_Cache object 98 | * 99 | * @deprecated Use {@see get_handler} instead 100 | */ 101 | public function create($location, $filename, $extension) 102 | { 103 | trigger_error('Cache::create() has been replaced with Cache::get_handler(). Switch to the registry system to use this.', E_USER_DEPRECATED); 104 | return self::get_handler($location, $filename, $extension); 105 | } 106 | 107 | /** 108 | * Register a handler 109 | * 110 | * @param string $type DSN type to register for 111 | * @param string $class Name of handler class. Must implement SimplePie_Cache_Base 112 | */ 113 | public static function register($type, $class) 114 | { 115 | self::$handlers[$type] = $class; 116 | } 117 | 118 | /** 119 | * Parse a URL into an array 120 | * 121 | * @param string $url 122 | * @return array 123 | */ 124 | public static function parse_URL($url) 125 | { 126 | $params = parse_url($url); 127 | $params['extras'] = array(); 128 | if (isset($params['query'])) 129 | { 130 | parse_str($params['query'], $params['extras']); 131 | } 132 | return $params; 133 | } 134 | } 135 | -------------------------------------------------------------------------------- /library/SimplePie/Cache/Base.php: -------------------------------------------------------------------------------- 1 | get_items(); 63 | $items_by_id = array(); 64 | 65 | if (!empty($items)) 66 | { 67 | foreach ($items as $item) 68 | { 69 | $items_by_id[$item->get_id()] = $item; 70 | } 71 | 72 | if (count($items_by_id) !== count($items)) 73 | { 74 | $items_by_id = array(); 75 | foreach ($items as $item) 76 | { 77 | $items_by_id[$item->get_id(true)] = $item; 78 | } 79 | } 80 | 81 | if (isset($data->data['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['feed'][0])) 82 | { 83 | $channel =& $data->data['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['feed'][0]; 84 | } 85 | elseif (isset($data->data['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['feed'][0])) 86 | { 87 | $channel =& $data->data['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['feed'][0]; 88 | } 89 | elseif (isset($data->data['child'][SIMPLEPIE_NAMESPACE_RDF]['RDF'][0])) 90 | { 91 | $channel =& $data->data['child'][SIMPLEPIE_NAMESPACE_RDF]['RDF'][0]; 92 | } 93 | elseif (isset($data->data['child'][SIMPLEPIE_NAMESPACE_RSS_20]['rss'][0]['child'][SIMPLEPIE_NAMESPACE_RSS_20]['channel'][0])) 94 | { 95 | $channel =& $data->data['child'][SIMPLEPIE_NAMESPACE_RSS_20]['rss'][0]['child'][SIMPLEPIE_NAMESPACE_RSS_20]['channel'][0]; 96 | } 97 | else 98 | { 99 | $channel = null; 100 | } 101 | 102 | if ($channel !== null) 103 | { 104 | if (isset($channel['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['entry'])) 105 | { 106 | unset($channel['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['entry']); 107 | } 108 | if (isset($channel['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['entry'])) 109 | { 110 | unset($channel['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['entry']); 111 | } 112 | if (isset($channel['child'][SIMPLEPIE_NAMESPACE_RSS_10]['item'])) 113 | { 114 | unset($channel['child'][SIMPLEPIE_NAMESPACE_RSS_10]['item']); 115 | } 116 | if (isset($channel['child'][SIMPLEPIE_NAMESPACE_RSS_090]['item'])) 117 | { 118 | unset($channel['child'][SIMPLEPIE_NAMESPACE_RSS_090]['item']); 119 | } 120 | if (isset($channel['child'][SIMPLEPIE_NAMESPACE_RSS_20]['item'])) 121 | { 122 | unset($channel['child'][SIMPLEPIE_NAMESPACE_RSS_20]['item']); 123 | } 124 | } 125 | if (isset($data->data['items'])) 126 | { 127 | unset($data->data['items']); 128 | } 129 | if (isset($data->data['ordered_items'])) 130 | { 131 | unset($data->data['ordered_items']); 132 | } 133 | } 134 | return array(serialize($data->data), $items_by_id); 135 | } 136 | } 137 | -------------------------------------------------------------------------------- /library/SimplePie/Cache/File.php: -------------------------------------------------------------------------------- 1 | location = $location; 91 | $this->filename = $name; 92 | $this->extension = $type; 93 | $this->name = "$this->location/$this->filename.$this->extension"; 94 | } 95 | 96 | /** 97 | * Save data to the cache 98 | * 99 | * @param array|SimplePie $data Data to store in the cache. If passed a SimplePie object, only cache the $data property 100 | * @return bool Successfulness 101 | */ 102 | public function save($data) 103 | { 104 | if (file_exists($this->name) && is_writable($this->name) || file_exists($this->location) && is_writable($this->location)) 105 | { 106 | if ($data instanceof SimplePie) 107 | { 108 | $data = $data->data; 109 | } 110 | 111 | $data = serialize($data); 112 | return (bool) file_put_contents($this->name, $data); 113 | } 114 | return false; 115 | } 116 | 117 | /** 118 | * Retrieve the data saved to the cache 119 | * 120 | * @return array Data for SimplePie::$data 121 | */ 122 | public function load() 123 | { 124 | if (file_exists($this->name) && is_readable($this->name)) 125 | { 126 | return unserialize(file_get_contents($this->name)); 127 | } 128 | return false; 129 | } 130 | 131 | /** 132 | * Retrieve the last modified time for the cache 133 | * 134 | * @return int Timestamp 135 | */ 136 | public function mtime() 137 | { 138 | return @filemtime($this->name); 139 | } 140 | 141 | /** 142 | * Set the last modified time to the current time 143 | * 144 | * @return bool Success status 145 | */ 146 | public function touch() 147 | { 148 | return @touch($this->name); 149 | } 150 | 151 | /** 152 | * Remove the cache 153 | * 154 | * @return bool Success status 155 | */ 156 | public function unlink() 157 | { 158 | if (file_exists($this->name)) 159 | { 160 | return unlink($this->name); 161 | } 162 | return false; 163 | } 164 | } 165 | -------------------------------------------------------------------------------- /library/SimplePie/Cache/Memcache.php: -------------------------------------------------------------------------------- 1 | options = array( 90 | 'host' => '127.0.0.1', 91 | 'port' => 11211, 92 | 'extras' => array( 93 | 'timeout' => 3600, // one hour 94 | 'prefix' => 'simplepie_', 95 | ), 96 | ); 97 | $this->options = SimplePie_Misc::array_merge_recursive($this->options, SimplePie_Cache::parse_URL($location)); 98 | 99 | $this->name = $this->options['extras']['prefix'] . md5("$name:$type"); 100 | 101 | $this->cache = new Memcache(); 102 | $this->cache->addServer($this->options['host'], (int) $this->options['port']); 103 | } 104 | 105 | /** 106 | * Save data to the cache 107 | * 108 | * @param array|SimplePie $data Data to store in the cache. If passed a SimplePie object, only cache the $data property 109 | * @return bool Successfulness 110 | */ 111 | public function save($data) 112 | { 113 | if ($data instanceof SimplePie) 114 | { 115 | $data = $data->data; 116 | } 117 | return $this->cache->set($this->name, serialize($data), MEMCACHE_COMPRESSED, (int) $this->options['extras']['timeout']); 118 | } 119 | 120 | /** 121 | * Retrieve the data saved to the cache 122 | * 123 | * @return array Data for SimplePie::$data 124 | */ 125 | public function load() 126 | { 127 | $data = $this->cache->get($this->name); 128 | 129 | if ($data !== false) 130 | { 131 | return unserialize($data); 132 | } 133 | return false; 134 | } 135 | 136 | /** 137 | * Retrieve the last modified time for the cache 138 | * 139 | * @return int Timestamp 140 | */ 141 | public function mtime() 142 | { 143 | $data = $this->cache->get($this->name); 144 | 145 | if ($data !== false) 146 | { 147 | // essentially ignore the mtime because Memcache expires on its own 148 | return time(); 149 | } 150 | 151 | return false; 152 | } 153 | 154 | /** 155 | * Set the last modified time to the current time 156 | * 157 | * @return bool Success status 158 | */ 159 | public function touch() 160 | { 161 | $data = $this->cache->get($this->name); 162 | 163 | if ($data !== false) 164 | { 165 | return $this->cache->set($this->name, $data, MEMCACHE_COMPRESSED, (int) $this->options['extras']['timeout']); 166 | } 167 | 168 | return false; 169 | } 170 | 171 | /** 172 | * Remove the cache 173 | * 174 | * @return bool Success status 175 | */ 176 | public function unlink() 177 | { 178 | return $this->cache->delete($this->name, 0); 179 | } 180 | } 181 | -------------------------------------------------------------------------------- /library/SimplePie/Cache/Memcached.php: -------------------------------------------------------------------------------- 1 | options = array( 86 | 'host' => '127.0.0.1', 87 | 'port' => 11211, 88 | 'extras' => array( 89 | 'timeout' => 3600, // one hour 90 | 'prefix' => 'simplepie_', 91 | ), 92 | ); 93 | $this->options = SimplePie_Misc::array_merge_recursive($this->options, SimplePie_Cache::parse_URL($location)); 94 | 95 | $this->name = $this->options['extras']['prefix'] . md5("$name:$type"); 96 | 97 | $this->cache = new Memcached(); 98 | $this->cache->addServer($this->options['host'], (int)$this->options['port']); 99 | } 100 | 101 | /** 102 | * Save data to the cache 103 | * @param array|SimplePie $data Data to store in the cache. If passed a SimplePie object, only cache the $data property 104 | * @return bool Successfulness 105 | */ 106 | public function save($data) { 107 | if ($data instanceof SimplePie) { 108 | $data = $data->data; 109 | } 110 | 111 | return $this->setData(serialize($data)); 112 | } 113 | 114 | /** 115 | * Retrieve the data saved to the cache 116 | * @return array Data for SimplePie::$data 117 | */ 118 | public function load() { 119 | $data = $this->cache->get($this->name); 120 | 121 | if ($data !== false) { 122 | return unserialize($data); 123 | } 124 | return false; 125 | } 126 | 127 | /** 128 | * Retrieve the last modified time for the cache 129 | * @return int Timestamp 130 | */ 131 | public function mtime() { 132 | $data = $this->cache->get($this->name . '_mtime'); 133 | return (int) $data; 134 | } 135 | 136 | /** 137 | * Set the last modified time to the current time 138 | * @return bool Success status 139 | */ 140 | public function touch() { 141 | $data = $this->cache->get($this->name); 142 | return $this->setData($data); 143 | } 144 | 145 | /** 146 | * Remove the cache 147 | * @return bool Success status 148 | */ 149 | public function unlink() { 150 | return $this->cache->delete($this->name, 0); 151 | } 152 | 153 | /** 154 | * Set the last modified time and data to Memcached 155 | * @return bool Success status 156 | */ 157 | private function setData($data) { 158 | 159 | if ($data !== false) { 160 | $this->cache->set($this->name . '_mtime', time(), (int)$this->options['extras']['timeout']); 161 | return $this->cache->set($this->name, $data, (int)$this->options['extras']['timeout']); 162 | } 163 | 164 | return false; 165 | } 166 | } 167 | -------------------------------------------------------------------------------- /library/SimplePie/Cache/Redis.php: -------------------------------------------------------------------------------- 1 | 8 | * @link http://galvani.cz/ 9 | * @license http://www.opensource.org/licenses/bsd-license.php BSD License 10 | * @version 0.2.9 11 | */ 12 | 13 | 14 | /** 15 | * Caches data to redis 16 | * 17 | * Registered for URLs with the "redis" protocol 18 | * 19 | * For example, `redis://localhost:6379/?timeout=3600&prefix=sp_&dbIndex=0` will 20 | * connect to redis on `localhost` on port 6379. All tables will be 21 | * prefixed with `simple_primary-` and data will expire after 3600 seconds 22 | * 23 | * @package SimplePie 24 | * @subpackage Caching 25 | * @uses Redis 26 | */ 27 | class SimplePie_Cache_Redis implements SimplePie_Cache_Base { 28 | /** 29 | * Redis instance 30 | * 31 | * @var \Redis 32 | */ 33 | protected $cache; 34 | 35 | /** 36 | * Options 37 | * 38 | * @var array 39 | */ 40 | protected $options; 41 | 42 | /** 43 | * Cache name 44 | * 45 | * @var string 46 | */ 47 | protected $name; 48 | 49 | /** 50 | * Cache Data 51 | * 52 | * @var type 53 | */ 54 | protected $data; 55 | 56 | /** 57 | * Create a new cache object 58 | * 59 | * @param string $location Location string (from SimplePie::$cache_location) 60 | * @param string $name Unique ID for the cache 61 | * @param string $type Either TYPE_FEED for SimplePie data, or TYPE_IMAGE for image data 62 | */ 63 | public function __construct($location, $name, $options = null) { 64 | //$this->cache = \flow\simple\cache\Redis::getRedisClientInstance(); 65 | $parsed = SimplePie_Cache::parse_URL($location); 66 | $redis = new Redis(); 67 | $redis->connect($parsed['host'], $parsed['port']); 68 | if (isset($parsed['pass'])) { 69 | $redis->auth($parsed['pass']); 70 | } 71 | if (isset($parsed['path'])) { 72 | $redis->select((int)substr($parsed['path'], 1)); 73 | } 74 | $this->cache = $redis; 75 | 76 | if (!is_null($options) && is_array($options)) { 77 | $this->options = $options; 78 | } else { 79 | $this->options = array ( 80 | 'prefix' => 'rss:simple_primary:', 81 | 'expire' => 0, 82 | ); 83 | } 84 | 85 | $this->name = $this->options['prefix'] . $name; 86 | } 87 | 88 | /** 89 | * @param \Redis $cache 90 | */ 91 | public function setRedisClient(\Redis $cache) { 92 | $this->cache = $cache; 93 | } 94 | 95 | /** 96 | * Save data to the cache 97 | * 98 | * @param array|SimplePie $data Data to store in the cache. If passed a SimplePie object, only cache the $data property 99 | * @return bool Successfulness 100 | */ 101 | public function save($data) { 102 | if ($data instanceof SimplePie) { 103 | $data = $data->data; 104 | } 105 | $response = $this->cache->set($this->name, serialize($data)); 106 | if ($this->options['expire']) { 107 | $this->cache->expire($this->name, $this->options['expire']); 108 | } 109 | 110 | return $response; 111 | } 112 | 113 | /** 114 | * Retrieve the data saved to the cache 115 | * 116 | * @return array Data for SimplePie::$data 117 | */ 118 | public function load() { 119 | $data = $this->cache->get($this->name); 120 | 121 | if ($data !== false) { 122 | return unserialize($data); 123 | } 124 | return false; 125 | } 126 | 127 | /** 128 | * Retrieve the last modified time for the cache 129 | * 130 | * @return int Timestamp 131 | */ 132 | public function mtime() { 133 | 134 | $data = $this->cache->get($this->name); 135 | 136 | if ($data !== false) { 137 | return time(); 138 | } 139 | 140 | return false; 141 | } 142 | 143 | /** 144 | * Set the last modified time to the current time 145 | * 146 | * @return bool Success status 147 | */ 148 | public function touch() { 149 | 150 | $data = $this->cache->get($this->name); 151 | 152 | if ($data !== false) { 153 | $return = $this->cache->set($this->name, $data); 154 | if ($this->options['expire']) { 155 | return $this->cache->expire($this->name, $this->ttl); 156 | } 157 | return $return; 158 | } 159 | 160 | return false; 161 | } 162 | 163 | /** 164 | * Remove the cache 165 | * 166 | * @return bool Success status 167 | */ 168 | public function unlink() { 169 | return $this->cache->set($this->name, null); 170 | } 171 | 172 | } 173 | -------------------------------------------------------------------------------- /library/SimplePie/Caption.php: -------------------------------------------------------------------------------- 1 | ` captions as defined in Media RSS. 47 | * 48 | * Used by {@see SimplePie_Enclosure::get_caption()} and {@see SimplePie_Enclosure::get_captions()} 49 | * 50 | * This class can be overloaded with {@see SimplePie::set_caption_class()} 51 | * 52 | * @package SimplePie 53 | * @subpackage API 54 | */ 55 | class SimplePie_Caption 56 | { 57 | /** 58 | * Content type 59 | * 60 | * @var string 61 | * @see get_type() 62 | */ 63 | var $type; 64 | 65 | /** 66 | * Language 67 | * 68 | * @var string 69 | * @see get_language() 70 | */ 71 | var $lang; 72 | 73 | /** 74 | * Start time 75 | * 76 | * @var string 77 | * @see get_starttime() 78 | */ 79 | var $startTime; 80 | 81 | /** 82 | * End time 83 | * 84 | * @var string 85 | * @see get_endtime() 86 | */ 87 | var $endTime; 88 | 89 | /** 90 | * Caption text 91 | * 92 | * @var string 93 | * @see get_text() 94 | */ 95 | var $text; 96 | 97 | /** 98 | * Constructor, used to input the data 99 | * 100 | * For documentation on all the parameters, see the corresponding 101 | * properties and their accessors 102 | */ 103 | public function __construct($type = null, $lang = null, $startTime = null, $endTime = null, $text = null) 104 | { 105 | $this->type = $type; 106 | $this->lang = $lang; 107 | $this->startTime = $startTime; 108 | $this->endTime = $endTime; 109 | $this->text = $text; 110 | } 111 | 112 | /** 113 | * String-ified version 114 | * 115 | * @return string 116 | */ 117 | public function __toString() 118 | { 119 | // There is no $this->data here 120 | return md5(serialize($this)); 121 | } 122 | 123 | /** 124 | * Get the end time 125 | * 126 | * @return string|null Time in the format 'hh:mm:ss.SSS' 127 | */ 128 | public function get_endtime() 129 | { 130 | if ($this->endTime !== null) 131 | { 132 | return $this->endTime; 133 | } 134 | 135 | return null; 136 | } 137 | 138 | /** 139 | * Get the language 140 | * 141 | * @link http://tools.ietf.org/html/rfc3066 142 | * @return string|null Language code as per RFC 3066 143 | */ 144 | public function get_language() 145 | { 146 | if ($this->lang !== null) 147 | { 148 | return $this->lang; 149 | } 150 | 151 | return null; 152 | } 153 | 154 | /** 155 | * Get the start time 156 | * 157 | * @return string|null Time in the format 'hh:mm:ss.SSS' 158 | */ 159 | public function get_starttime() 160 | { 161 | if ($this->startTime !== null) 162 | { 163 | return $this->startTime; 164 | } 165 | 166 | return null; 167 | } 168 | 169 | /** 170 | * Get the text of the caption 171 | * 172 | * @return string|null 173 | */ 174 | public function get_text() 175 | { 176 | if ($this->text !== null) 177 | { 178 | return $this->text; 179 | } 180 | 181 | return null; 182 | } 183 | 184 | /** 185 | * Get the content type (not MIME type) 186 | * 187 | * @return string|null Either 'text' or 'html' 188 | */ 189 | public function get_type() 190 | { 191 | if ($this->type !== null) 192 | { 193 | return $this->type; 194 | } 195 | 196 | return null; 197 | } 198 | } 199 | -------------------------------------------------------------------------------- /library/SimplePie/Category.php: -------------------------------------------------------------------------------- 1 | 84 | * subject for 85 | * 86 | * @var string|null 87 | * @see get_type() 88 | */ 89 | var $type; 90 | 91 | /** 92 | * Constructor, used to input the data 93 | * 94 | * @param string|null $term 95 | * @param string|null $scheme 96 | * @param string|null $label 97 | * @param string|null $type 98 | */ 99 | public function __construct($term = null, $scheme = null, $label = null, $type = null) 100 | { 101 | $this->term = $term; 102 | $this->scheme = $scheme; 103 | $this->label = $label; 104 | $this->type = $type; 105 | } 106 | 107 | /** 108 | * String-ified version 109 | * 110 | * @return string 111 | */ 112 | public function __toString() 113 | { 114 | // There is no $this->data here 115 | return md5(serialize($this)); 116 | } 117 | 118 | /** 119 | * Get the category identifier 120 | * 121 | * @return string|null 122 | */ 123 | public function get_term() 124 | { 125 | return $this->term; 126 | } 127 | 128 | /** 129 | * Get the categorization scheme identifier 130 | * 131 | * @return string|null 132 | */ 133 | public function get_scheme() 134 | { 135 | return $this->scheme; 136 | } 137 | 138 | /** 139 | * Get the human readable label 140 | * 141 | * @param bool $strict 142 | * @return string|null 143 | */ 144 | public function get_label($strict = false) 145 | { 146 | if ($this->label === null && $strict !== true) 147 | { 148 | return $this->get_term(); 149 | } 150 | return $this->label; 151 | } 152 | 153 | /** 154 | * Get the category type 155 | * 156 | * @return string|null 157 | */ 158 | public function get_type() 159 | { 160 | return $this->type; 161 | } 162 | } 163 | 164 | -------------------------------------------------------------------------------- /library/SimplePie/Copyright.php: -------------------------------------------------------------------------------- 1 | ` copyright tags as defined in Media RSS 46 | * 47 | * Used by {@see SimplePie_Enclosure::get_copyright()} 48 | * 49 | * This class can be overloaded with {@see SimplePie::set_copyright_class()} 50 | * 51 | * @package SimplePie 52 | * @subpackage API 53 | */ 54 | class SimplePie_Copyright 55 | { 56 | /** 57 | * Copyright URL 58 | * 59 | * @var string 60 | * @see get_url() 61 | */ 62 | var $url; 63 | 64 | /** 65 | * Attribution 66 | * 67 | * @var string 68 | * @see get_attribution() 69 | */ 70 | var $label; 71 | 72 | /** 73 | * Constructor, used to input the data 74 | * 75 | * For documentation on all the parameters, see the corresponding 76 | * properties and their accessors 77 | */ 78 | public function __construct($url = null, $label = null) 79 | { 80 | $this->url = $url; 81 | $this->label = $label; 82 | } 83 | 84 | /** 85 | * String-ified version 86 | * 87 | * @return string 88 | */ 89 | public function __toString() 90 | { 91 | // There is no $this->data here 92 | return md5(serialize($this)); 93 | } 94 | 95 | /** 96 | * Get the copyright URL 97 | * 98 | * @return string|null URL to copyright information 99 | */ 100 | public function get_url() 101 | { 102 | if ($this->url !== null) 103 | { 104 | return $this->url; 105 | } 106 | 107 | return null; 108 | } 109 | 110 | /** 111 | * Get the attribution text 112 | * 113 | * @return string|null 114 | */ 115 | public function get_attribution() 116 | { 117 | if ($this->label !== null) 118 | { 119 | return $this->label; 120 | } 121 | 122 | return null; 123 | } 124 | } 125 | -------------------------------------------------------------------------------- /library/SimplePie/Core.php: -------------------------------------------------------------------------------- 1 | ` as defined in Media RSS 46 | * 47 | * Used by {@see SimplePie_Enclosure::get_credit()} and {@see SimplePie_Enclosure::get_credits()} 48 | * 49 | * This class can be overloaded with {@see SimplePie::set_credit_class()} 50 | * 51 | * @package SimplePie 52 | * @subpackage API 53 | */ 54 | class SimplePie_Credit 55 | { 56 | /** 57 | * Credited role 58 | * 59 | * @var string 60 | * @see get_role() 61 | */ 62 | var $role; 63 | 64 | /** 65 | * Organizational scheme 66 | * 67 | * @var string 68 | * @see get_scheme() 69 | */ 70 | var $scheme; 71 | 72 | /** 73 | * Credited name 74 | * 75 | * @var string 76 | * @see get_name() 77 | */ 78 | var $name; 79 | 80 | /** 81 | * Constructor, used to input the data 82 | * 83 | * For documentation on all the parameters, see the corresponding 84 | * properties and their accessors 85 | */ 86 | public function __construct($role = null, $scheme = null, $name = null) 87 | { 88 | $this->role = $role; 89 | $this->scheme = $scheme; 90 | $this->name = $name; 91 | } 92 | 93 | /** 94 | * String-ified version 95 | * 96 | * @return string 97 | */ 98 | public function __toString() 99 | { 100 | // There is no $this->data here 101 | return md5(serialize($this)); 102 | } 103 | 104 | /** 105 | * Get the role of the person receiving credit 106 | * 107 | * @return string|null 108 | */ 109 | public function get_role() 110 | { 111 | if ($this->role !== null) 112 | { 113 | return $this->role; 114 | } 115 | 116 | return null; 117 | } 118 | 119 | /** 120 | * Get the organizational scheme 121 | * 122 | * @return string|null 123 | */ 124 | public function get_scheme() 125 | { 126 | if ($this->scheme !== null) 127 | { 128 | return $this->scheme; 129 | } 130 | 131 | return null; 132 | } 133 | 134 | /** 135 | * Get the credited person/entity's name 136 | * 137 | * @return string|null 138 | */ 139 | public function get_name() 140 | { 141 | if ($this->name !== null) 142 | { 143 | return $this->name; 144 | } 145 | 146 | return null; 147 | } 148 | } 149 | -------------------------------------------------------------------------------- /library/SimplePie/Exception.php: -------------------------------------------------------------------------------- 1 | 54 | * @author elfrink at introweb dot nl 55 | * @author Josh Peck 56 | * @author Sam Sneddon 57 | */ 58 | class SimplePie_Net_IPv6 59 | { 60 | /** 61 | * Uncompresses an IPv6 address 62 | * 63 | * RFC 4291 allows you to compress concecutive zero pieces in an address to 64 | * '::'. This method expects a valid IPv6 address and expands the '::' to 65 | * the required number of zero pieces. 66 | * 67 | * Example: FF01::101 -> FF01:0:0:0:0:0:0:101 68 | * ::1 -> 0:0:0:0:0:0:0:1 69 | * 70 | * @author Alexander Merz 71 | * @author elfrink at introweb dot nl 72 | * @author Josh Peck 73 | * @copyright 2003-2005 The PHP Group 74 | * @license http://www.opensource.org/licenses/bsd-license.php 75 | * @param string $ip An IPv6 address 76 | * @return string The uncompressed IPv6 address 77 | */ 78 | public static function uncompress($ip) 79 | { 80 | $c1 = -1; 81 | $c2 = -1; 82 | if (substr_count($ip, '::') === 1) 83 | { 84 | list($ip1, $ip2) = explode('::', $ip); 85 | if ($ip1 === '') 86 | { 87 | $c1 = -1; 88 | } 89 | else 90 | { 91 | $c1 = substr_count($ip1, ':'); 92 | } 93 | if ($ip2 === '') 94 | { 95 | $c2 = -1; 96 | } 97 | else 98 | { 99 | $c2 = substr_count($ip2, ':'); 100 | } 101 | if (strpos($ip2, '.') !== false) 102 | { 103 | $c2++; 104 | } 105 | // :: 106 | if ($c1 === -1 && $c2 === -1) 107 | { 108 | $ip = '0:0:0:0:0:0:0:0'; 109 | } 110 | // ::xxx 111 | else if ($c1 === -1) 112 | { 113 | $fill = str_repeat('0:', 7 - $c2); 114 | $ip = str_replace('::', $fill, $ip); 115 | } 116 | // xxx:: 117 | else if ($c2 === -1) 118 | { 119 | $fill = str_repeat(':0', 7 - $c1); 120 | $ip = str_replace('::', $fill, $ip); 121 | } 122 | // xxx::xxx 123 | else 124 | { 125 | $fill = ':' . str_repeat('0:', 6 - $c2 - $c1); 126 | $ip = str_replace('::', $fill, $ip); 127 | } 128 | } 129 | return $ip; 130 | } 131 | 132 | /** 133 | * Compresses an IPv6 address 134 | * 135 | * RFC 4291 allows you to compress concecutive zero pieces in an address to 136 | * '::'. This method expects a valid IPv6 address and compresses consecutive 137 | * zero pieces to '::'. 138 | * 139 | * Example: FF01:0:0:0:0:0:0:101 -> FF01::101 140 | * 0:0:0:0:0:0:0:1 -> ::1 141 | * 142 | * @see uncompress() 143 | * @param string $ip An IPv6 address 144 | * @return string The compressed IPv6 address 145 | */ 146 | public static function compress($ip) 147 | { 148 | // Prepare the IP to be compressed 149 | $ip = self::uncompress($ip); 150 | $ip_parts = self::split_v6_v4($ip); 151 | 152 | // Replace all leading zeros 153 | $ip_parts[0] = preg_replace('/(^|:)0+([0-9])/', '\1\2', $ip_parts[0]); 154 | 155 | // Find bunches of zeros 156 | if (preg_match_all('/(?:^|:)(?:0(?::|$))+/', $ip_parts[0], $matches, PREG_OFFSET_CAPTURE)) 157 | { 158 | $max = 0; 159 | $pos = null; 160 | foreach ($matches[0] as $match) 161 | { 162 | if (strlen($match[0]) > $max) 163 | { 164 | $max = strlen($match[0]); 165 | $pos = $match[1]; 166 | } 167 | } 168 | 169 | $ip_parts[0] = substr_replace($ip_parts[0], '::', $pos, $max); 170 | } 171 | 172 | if ($ip_parts[1] !== '') 173 | { 174 | return implode(':', $ip_parts); 175 | } 176 | 177 | return $ip_parts[0]; 178 | } 179 | 180 | /** 181 | * Splits an IPv6 address into the IPv6 and IPv4 representation parts 182 | * 183 | * RFC 4291 allows you to represent the last two parts of an IPv6 address 184 | * using the standard IPv4 representation 185 | * 186 | * Example: 0:0:0:0:0:0:13.1.68.3 187 | * 0:0:0:0:0:FFFF:129.144.52.38 188 | * 189 | * @param string $ip An IPv6 address 190 | * @return array [0] contains the IPv6 represented part, and [1] the IPv4 represented part 191 | */ 192 | private static function split_v6_v4($ip) 193 | { 194 | if (strpos($ip, '.') !== false) 195 | { 196 | $pos = strrpos($ip, ':'); 197 | $ipv6_part = substr($ip, 0, $pos); 198 | $ipv4_part = substr($ip, $pos + 1); 199 | return array($ipv6_part, $ipv4_part); 200 | } 201 | 202 | return array($ip, ''); 203 | } 204 | 205 | /** 206 | * Checks an IPv6 address 207 | * 208 | * Checks if the given IP is a valid IPv6 address 209 | * 210 | * @param string $ip An IPv6 address 211 | * @return bool true if $ip is a valid IPv6 address 212 | */ 213 | public static function check_ipv6($ip) 214 | { 215 | $ip = self::uncompress($ip); 216 | list($ipv6, $ipv4) = self::split_v6_v4($ip); 217 | $ipv6 = explode(':', $ipv6); 218 | $ipv4 = explode('.', $ipv4); 219 | if (count($ipv6) === 8 && count($ipv4) === 1 || count($ipv6) === 6 && count($ipv4) === 4) 220 | { 221 | foreach ($ipv6 as $ipv6_part) 222 | { 223 | // The section can't be empty 224 | if ($ipv6_part === '') 225 | return false; 226 | 227 | // Nor can it be over four characters 228 | if (strlen($ipv6_part) > 4) 229 | return false; 230 | 231 | // Remove leading zeros (this is safe because of the above) 232 | $ipv6_part = ltrim($ipv6_part, '0'); 233 | if ($ipv6_part === '') 234 | $ipv6_part = '0'; 235 | 236 | // Check the value is valid 237 | $value = hexdec($ipv6_part); 238 | if (dechex($value) !== strtolower($ipv6_part) || $value < 0 || $value > 0xFFFF) 239 | return false; 240 | } 241 | if (count($ipv4) === 4) 242 | { 243 | foreach ($ipv4 as $ipv4_part) 244 | { 245 | $value = (int) $ipv4_part; 246 | if ((string) $value !== $ipv4_part || $value < 0 || $value > 0xFF) 247 | return false; 248 | } 249 | } 250 | return true; 251 | } 252 | 253 | return false; 254 | } 255 | 256 | /** 257 | * Checks if the given IP is a valid IPv6 address 258 | * 259 | * @codeCoverageIgnore 260 | * @deprecated Use {@see SimplePie_Net_IPv6::check_ipv6()} instead 261 | * @see check_ipv6 262 | * @param string $ip An IPv6 address 263 | * @return bool true if $ip is a valid IPv6 address 264 | */ 265 | public static function checkIPv6($ip) 266 | { 267 | return self::check_ipv6($ip); 268 | } 269 | } 270 | -------------------------------------------------------------------------------- /library/SimplePie/Rating.php: -------------------------------------------------------------------------------- 1 | ` or `` tags as defined in Media RSS and iTunes RSS respectively 46 | * 47 | * Used by {@see SimplePie_Enclosure::get_rating()} and {@see SimplePie_Enclosure::get_ratings()} 48 | * 49 | * This class can be overloaded with {@see SimplePie::set_rating_class()} 50 | * 51 | * @package SimplePie 52 | * @subpackage API 53 | */ 54 | class SimplePie_Rating 55 | { 56 | /** 57 | * Rating scheme 58 | * 59 | * @var string 60 | * @see get_scheme() 61 | */ 62 | var $scheme; 63 | 64 | /** 65 | * Rating value 66 | * 67 | * @var string 68 | * @see get_value() 69 | */ 70 | var $value; 71 | 72 | /** 73 | * Constructor, used to input the data 74 | * 75 | * For documentation on all the parameters, see the corresponding 76 | * properties and their accessors 77 | */ 78 | public function __construct($scheme = null, $value = null) 79 | { 80 | $this->scheme = $scheme; 81 | $this->value = $value; 82 | } 83 | 84 | /** 85 | * String-ified version 86 | * 87 | * @return string 88 | */ 89 | public function __toString() 90 | { 91 | // There is no $this->data here 92 | return md5(serialize($this)); 93 | } 94 | 95 | /** 96 | * Get the organizational scheme for the rating 97 | * 98 | * @return string|null 99 | */ 100 | public function get_scheme() 101 | { 102 | if ($this->scheme !== null) 103 | { 104 | return $this->scheme; 105 | } 106 | 107 | return null; 108 | } 109 | 110 | /** 111 | * Get the value of the rating 112 | * 113 | * @return string|null 114 | */ 115 | public function get_value() 116 | { 117 | if ($this->value !== null) 118 | { 119 | return $this->value; 120 | } 121 | 122 | return null; 123 | } 124 | } 125 | -------------------------------------------------------------------------------- /library/SimplePie/Registry.php: -------------------------------------------------------------------------------- 1 | 'SimplePie_Cache', 62 | 'Locator' => 'SimplePie_Locator', 63 | 'Parser' => 'SimplePie_Parser', 64 | 'File' => 'SimplePie_File', 65 | 'Sanitize' => 'SimplePie_Sanitize', 66 | 'Item' => 'SimplePie_Item', 67 | 'Author' => 'SimplePie_Author', 68 | 'Category' => 'SimplePie_Category', 69 | 'Enclosure' => 'SimplePie_Enclosure', 70 | 'Caption' => 'SimplePie_Caption', 71 | 'Copyright' => 'SimplePie_Copyright', 72 | 'Credit' => 'SimplePie_Credit', 73 | 'Rating' => 'SimplePie_Rating', 74 | 'Restriction' => 'SimplePie_Restriction', 75 | 'Content_Type_Sniffer' => 'SimplePie_Content_Type_Sniffer', 76 | 'Source' => 'SimplePie_Source', 77 | 'Misc' => 'SimplePie_Misc', 78 | 'XML_Declaration_Parser' => 'SimplePie_XML_Declaration_Parser', 79 | 'Parse_Date' => 'SimplePie_Parse_Date', 80 | ); 81 | 82 | /** 83 | * Class mapping 84 | * 85 | * @see register() 86 | * @var array 87 | */ 88 | protected $classes = array(); 89 | 90 | /** 91 | * Legacy classes 92 | * 93 | * @see register() 94 | * @var array 95 | */ 96 | protected $legacy = array(); 97 | 98 | /** 99 | * Constructor 100 | * 101 | * No-op 102 | */ 103 | public function __construct() { } 104 | 105 | /** 106 | * Register a class 107 | * 108 | * @param string $type See {@see $default} for names 109 | * @param string $class Class name, must subclass the corresponding default 110 | * @param bool $legacy Whether to enable legacy support for this class 111 | * @return bool Successfulness 112 | */ 113 | public function register($type, $class, $legacy = false) 114 | { 115 | if (!@is_subclass_of($class, $this->default[$type])) 116 | { 117 | return false; 118 | } 119 | 120 | $this->classes[$type] = $class; 121 | 122 | if ($legacy) 123 | { 124 | $this->legacy[] = $class; 125 | } 126 | 127 | return true; 128 | } 129 | 130 | /** 131 | * Get the class registered for a type 132 | * 133 | * Where possible, use {@see create()} or {@see call()} instead 134 | * 135 | * @param string $type 136 | * @return string|null 137 | */ 138 | public function get_class($type) 139 | { 140 | if (!empty($this->classes[$type])) 141 | { 142 | return $this->classes[$type]; 143 | } 144 | if (!empty($this->default[$type])) 145 | { 146 | return $this->default[$type]; 147 | } 148 | 149 | return null; 150 | } 151 | 152 | /** 153 | * Create a new instance of a given type 154 | * 155 | * @param string $type 156 | * @param array $parameters Parameters to pass to the constructor 157 | * @return object Instance of class 158 | */ 159 | public function &create($type, $parameters = array()) 160 | { 161 | $class = $this->get_class($type); 162 | 163 | if (in_array($class, $this->legacy)) 164 | { 165 | switch ($type) 166 | { 167 | case 'locator': 168 | // Legacy: file, timeout, useragent, file_class, max_checked_feeds, content_type_sniffer_class 169 | // Specified: file, timeout, useragent, max_checked_feeds 170 | $replacement = array($this->get_class('file'), $parameters[3], $this->get_class('content_type_sniffer')); 171 | array_splice($parameters, 3, 1, $replacement); 172 | break; 173 | } 174 | } 175 | 176 | if (!method_exists($class, '__construct')) 177 | { 178 | $instance = new $class; 179 | } 180 | else 181 | { 182 | $reflector = new ReflectionClass($class); 183 | $instance = $reflector->newInstanceArgs($parameters); 184 | } 185 | 186 | if (method_exists($instance, 'set_registry')) 187 | { 188 | $instance->set_registry($this); 189 | } 190 | return $instance; 191 | } 192 | 193 | /** 194 | * Call a static method for a type 195 | * 196 | * @param string $type 197 | * @param string $method 198 | * @param array $parameters 199 | * @return mixed 200 | */ 201 | public function &call($type, $method, $parameters = array()) 202 | { 203 | $class = $this->get_class($type); 204 | 205 | if (in_array($class, $this->legacy)) 206 | { 207 | switch ($type) 208 | { 209 | case 'Cache': 210 | // For backwards compatibility with old non-static 211 | // Cache::create() methods 212 | if ($method === 'get_handler') 213 | { 214 | $result = @call_user_func_array(array($class, 'create'), $parameters); 215 | return $result; 216 | } 217 | break; 218 | } 219 | } 220 | 221 | $result = call_user_func_array(array($class, $method), $parameters); 222 | return $result; 223 | } 224 | } 225 | -------------------------------------------------------------------------------- /library/SimplePie/Restriction.php: -------------------------------------------------------------------------------- 1 | ` as defined in Media RSS 46 | * 47 | * Used by {@see SimplePie_Enclosure::get_restriction()} and {@see SimplePie_Enclosure::get_restrictions()} 48 | * 49 | * This class can be overloaded with {@see SimplePie::set_restriction_class()} 50 | * 51 | * @package SimplePie 52 | * @subpackage API 53 | */ 54 | class SimplePie_Restriction 55 | { 56 | /** 57 | * Relationship ('allow'/'deny') 58 | * 59 | * @var string 60 | * @see get_relationship() 61 | */ 62 | var $relationship; 63 | 64 | /** 65 | * Type of restriction 66 | * 67 | * @var string 68 | * @see get_type() 69 | */ 70 | var $type; 71 | 72 | /** 73 | * Restricted values 74 | * 75 | * @var string 76 | * @see get_value() 77 | */ 78 | var $value; 79 | 80 | /** 81 | * Constructor, used to input the data 82 | * 83 | * For documentation on all the parameters, see the corresponding 84 | * properties and their accessors 85 | */ 86 | public function __construct($relationship = null, $type = null, $value = null) 87 | { 88 | $this->relationship = $relationship; 89 | $this->type = $type; 90 | $this->value = $value; 91 | } 92 | 93 | /** 94 | * String-ified version 95 | * 96 | * @return string 97 | */ 98 | public function __toString() 99 | { 100 | // There is no $this->data here 101 | return md5(serialize($this)); 102 | } 103 | 104 | /** 105 | * Get the relationship 106 | * 107 | * @return string|null Either 'allow' or 'deny' 108 | */ 109 | public function get_relationship() 110 | { 111 | if ($this->relationship !== null) 112 | { 113 | return $this->relationship; 114 | } 115 | 116 | return null; 117 | } 118 | 119 | /** 120 | * Get the type 121 | * 122 | * @return string|null 123 | */ 124 | public function get_type() 125 | { 126 | if ($this->type !== null) 127 | { 128 | return $this->type; 129 | } 130 | 131 | return null; 132 | } 133 | 134 | /** 135 | * Get the list of restricted things 136 | * 137 | * @return string|null 138 | */ 139 | public function get_value() 140 | { 141 | if ($this->value !== null) 142 | { 143 | return $this->value; 144 | } 145 | 146 | return null; 147 | } 148 | } 149 | -------------------------------------------------------------------------------- /library/SimplePie/XML/Declaration/Parser.php: -------------------------------------------------------------------------------- 1 | data = $data; 118 | $this->data_length = strlen($this->data); 119 | } 120 | 121 | /** 122 | * Parse the input data 123 | * 124 | * @access public 125 | * @return bool true on success, false on failure 126 | */ 127 | public function parse() 128 | { 129 | while ($this->state && $this->state !== 'emit' && $this->has_data()) 130 | { 131 | $state = $this->state; 132 | $this->$state(); 133 | } 134 | $this->data = ''; 135 | if ($this->state === 'emit') 136 | { 137 | return true; 138 | } 139 | 140 | $this->version = ''; 141 | $this->encoding = ''; 142 | $this->standalone = ''; 143 | return false; 144 | } 145 | 146 | /** 147 | * Check whether there is data beyond the pointer 148 | * 149 | * @access private 150 | * @return bool true if there is further data, false if not 151 | */ 152 | public function has_data() 153 | { 154 | return (bool) ($this->position < $this->data_length); 155 | } 156 | 157 | /** 158 | * Advance past any whitespace 159 | * 160 | * @return int Number of whitespace characters passed 161 | */ 162 | public function skip_whitespace() 163 | { 164 | $whitespace = strspn($this->data, "\x09\x0A\x0D\x20", $this->position); 165 | $this->position += $whitespace; 166 | return $whitespace; 167 | } 168 | 169 | /** 170 | * Read value 171 | */ 172 | public function get_value() 173 | { 174 | $quote = substr($this->data, $this->position, 1); 175 | if ($quote === '"' || $quote === "'") 176 | { 177 | $this->position++; 178 | $len = strcspn($this->data, $quote, $this->position); 179 | if ($this->has_data()) 180 | { 181 | $value = substr($this->data, $this->position, $len); 182 | $this->position += $len + 1; 183 | return $value; 184 | } 185 | } 186 | return false; 187 | } 188 | 189 | public function before_version_name() 190 | { 191 | if ($this->skip_whitespace()) 192 | { 193 | $this->state = 'version_name'; 194 | } 195 | else 196 | { 197 | $this->state = false; 198 | } 199 | } 200 | 201 | public function version_name() 202 | { 203 | if (substr($this->data, $this->position, 7) === 'version') 204 | { 205 | $this->position += 7; 206 | $this->skip_whitespace(); 207 | $this->state = 'version_equals'; 208 | } 209 | else 210 | { 211 | $this->state = false; 212 | } 213 | } 214 | 215 | public function version_equals() 216 | { 217 | if (substr($this->data, $this->position, 1) === '=') 218 | { 219 | $this->position++; 220 | $this->skip_whitespace(); 221 | $this->state = 'version_value'; 222 | } 223 | else 224 | { 225 | $this->state = false; 226 | } 227 | } 228 | 229 | public function version_value() 230 | { 231 | if ($this->version = $this->get_value()) 232 | { 233 | $this->skip_whitespace(); 234 | if ($this->has_data()) 235 | { 236 | $this->state = 'encoding_name'; 237 | } 238 | else 239 | { 240 | $this->state = 'emit'; 241 | } 242 | } 243 | else 244 | { 245 | $this->state = false; 246 | } 247 | } 248 | 249 | public function encoding_name() 250 | { 251 | if (substr($this->data, $this->position, 8) === 'encoding') 252 | { 253 | $this->position += 8; 254 | $this->skip_whitespace(); 255 | $this->state = 'encoding_equals'; 256 | } 257 | else 258 | { 259 | $this->state = 'standalone_name'; 260 | } 261 | } 262 | 263 | public function encoding_equals() 264 | { 265 | if (substr($this->data, $this->position, 1) === '=') 266 | { 267 | $this->position++; 268 | $this->skip_whitespace(); 269 | $this->state = 'encoding_value'; 270 | } 271 | else 272 | { 273 | $this->state = false; 274 | } 275 | } 276 | 277 | public function encoding_value() 278 | { 279 | if ($this->encoding = $this->get_value()) 280 | { 281 | $this->skip_whitespace(); 282 | if ($this->has_data()) 283 | { 284 | $this->state = 'standalone_name'; 285 | } 286 | else 287 | { 288 | $this->state = 'emit'; 289 | } 290 | } 291 | else 292 | { 293 | $this->state = false; 294 | } 295 | } 296 | 297 | public function standalone_name() 298 | { 299 | if (substr($this->data, $this->position, 10) === 'standalone') 300 | { 301 | $this->position += 10; 302 | $this->skip_whitespace(); 303 | $this->state = 'standalone_equals'; 304 | } 305 | else 306 | { 307 | $this->state = false; 308 | } 309 | } 310 | 311 | public function standalone_equals() 312 | { 313 | if (substr($this->data, $this->position, 1) === '=') 314 | { 315 | $this->position++; 316 | $this->skip_whitespace(); 317 | $this->state = 'standalone_value'; 318 | } 319 | else 320 | { 321 | $this->state = false; 322 | } 323 | } 324 | 325 | public function standalone_value() 326 | { 327 | if ($standalone = $this->get_value()) 328 | { 329 | switch ($standalone) 330 | { 331 | case 'yes': 332 | $this->standalone = true; 333 | break; 334 | 335 | case 'no': 336 | $this->standalone = false; 337 | break; 338 | 339 | default: 340 | $this->state = false; 341 | return; 342 | } 343 | 344 | $this->skip_whitespace(); 345 | if ($this->has_data()) 346 | { 347 | $this->state = false; 348 | } 349 | else 350 | { 351 | $this->state = 'emit'; 352 | } 353 | } 354 | else 355 | { 356 | $this->state = false; 357 | } 358 | } 359 | } 360 | -------------------------------------------------------------------------------- /library/urljoin.php: -------------------------------------------------------------------------------- 1 | $rel); 36 | } 37 | 38 | if (array_key_exists('path', $pbase) && $pbase['path'] === '/') { 39 | unset($pbase['path']); 40 | } 41 | 42 | if (isset($prel['scheme'])) { 43 | if ($prel['scheme'] != $pbase['scheme'] || in_array($prel['scheme'], $uses_relative) == false) { 44 | return $rel; 45 | } 46 | } 47 | 48 | $merged = array_merge($pbase, $prel); 49 | 50 | // Handle relative paths: 51 | // 'path/to/file.ext' 52 | // './path/to/file.ext' 53 | if (array_key_exists('path', $prel) && substr($prel['path'], 0, 1) != '/') { 54 | 55 | // Normalize: './path/to/file.ext' => 'path/to/file.ext' 56 | if (substr($prel['path'], 0, 2) === './') { 57 | $prel['path'] = substr($prel['path'], 2); 58 | } 59 | 60 | if (array_key_exists('path', $pbase)) { 61 | $dir = preg_replace('@/[^/]*$@', '', $pbase['path']); 62 | $merged['path'] = $dir . '/' . $prel['path']; 63 | } else { 64 | $merged['path'] = '/' . $prel['path']; 65 | } 66 | 67 | } 68 | 69 | if(array_key_exists('path', $merged)) { 70 | // Get the path components, and remove the initial empty one 71 | $pathParts = explode('/', $merged['path']); 72 | array_shift($pathParts); 73 | 74 | $path = []; 75 | $prevPart = ''; 76 | foreach ($pathParts as $part) { 77 | if ($part == '..' && count($path) > 0) { 78 | // Cancel out the parent directory (if there's a parent to cancel) 79 | $parent = array_pop($path); 80 | // But if it was also a parent directory, leave it in 81 | if ($parent == '..') { 82 | array_push($path, $parent); 83 | array_push($path, $part); 84 | } 85 | } else if ($prevPart != '' || ($part != '.' && $part != '')) { 86 | // Don't include empty or current-directory components 87 | if ($part == '.') { 88 | $part = ''; 89 | } 90 | array_push($path, $part); 91 | } 92 | $prevPart = $part; 93 | } 94 | $merged['path'] = '/' . implode('/', $path); 95 | } 96 | 97 | $ret = ''; 98 | if (isset($merged['scheme'])) { 99 | $ret .= $merged['scheme'] . ':'; 100 | } 101 | 102 | if (isset($merged['scheme']) || isset($merged['host'])) { 103 | $ret .= '//'; 104 | } 105 | 106 | if (isset($prel['host'])) { 107 | $hostSource = $prel; 108 | } else { 109 | $hostSource = $pbase; 110 | } 111 | 112 | // username, password, and port are associated with the hostname, not merged 113 | if (isset($hostSource['host'])) { 114 | if (isset($hostSource['user'])) { 115 | $ret .= $hostSource['user']; 116 | if (isset($hostSource['pass'])) { 117 | $ret .= ':' . $hostSource['pass']; 118 | } 119 | $ret .= '@'; 120 | } 121 | $ret .= $hostSource['host']; 122 | if (isset($hostSource['port'])) { 123 | $ret .= ':' . $hostSource['port']; 124 | } 125 | } 126 | 127 | if (isset($merged['path'])) { 128 | $ret .= $merged['path']; 129 | } 130 | 131 | if (isset($prel['query'])) { 132 | $ret .= '?' . $prel['query']; 133 | } 134 | 135 | if (isset($prel['fragment'])) { 136 | $ret .= '#' . $prel['fragment']; 137 | } 138 | 139 | return $ret; 140 | } 141 | -------------------------------------------------------------------------------- /login.php: -------------------------------------------------------------------------------- 1 | 40 | 41 | 42 | 43 | Feed on Feeds - Log on 44 | 45 | 61 | 62 | 63 | 64 |
    65 |
    66 |
    Feed on Feeds

    67 | User name:


    68 | Password:


    69 |
    70 | 73 |
    Incorrect user name or password
    74 | 77 |
    78 |
    79 | 80 | 81 | -------------------------------------------------------------------------------- /logout.php: -------------------------------------------------------------------------------- 1 | 23 | -------------------------------------------------------------------------------- /microsummary.php: -------------------------------------------------------------------------------- 1 | 15 | 16 | -------------------------------------------------------------------------------- /opml.php: -------------------------------------------------------------------------------- 1 | '; 20 | ?> 21 | 22 | 23 | 24 | Feed on Feeds Subscriptions 25 | 26 | 27 | 42 | 43 | HEYO; 44 | } 45 | ?> 46 | 47 | 48 | 49 | -------------------------------------------------------------------------------- /plugins/adv_autotag.ini: -------------------------------------------------------------------------------- 1 | type="prefilter" 2 | description="custom tagging of new items based on regular expressions" -------------------------------------------------------------------------------- /plugins/adv_autotag.php: -------------------------------------------------------------------------------- 1 | $val) { 62 | if (empty($val['filter']) || empty($val['tag'])) { 63 | unset($post['aa_pref'][$key]); 64 | } 65 | } 66 | 67 | usort($post['aa_pref'], '_cb_prefsort'); 68 | 69 | $prefs->set('adv_autotag', array_values($post['aa_pref'])); 70 | $prefs->save(); 71 | 72 | // apply? 73 | if (isset($post['aa_apply_u']) || isset($post['aa_apply_a'])) { 74 | if (isset($post['aa_apply_u'])) { 75 | $items = fof_get_items(fof_current_user()); 76 | } else { 77 | $items = fof_get_items(fof_current_user(), null, 'all'); 78 | } 79 | 80 | foreach ($items as $item) { 81 | $tags = fof_adv_autotag(null, $item['item_title'], $item['item_content'], $item); 82 | 83 | if (count($tags)) { 84 | fof_tag_item(fof_current_user(), $item['item_id'], $tags); 85 | } 86 | 87 | } 88 | } 89 | } 90 | 91 | function edit_prefs_adv_autotag($prefs) { 92 | echo '

    Advanced Autotagging

    93 |
    94 |

    Note: Instead of adding you can also remove a tag by using a dash as first character, use e.g. \'-unread\' to automatically mark items as read.

    95 |
    96 | 97 | 98 | 99 | 100 | 101 | 102 | '; 103 | 104 | $aa_prefs = $prefs->get('adv_autotag'); 105 | if (empty($aa_prefs)) { 106 | $aa_prefs = array(); 107 | } 108 | 109 | $count = 0; 110 | 111 | foreach ($aa_prefs as $p) { 112 | echo ' 113 | 120 | 127 | 130 | 133 | '; 134 | 135 | $count++; 136 | } 137 | 138 | echo ' 139 | 146 | 153 | 156 | 159 | 160 |
    WhatOperatorFilterTag
    114 | 119 | 121 | 126 | 128 | 129 | 131 | 132 |
    140 | 145 | 147 | 152 | 154 | 155 | 157 | 158 |
    161 |
    162 | 163 | 164 | 165 |
    166 |
    '; 167 | } 168 | 169 | function _cb_prefsort($a, $b) { 170 | if ($a['tag'] == $b['tag']) { 171 | return strcmp($a['filter'], $b['filter']); 172 | } 173 | 174 | return strcmp($a['tag'], $b['tag']); 175 | } -------------------------------------------------------------------------------- /plugins/autotag.ini: -------------------------------------------------------------------------------- 1 | type="prefilter" 2 | description="new entries containing certain words defined below will be automatically tagged using those words" -------------------------------------------------------------------------------- /plugins/autotag.php: -------------------------------------------------------------------------------- 1 | getElementsByTagName('svg') as $svg) { 9 | if ($svg->hasAttribute('viewbox')) { 10 | $viewBox = preg_split('/\s+/', $svg->getAttribute('viewbox'), -1, PREG_SPLIT_NO_EMPTY); 11 | if (!$svg->hasAttribute('width')) { 12 | $svg->setAttribute('width', (int)$viewBox[2] - (int)$viewBox[0]); 13 | } 14 | if (!$svg->hasAttribute('height')) { 15 | $svg->setAttribute('height', (int)$viewBox[3] - (int)$viewBox[1]); 16 | } 17 | } 18 | } 19 | 20 | return $dom; 21 | } 22 | 23 | ?> 24 | 25 | -------------------------------------------------------------------------------- /plugins/images_by_proxy.ini: -------------------------------------------------------------------------------- 1 | type="domitemfilter" 2 | description="Proxy loading of images locally to fix referrer issues and mixed-content warnings" 3 | -------------------------------------------------------------------------------- /plugins/images_by_proxy.php: -------------------------------------------------------------------------------- 1 | getElementsByTagName('img') as $img) { 13 | foreach (['src', 'data-fof-ondemand-src'] as $attr) { 14 | if ($img->hasAttribute($attr)) { 15 | $img->setAttribute($attr, wrap_image_in_proxy($img->getAttribute($attr), $item)); 16 | } 17 | } 18 | foreach (['srcset', 'data-fof-ondemand-srcset'] as $attr) { 19 | if ($img->hasAttribute($attr)) { 20 | $srcset = $img->getAttribute($attr); 21 | $specs = preg_split('/,\s+/', $srcset); 22 | 23 | $outspec = []; 24 | foreach ($specs as $spec) { 25 | list($url, $selector) = preg_split('/\s+/', $spec, 2); 26 | $outspec[] = wrap_image_in_proxy($url, $item) . ' ' . $selector; 27 | } 28 | $img->setAttribute($attr, implode(', ', $outspec)); 29 | } 30 | } 31 | } 32 | 33 | return $dom; 34 | } 35 | ?> 36 | -------------------------------------------------------------------------------- /plugins/item_targets.ini: -------------------------------------------------------------------------------- 1 | type="domitemfilter" 2 | description="always open links in feed entries in new window" 3 | -------------------------------------------------------------------------------- /plugins/item_targets.php: -------------------------------------------------------------------------------- 1 | getElementsByTagName('a') as $a) { 9 | $a->setAttribute('target', '_blank'); 10 | } 11 | 12 | return $dom; 13 | } 14 | ?> -------------------------------------------------------------------------------- /plugins/mediaplayer.swf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RomanSixty/Feed-on-Feeds/ea3f132aecbd172aa765631a2ec8ed0a127fdee9/plugins/mediaplayer.swf -------------------------------------------------------------------------------- /plugins/mini_podcast.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RomanSixty/Feed-on-Feeds/ea3f132aecbd172aa765631a2ec8ed0a127fdee9/plugins/mini_podcast.png -------------------------------------------------------------------------------- /plugins/permalink.ini: -------------------------------------------------------------------------------- 1 | type="widget" 2 | description="display a link to the entry below each feed entry" -------------------------------------------------------------------------------- /plugins/permalink.php: -------------------------------------------------------------------------------- 1 | permalink'; 9 | } 10 | ?> 11 | -------------------------------------------------------------------------------- /plugins/place_audio.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RomanSixty/Feed-on-Feeds/ea3f132aecbd172aa765631a2ec8ed0a127fdee9/plugins/place_audio.png -------------------------------------------------------------------------------- /plugins/place_video.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RomanSixty/Feed-on-Feeds/ea3f132aecbd172aa765631a2ec8ed0a127fdee9/plugins/place_video.png -------------------------------------------------------------------------------- /plugins/plain.ini: -------------------------------------------------------------------------------- 1 | type="renderfilter" 2 | description="remove most HTML markup from feed entries (e.g. , , ,
    )" -------------------------------------------------------------------------------- /plugins/plain.php: -------------------------------------------------------------------------------- 1 |

    "); 7 | } -------------------------------------------------------------------------------- /plugins/reddit.ini: -------------------------------------------------------------------------------- 1 | type="domitemfilter" 2 | description="try and replace default mini thumbnails" 3 | -------------------------------------------------------------------------------- /plugins/reddit.php: -------------------------------------------------------------------------------- 1 | getElementsByTagName('a') as $link) { 12 | if ($link->nodeValue != '[link]') 13 | continue; 14 | $url = $link->getAttribute('href'); 15 | } 16 | 17 | $matches = array(); 18 | 19 | // some manipulations on that because of different content shared on reddit 20 | if (preg_match ('~gfycat\.com/(.+)$~', $url, $matches)) { 21 | if ( preg_match ('~[A-Z]~', $matches[1])) 22 | $url = 'https://thumbs.gfycat.com/'.$matches[1].'-size_restricted.gif'; 23 | } 24 | elseif (preg_match ('~^.*imgur\.com/[^/.]+$~', $url)) { 25 | $url .= '.jpg'; 26 | } 27 | elseif (preg_match ('~^(.*imgur\.com/[^/]+)\.gifv$~', $url, $matches)) { 28 | $url = $matches[1].'.gif'; 29 | } 30 | 31 | if (preg_match('~\.(jpe?g|gif|png)$~i', $url)) { 32 | $nodes = $dom->getElementsByTagName('img'); 33 | // replace currently inserted image with larger version 34 | if ($nodes->length) { 35 | foreach ($dom->getElementsByTagName('img') as $img) { 36 | if ($img->hasAttribute('data-fof-ondemand-src')) 37 | $img->setAttribute('data-fof-ondemand-src', $url); 38 | else 39 | $img->setAttribute('src', $url); 40 | } 41 | } 42 | // create image if none was present before 43 | else { 44 | $img = $dom->createElement('img'); 45 | $img->setAttribute('src', $url); 46 | $dom->appendChild($img); 47 | } 48 | } 49 | } 50 | 51 | return $dom; 52 | } -------------------------------------------------------------------------------- /plugins/remove_media_autoplay.ini: -------------------------------------------------------------------------------- 1 | type="domitemfilter" 2 | description="prevent media in feed items from autoplaying" 3 | -------------------------------------------------------------------------------- /plugins/remove_media_autoplay.php: -------------------------------------------------------------------------------- 1 | getElementsByTagName('video') as $video) { 10 | $video->removeAttribute("autoplay"); 11 | $video->setAttribute("controls", true); 12 | } 13 | 14 | // Make sure audio doesn't autoplay and does have contorls 15 | foreach ($dom->getElementsByTagName('audio') as $audio) { 16 | $audio->removeAttribute("autoplay"); 17 | $audio->setAttribute("controls", true); 18 | } 19 | 20 | return $dom; 21 | } 22 | ?> -------------------------------------------------------------------------------- /plugins/share-off.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RomanSixty/Feed-on-Feeds/ea3f132aecbd172aa765631a2ec8ed0a127fdee9/plugins/share-off.gif -------------------------------------------------------------------------------- /plugins/share-on.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RomanSixty/Feed-on-Feeds/ea3f132aecbd172aa765631a2ec8ed0a127fdee9/plugins/share-on.gif -------------------------------------------------------------------------------- /plugins/sharing.ini: -------------------------------------------------------------------------------- 1 | type="widget" 2 | description="display 'share' and 'unshare' links below each feed entry to tag them as 'shared' (provided sharing is enabled above)" -------------------------------------------------------------------------------- /plugins/sharing.php: -------------------------------------------------------------------------------- 1 | ' . $shared_text . ''; 20 | } -------------------------------------------------------------------------------- /plugins/text_content.ini: -------------------------------------------------------------------------------- 1 | type="prefilter" 2 | description="Wrap plain-text Atom content in an appropriate div" 3 | -------------------------------------------------------------------------------- /plugins/text_content.php: -------------------------------------------------------------------------------- 1 | (or unspecified type) wrap it in a "text-post" div 7 | */ 8 | function fof_text_content($item, $link, $title, $content) { 9 | $content_tag = null; 10 | 11 | foreach (Array(SIMPLEPIE_NAMESPACE_ATOM_10, SIMPLEPIE_NAMESPACE_ATOM_03) as $ns) { 12 | if ($tags = $item->get_item_tags($ns, "content")) { 13 | foreach ($tags as $tag) { 14 | fof_log("found $ns:content"); 15 | $content_tag = $tag; 16 | } 17 | } else if ($tags = $item->get_item_tags($ns, "summary")) { 18 | foreach ($tags as $tag) { 19 | fof_log("found $ns:summary"); 20 | $content_tag = $tag; 21 | } 22 | } 23 | } 24 | 25 | if ($content_tag) { 26 | // We are an atom item with a best-match content element 27 | if (!isset($tag['attribs']['']['type']) 28 | || $tag['attribs']['']['type'] == 'text') { 29 | // type is either unset, or explicitly set to text 30 | $content = '
    ' . $content . '
    '; 31 | } 32 | } 33 | 34 | return array($link, $title, $content); 35 | } -------------------------------------------------------------------------------- /plugins/twitter.ini: -------------------------------------------------------------------------------- 1 | type="widget" 2 | description="display a link below each feed item to quickly share it on twitter" -------------------------------------------------------------------------------- /plugins/twitter.php: -------------------------------------------------------------------------------- 1 | $maxlen) { 15 | $via = ' via ' . $item['feed_title']; 16 | $text = $title . $via; 17 | } 18 | 19 | # then try truncating the title 20 | if (strlen($text) > $maxlen) { 21 | $text = substr($title, 0, $maxlen - strlen($via) - 3) . '...' . $via; 22 | } 23 | 24 | # then try truncating the source as well 25 | if (strlen($text) > $maxlen) { 26 | $text = substr($title, 0, $maxlen * strlen($title) / strlen($text) - 3) 27 | . '...' 28 | . substr($via, 0, $maxlen * strlen($via) / strlen($text) - 3) 29 | . '...'; 30 | } 31 | 32 | $posturl = 'http://twitter.com/home?status=' . urlencode("$text $url"); 33 | $linktag = ''; 34 | 35 | return $linktag . ' tweet'; 36 | } -------------------------------------------------------------------------------- /plugins/vimeo.ini: -------------------------------------------------------------------------------- 1 | type="prefilter" 2 | description="replace feed entries from Vimeo by their thumbnails and only embed the video on click" -------------------------------------------------------------------------------- /plugins/vimeo.php: -------------------------------------------------------------------------------- 1 | get_enclosure(0); 15 | 16 | $thumb_source = $enclosure->get_thumbnail(0); 17 | 18 | if (preg_match('~/([0-9]+)_960\.jpg?~', $thumb_source, $matches1) && preg_match('~/([0-9]+)~', $link, $matches2)) { 19 | $embed = '
    '; 23 | } 24 | 25 | $content = $embed . preg_replace('~~ui', '', $content); 26 | } 27 | 28 | return array($link, $title, $content); 29 | } -------------------------------------------------------------------------------- /plugins/wordpress.ini: -------------------------------------------------------------------------------- 1 | type="widget" 2 | description="add a link below each feed entry to quickly post it on your wordpress blog" -------------------------------------------------------------------------------- /plugins/wordpress.php: -------------------------------------------------------------------------------- 1 | ' . $item['item_content'] . '
    '); 19 | 20 | $link = "$wordpress/wp-admin/post-new.php?text=$text&popupurl=$url&popuptitle=$title"; 21 | 22 | return ' blog'; 23 | } -------------------------------------------------------------------------------- /plugins/wordpress.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RomanSixty/Feed-on-Feeds/ea3f132aecbd172aa765631a2ec8ed0a127fdee9/plugins/wordpress.png -------------------------------------------------------------------------------- /plugins/youtube.ini: -------------------------------------------------------------------------------- 1 | type="prefilter" 2 | description="replace feed entries from YouTube by their thumbnails and only embed the video on click" -------------------------------------------------------------------------------- /plugins/youtube.php: -------------------------------------------------------------------------------- 1 | '; 18 | } 19 | 20 | $content .= $embed; 21 | } 22 | 23 | // replace default YouTube embeds... 24 | // but not if it's a playlist 25 | if (strstr($content, 'youtube.com/embed/videoseries') === false) { 26 | $content = preg_replace( 27 | '~]+src="https?://(?:www\.)?youtube\.com/embed/([^?"]+)[^>]*>.*~iu', 28 | '
    ', 32 | $content); 33 | } 34 | 35 | return array($link, $title, $content); 36 | } -------------------------------------------------------------------------------- /set-prefs.php: -------------------------------------------------------------------------------- 1 | $v) { 20 | $prefs->set($k, $v); 21 | } 22 | 23 | $prefs->save(); 24 | 25 | ?> 26 | -------------------------------------------------------------------------------- /shared.php: -------------------------------------------------------------------------------- 1 | get("sharing"); 29 | if ($sharing == "no") { 30 | die; 31 | } 32 | 33 | $name = $prefs->get("sharedname"); 34 | $url = $prefs->get("sharedurl"); 35 | 36 | $offset = $prefs->get('tzoffset'); 37 | 38 | $which = ($sharing == "all") ? "all" : "shared"; 39 | 40 | if (isset($_GET['which'])) { 41 | $which = ($sharing == "all" || $sharing == "all_tagged") ? $_GET['which'] : "shared " . $_GET['which']; 42 | $extratitle = " items tagged " . $_GET['which']; 43 | } else { 44 | $extratitle = ''; 45 | } 46 | 47 | $feed = NULL; 48 | if (isset($_GET['feed'])) { 49 | $feed = $_GET['feed']; 50 | $r = fof_db_get_feed_by_id($feed); 51 | $extratitle .= " from " . $r['feed_title'] . ""; 52 | } 53 | 54 | $result = fof_get_items($user, $feed, $which, NULL, 0, 100); 55 | 56 | $qv = array('user' => $user, 57 | 'which' => isset($_GET['which']) ? $_GET['which'] : NULL, 58 | 'feed' => isset($_GET['feed']) ? $_GET['feed'] : NULL, 59 | ); 60 | $baseurl = fof_base_url(); 61 | $shared_feed = fof_url($baseurl, array_merge($qv, array('format' => 'atom'))); 62 | $shared_link = fof_url($baseurl, $qv); 63 | 64 | if ($format == 'atom') { 65 | fof_set_content_type('application/atom+xml'); 66 | echo ''; 67 | ?> 68 | 69 | 70 | Feed on Feeds - Shared Items<?php if ($name) { 71 | echo " from $name"; 72 | } 73 | if ($extratitle) { 74 | echo " " . strip_tags($extratitle); 75 | } 76 | ?> 77 | 78 | Feed on Feeds 79 | $name"; 81 | } 82 | ?> 83 | 84 | 85 | 86 | 113 | 114 | 115 | 116 | <?php echo $item_title ?> 117 | 118 | 119 | 120 | 121 | 122 | 123 | <?php echo $feed_title ?> 124 | 125 | 126 | '; 129 | } else /* format != 'atom' */ 130 | { 131 | fof_set_content_type(); 132 | ?> 133 | 134 | 135 | 136 | 137 | Feed on Feeds - Shared Items<?php if ($name) { 138 | echo " from $name"; 139 | } 140 | if ($extratitle) { 141 | echo " " . strip_tags($extratitle); 142 | } 143 | ?> 144 | 145 | 146 | 158 | 159 | 160 |

    Feed on Feeds - Shared Items 161 | "; 168 | } 169 | 170 | if ($name) { 171 | echo "$name"; 172 | } 173 | 174 | if ($url) { 175 | echo ""; 176 | } 177 | 178 | if ($extratitle) { 179 | echo "
    $extratitle" 180 | ; 181 | } 182 | ?> 183 |

    184 |
    185 | '; 191 | 192 | $feed_link = $item['feed_link']; 193 | $feed_title = $item['feed_title']; 194 | $feed_image = $item['feed_image']; 195 | $feed_description = $item['feed_description']; 196 | 197 | $item_link = $item['item_link']; 198 | $item_id = $item['item_id']; 199 | $item_title = $item['item_title']; 200 | $item_content = $item['item_content']; 201 | 202 | $item_published = gmdate("Y-n-d g:ia", $item['item_published'] + $offset * 60 * 60); 203 | $item_cached = gmdate("Y-n-d g:ia", $item['item_cached'] + $offset * 60 * 60); 204 | $item_updated = gmdate("Y-n-d g:ia", $item['item_updated'] + $offset * 60 * 60); 205 | 206 | if (!$item_title) { 207 | $item_title = "[no title]"; 208 | } 209 | 210 | ?> 211 |
    212 |

    213 | 214 | 215 | 216 |

    217 | - 218 |

    219 | 220 | 221 |

    222 | on GMT 223 |
    224 | 225 |
    226 |
    227 |
    228 | No shared items.

    "; 233 | } 234 | ?> 235 | 236 | 239 | -------------------------------------------------------------------------------- /uninstall.php: -------------------------------------------------------------------------------- 1 | 20 | 21 | 22 | 23 | 24 | feed on feeds - uninstallation 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | exec($query); 46 | } 47 | } catch (PDOException $e) { 48 | die('Cannot drop table:
    ' . $e->GetMessage() . '
    '); 49 | } 50 | 51 | echo 'Done. Now just delete this entire directory and we\'ll forget this ever happened.'; 52 | } else { 53 | ?> 54 | 60 | phew! 61 | 62 | 63 | -------------------------------------------------------------------------------- /update-quiet.php: -------------------------------------------------------------------------------- 1 | prefs; 25 | 26 | fof_log("=== update started, timeout = $fof_admin_prefs[autotimeout], purge = $fof_admin_prefs[purge] ===", "update"); 27 | 28 | $result = fof_db_get_feeds_needing_attempt(); 29 | 30 | $feeds = array(); 31 | 32 | $now = time(); 33 | while (($feed = fof_db_get_row($result)) !== false) { 34 | if (($now - $feed['feed_cache_attempt_date']) > ($fof_admin_prefs['autotimeout'] * 60) 35 | && ($now > $feed['feed_cache_next_attempt'])) { 36 | $feeds[] = $feed; 37 | } else { 38 | fof_log("skipping $feed[feed_id] $feed[feed_url]", 'update'); 39 | } 40 | } 41 | 42 | $feeds = fof_multi_sort($feeds, 'feed_cache_attempt_date', false); 43 | 44 | foreach ($feeds as $feed) { 45 | fof_log("updating $feed[feed_id] $feed[feed_url]", 'update'); 46 | fof_update_feed($feed['feed_id']); 47 | } 48 | 49 | fof_log("optimizing database", "update"); 50 | 51 | fof_db_optimize(); 52 | 53 | fof_log("=== update complete ===", "update"); 54 | 55 | ob_end_clean(); 56 | ?> 57 | -------------------------------------------------------------------------------- /update-single.php: -------------------------------------------------------------------------------- 1 | " . fof_render_feed_link($feed) . " has $count new items."; 23 | } else { 24 | echo "" . fof_render_feed_link($feed) . " has no new items."; 25 | } 26 | 27 | if ($error) { 28 | echo " $error"; 29 | } 30 | 31 | ?> 32 | -------------------------------------------------------------------------------- /update.php: -------------------------------------------------------------------------------- 1 | \n"; 18 | 19 | $p = &FoF_Prefs::instance(); 20 | $admin_prefs = $p->admin_prefs; 21 | 22 | $feeds = array(); 23 | 24 | if (!empty($_GET['feed'])) { 25 | $feed = fof_db_get_feed_by_id($_GET['feed']); 26 | if (empty($feed)) { 27 | $feed_status = "Error: unknown feed '" . $_GET['feed'] . "'"; 28 | } else { 29 | $feeds[] = $feed; 30 | $feed_status = " is waiting to update..."; 31 | } 32 | echo '
    ' 33 | . fof_render_feed_link($feed) . ' ' . $feed_status 34 | . "
    \n"; 35 | } else { 36 | if ($fof_user_id == 1) { 37 | $result = fof_db_get_feeds_needing_attempt(); 38 | } else { 39 | $result = fof_db_get_subscriptions(fof_current_user(), true); 40 | } 41 | 42 | while (($feed = fof_db_get_row($result)) !== false) { 43 | if ((time() - $feed['feed_cache_attempt_date']) < ($admin_prefs['manualtimeout'] * 60)) { 44 | list($timestamp) = fof_nice_time_stamp($feed['feed_cache_date']); 45 | $feed_status = " was just updated $timestamp!"; 46 | } else if (time() < $feed['feed_cache_next_attempt']) { 47 | list($timestamp) = fof_nice_time_stamp($feed['feed_cache_next_attempt']); 48 | $feed_status = " isn't due for an update for $timestamp."; 49 | } else { 50 | $feeds[] = $feed; 51 | $feed_status = " is waiting to update..."; 52 | } 53 | echo '
    ' 54 | . fof_render_feed_link($feed) . ' ' . $feed_status 55 | . "
    \n"; 56 | } 57 | } 58 | 59 | $feeds = fof_multi_sort($feeds, 'feed_cache_attempt_date', false); 60 | 61 | $feedjson = array(); 62 | foreach ($feeds as $feed) { 63 | $feedjson[] = json_encode(array('id' => $feed['feed_id'], 'title' => $feed['feed_title'])); 64 | } 65 | ?> 66 | 99 | 100 | 103 | 104 | -------------------------------------------------------------------------------- /view-action.php: -------------------------------------------------------------------------------- 1 | $val) { 19 | if ($val == "checked") { 20 | $key = substr($key, 1); 21 | $items[] = $key; 22 | } 23 | } 24 | 25 | /* update a view's sorting settings, removes the view if it matches user's current default */ 26 | if (!empty($_POST['view_order'])) { 27 | $feed = empty($_POST['view_feed']) ? NULL : $_POST['view_feed']; 28 | $what = empty($_POST['view_what']) ? NULL : $_POST['view_what']; 29 | 30 | $prefs = &FoF_Prefs::instance(); 31 | $order = $prefs->get('order'); 32 | 33 | $tag_ids = fof_db_get_tag_name_map(explode(' ', $what)); 34 | $feed_ids = array_filter(array($feed)); 35 | 36 | $settings = fof_db_view_settings_get(fof_current_user(), $tag_ids, $feed_ids); 37 | $settings['order'] = $_POST['view_order']; 38 | 39 | if ($order == $_POST['view_order']) { 40 | /* As there's only one view option currently, just remove view entirely if desired order matches default. */ 41 | fof_db_view_expunge(fof_current_user(), $tag_ids, $feed_ids); 42 | echo ''; 43 | } else { 44 | fof_db_view_settings_set(fof_current_user(), $tag_ids, $feed_ids, $settings); 45 | echo ''; 46 | } 47 | exit(); 48 | } 49 | 50 | if (!empty($_POST['deltag'])) { 51 | fof_untag(fof_current_user(), $_POST['deltag']); 52 | } else if (!empty($_POST['feed'])) { 53 | fof_db_mark_feed_read(fof_current_user(), $_POST['feed']); 54 | } else if (!empty($_POST['mark_read'])) { 55 | fof_db_mark_read(fof_current_user(), array($_POST['mark_read'])); 56 | } else if (!empty($_POST['fold'])) { 57 | fof_db_fold(fof_current_user(), array($_POST['fold'])); 58 | } else if (!empty($_POST['unfold'])) { 59 | fof_db_unfold(fof_current_user(), array($_POST['unfold'])); 60 | } else if (!empty($_POST['tag_read'])) { 61 | fof_db_mark_tag_read(fof_current_user(), $_POST['tag_read']); 62 | } else { 63 | if (!empty($items) && !empty($_POST['action'])) { 64 | if ($_POST['action'] == 'read') { 65 | fof_db_mark_read(fof_current_user(), $items); 66 | } 67 | 68 | if ($_POST['action'] == 'unread') { 69 | fof_db_mark_unread(fof_current_user(), $items); 70 | } 71 | } 72 | 73 | if (!empty($_POST['return'])) { 74 | header("Location: " . urldecode($_POST['return'])); 75 | } 76 | 77 | } 78 | ?> 79 | -------------------------------------------------------------------------------- /websub.php: -------------------------------------------------------------------------------- 1 | 63 | 64 | Updated feed . 65 | --------------------------------------------------------------------------------