summaryrefslogtreecommitdiffstats
path: root/common/app
diff options
context:
space:
mode:
authorNicolas Lécureuil <neoclust@mageia.org>2020-05-17 14:46:00 +0200
committerNicolas Lécureuil <neoclust@mageia.org>2020-05-17 14:46:00 +0200
commite3de9d7dd1331f9718e04cc98e9ca7cfa27cf4aa (patch)
tree336981502f93ceb9fa5ed33ea3b47dcefc5a8402 /common/app
parentff32e499745367b816d10f25e63ff3328214c32f (diff)
downloadplanet-e3de9d7dd1331f9718e04cc98e9ca7cfa27cf4aa.tar
planet-e3de9d7dd1331f9718e04cc98e9ca7cfa27cf4aa.tar.gz
planet-e3de9d7dd1331f9718e04cc98e9ca7cfa27cf4aa.tar.bz2
planet-e3de9d7dd1331f9718e04cc98e9ca7cfa27cf4aa.tar.xz
planet-e3de9d7dd1331f9718e04cc98e9ca7cfa27cf4aa.zip
Sync with master of moonmoon ( version 9.0.0-rc)HEADuser/wally/upstream-10-devmaster
Source from https://github.com/Emmafrs/moonmoon/
Diffstat (limited to 'common/app')
-rwxr-xr-xcommon/app/app.php37
-rw-r--r--common/app/classes/CSRF.php55
-rw-r--r--common/app/classes/Cache.php254
-rw-r--r--common/app/classes/Opml.php70
-rw-r--r--common/app/classes/OpmlManager.php50
-rw-r--r--common/app/classes/Planet.php266
-rw-r--r--common/app/classes/PlanetConfig.php166
-rw-r--r--common/app/classes/PlanetError.php23
-rw-r--r--common/app/classes/PlanetFeed.php20
-rw-r--r--common/app/classes/PlanetItem.php11
-rwxr-xr-xcommon/app/classes/Simplel10n.php52
-rw-r--r--common/app/helpers.php130
-rw-r--r--common/app/l10n/de.lang219
-rw-r--r--common/app/l10n/en.lang4
-rw-r--r--common/app/l10n/es.lang244
-rwxr-xr-xcommon/app/l10n/extract.php13
-rwxr-xr-xcommon/app/l10n/fr.lang4
17 files changed, 1543 insertions, 75 deletions
diff --git a/common/app/app.php b/common/app/app.php
index 0703ac5..0797cc7 100755
--- a/common/app/app.php
+++ b/common/app/app.php
@@ -1,29 +1,15 @@
<?php
-//Debug ?
-$debug = isset($_GET['debug']) ? $_GET['debug'] : 0;
-if ($debug) {
- error_reporting(E_ALL);
-} else {
- error_reporting(0);
-}
-
-include(dirname(__FILE__).'/lib/lib.opml.php');
-include(dirname(__FILE__).'/lib/simplepie/simplepie.inc');
-include(dirname(__FILE__).'/lib/spyc-0.5/spyc.php');
+error_reporting(0);
-include_once(dirname(__FILE__).'/classes/PlanetConfig.php');
-include_once(dirname(__FILE__).'/classes/PlanetFeed.php');
-include_once(dirname(__FILE__).'/classes/PlanetItem.php');
-include_once(dirname(__FILE__).'/classes/PlanetError.php');
-include_once(dirname(__FILE__).'/classes/Planet.class.php');
-include_once(dirname(__FILE__).'/classes/Simplel10n.class.php');
+require_once __DIR__.'/../vendor/autoload.php';
-$savedConfig = dirname(__FILE__).'/../custom/config.yml';
-$moon_version = file_get_contents(dirname(__FILE__).'/../VERSION');
+$savedConfig = __DIR__.'/../custom/config.yml';
+$moon_version = file_get_contents(__DIR__.'/../VERSION');
-if (is_file($savedConfig)){
+session_start();
+if (is_installed()) {
$conf = Spyc::YAMLLoad($savedConfig);
// this is a check to upgrade older config file without l10n
@@ -35,13 +21,12 @@ if (is_file($savedConfig)){
$PlanetConfig = new PlanetConfig($conf);
$Planet = new Planet($PlanetConfig);
-}
-$l10n = new Simplel10n($conf['locale']);
+ if ($conf['debug']) {
+ error_reporting(E_ALL);
+ }
-// this is an helper function. We will usually use that function and not Simplel10n::getString()
-function _g($str, $comment='') {
- return Simplel10n::getString($str);
}
-
+$l10n = new Simplel10n($conf['locale']);
+$csrf = new CSRF();
diff --git a/common/app/classes/CSRF.php b/common/app/classes/CSRF.php
new file mode 100644
index 0000000..9a700cf
--- /dev/null
+++ b/common/app/classes/CSRF.php
@@ -0,0 +1,55 @@
+<?php
+
+class CSRF
+{
+ /** @var string */
+ const HMAC_ALGORITHM = 'sha1';
+
+ /** @var string */
+ const SESSION_KEY_NAME = '_csrf_key';
+
+ /**
+ * Ensure that a CSRF token is valid for a given action.
+ *
+ * @param string $token
+ * @param string $action
+ * @return bool
+ */
+ public static function verify($token = '', $action = null)
+ {
+ if (!is_string($token) || !is_string($action)) {
+ return false;
+ }
+
+ $known = self::generate($action);
+ return hash_equals($known, $token);
+ }
+
+ /**
+ * Generate a CSRF token for a given action.
+ *
+ * @param string $action
+ * @throws InvalidArgumentException
+ * @return string
+ */
+ public static function generate($action = null)
+ {
+ if (!is_string($action)) {
+ throw new InvalidArgumentException('A valid action must be defined.');
+ }
+ return hash_hmac(self::HMAC_ALGORITHM, $action, self::getKey());
+ }
+
+ /**
+ * Get HMAC key.
+ *
+ * @return string
+ */
+ public static function getKey()
+ {
+ if (empty($_SESSION[self::SESSION_KEY_NAME])) {
+ $_SESSION[self::SESSION_KEY_NAME] = random_bytes(16);
+ }
+ return $_SESSION[self::SESSION_KEY_NAME];
+ }
+}
diff --git a/common/app/classes/Cache.php b/common/app/classes/Cache.php
new file mode 100644
index 0000000..b73182e
--- /dev/null
+++ b/common/app/classes/Cache.php
@@ -0,0 +1,254 @@
+<?php
+/**
+* This library is free software; you can redistribute it and/or modify
+* it under the terms of the GNU General Public License as published by
+* the Free Software Foundation; either version 2 of the License, or
+* (at your option) any later version.
+*
+* This library is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+* GNU General Public License for more details.
+*
+* You should have received a copy of the GNU General Public License
+* along with this software; if not, write to the Free Software
+* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+*
+* © Copyright 2005 Richard Heyes
+*/
+
+/**
+* Caching Libraries for PHP5
+*
+* Handles data and output caching. Defaults to /dev/shm
+* (shared memory). All methods are static.
+*
+* Eg: (output caching)
+*
+* if (!OutputCache::Start('group', 'unique id', 600)) {
+*
+* // ... Output
+*
+* OutputCache::End();
+* }
+*
+* Eg: (data caching)
+*
+* if (!$data = DataCache::Get('group', 'unique id')) {
+*
+* $data = time();
+*
+* DataCache::Put('group', 'unique id', 10, $data);
+* }
+*
+* echo $data;
+*/
+ class Cache
+ {
+ /**
+ * Whether caching is enabled
+ * @var bool
+ */
+ public static $enabled = true;
+
+ /**
+ * Place to store the cache files
+ * @var string
+ */
+ protected static $store = '/dev/shm/';
+
+ /**
+ * Prefix to use on cache files
+ * @var string
+ */
+ protected static $prefix = 'cache_';
+
+ /**
+ * Stores data
+ *
+ * @param string $group Group to store data under
+ * @param string $id Unique ID of this data
+ * @param int $ttl How long to cache for (in seconds)
+ */
+ protected static function write($group, $id, $ttl, $data)
+ {
+ $filename = self::getFilename($group, $id);
+
+ if (self::$enabled && $fp = fopen($filename, 'xb')) {
+
+ if (flock($fp, LOCK_EX)) {
+ fwrite($fp, $data);
+ }
+ fclose($fp);
+
+ // Set filemtime
+ touch($filename, time() + $ttl);
+ }
+ }
+
+ /**
+ * Reads data
+ *
+ * @param string $group Group to store data under
+ * @param string $id Unique ID of this data
+ */
+ protected static function read($group, $id)
+ {
+ $filename = self::getFilename($group, $id);
+
+ return file_get_contents($filename);
+ }
+
+ /**
+ * Determines if an entry is cached
+ *
+ * @param string $group Group to store data under
+ * @param string $id Unique ID of this data
+ */
+ protected static function isCached($group, $id)
+ {
+ $filename = self::getFilename($group, $id);
+
+ if (self::$enabled && file_exists($filename) && filemtime($filename) > time()) {
+ return true;
+ }
+
+ @unlink($filename);
+
+ return false;
+ }
+
+ /**
+ * Builds a filename/path from group, id and
+ * store.
+ *
+ * @param string $group Group to store data under
+ * @param string $id Unique ID of this data
+ */
+ protected static function getFilename($group, $id)
+ {
+ $id = md5($id);
+
+ return self::$store . self::$prefix . "{$group}_{$id}";
+ }
+
+ /**
+ * Sets the filename prefix to use
+ *
+ * @param string $prefix Filename Prefix to use
+ */
+ public static function setPrefix($prefix)
+ {
+ self::$prefix = $prefix;
+ }
+
+ /**
+ * Sets the store for cache files. Defaults to
+ * /dev/shm. Must have trailing slash.
+ *
+ * @param string $store The dir to store the cache data in
+ */
+ public static function setStore($store)
+ {
+ self::$store = $store;
+ }
+ }
+
+ /**
+ * Output Cache extension of base caching class
+ */
+ class OutputCache extends Cache
+ {
+ /**
+ * Group of currently being recorded data
+ * @var string
+ */
+ private static $group;
+
+ /**
+ * ID of currently being recorded data
+ * @var string
+ */
+ private static $id;
+
+ /**
+ * Ttl of currently being recorded data
+ * @var int
+ */
+ private static $ttl;
+
+ /**
+ * Starts caching off. Returns true if cached, and dumps
+ * the output. False if not cached and start output buffering.
+ *
+ * @param string $group Group to store data under
+ * @param string $id Unique ID of this data
+ * @param int $ttl How long to cache for (in seconds)
+ * @return bool True if cached, false if not
+ */
+ public static function Start($group, $id, $ttl)
+ {
+ if (self::isCached($group, $id)) {
+ echo self::read($group, $id);
+ return true;
+
+ } else {
+
+ ob_start();
+
+ self::$group = $group;
+ self::$id = $id;
+ self::$ttl = $ttl;
+
+ return false;
+ }
+ }
+
+ /**
+ * Ends caching. Writes data to disk.
+ */
+ public static function End()
+ {
+ $data = ob_get_contents();
+ ob_end_flush();
+
+ self::write(self::$group, self::$id, self::$ttl, $data);
+ }
+ }
+
+ /**
+ * Data cache extension of base caching class
+ */
+ class DataCache extends Cache
+ {
+
+ /**
+ * Retrieves data from the cache
+ *
+ * @param string $group Group this data belongs to
+ * @param string $id Unique ID of the data
+ * @return mixed Either the resulting data, or null
+ */
+ public static function Get($group, $id)
+ {
+ if (self::isCached($group, $id)) {
+ return unserialize(self::read($group, $id));
+ }
+
+ return null;
+ }
+
+ /**
+ * Stores data in the cache
+ *
+ * @param string $group Group this data belongs to
+ * @param string $id Unique ID of the data
+ * @param int $ttl How long to cache for (in seconds)
+ * @param mixed $data The data to store
+ */
+ public static function Put($group, $id, $ttl, $data)
+ {
+ self::write($group, $id, $ttl, serialize($data));
+ }
+ }
+?> \ No newline at end of file
diff --git a/common/app/classes/Opml.php b/common/app/classes/Opml.php
new file mode 100644
index 0000000..ae9e8b1
--- /dev/null
+++ b/common/app/classes/Opml.php
@@ -0,0 +1,70 @@
+<?php
+
+class Opml
+{
+ var $_xml = null;
+ var $_currentTag = '';
+
+ var $title = '';
+ var $entries = array();
+ var $map =
+ array(
+ 'URL' => 'website',
+ 'HTMLURL' => 'website',
+ 'TEXT' => 'name',
+ 'TITLE' => 'name',
+ 'XMLURL' => 'feed',
+ 'DESCRIPTION' => 'description',
+ 'ISDOWN' => 'isDown'
+ );
+
+
+ function parse($data)
+ {
+ $this->_xml = xml_parser_create('UTF-8');
+ //xml_parser_set_option($this->_xml, XML_OPTION_CASE_FOLDING, false);
+ //xml_parser_set_option($this->_xml, XML_OPTION_SKIP_WHITE, true);
+ xml_set_object($this->_xml, $this);
+ xml_set_element_handler($this->_xml,'_openTag','_closeTag');
+ xml_set_character_data_handler ($this->_xml, '_cData');
+
+ xml_parse($this->_xml,$data);
+ xml_parser_free($this->_xml);
+ return $this->entries;
+ }
+
+
+ function _openTag($p,$tag,$attrs)
+ {
+ $this->_currentTag = $tag;
+
+ if ($tag == 'OUTLINE')
+ {
+ $i = count($this->entries);
+ foreach (array_keys($this->map) as $key)
+ {
+ if (isset($attrs[$key])) {
+ $this->entries[$i][$this->map[$key]] = $attrs[$key];
+ }
+ }
+ }
+ }
+
+ function _closeTag($p, $tag){
+ $this->_currentTag = '';
+ }
+
+ function _cData($p, $cdata){
+ if ($this->_currentTag == 'TITLE'){
+ $this->title = $cdata;
+ }
+ }
+
+ function getTitle(){
+ return $this->title;
+ }
+
+ function getPeople(){
+ return $this->entries;
+ }
+} \ No newline at end of file
diff --git a/common/app/classes/OpmlManager.php b/common/app/classes/OpmlManager.php
new file mode 100644
index 0000000..d3940b2
--- /dev/null
+++ b/common/app/classes/OpmlManager.php
@@ -0,0 +1,50 @@
+<?php
+
+
+class OpmlManager
+{
+ public static function load($file)
+ {
+ if (!file_exists($file)) {
+ throw new Exception('OPML file not found!');
+ }
+
+ $opml = new Opml();
+
+ //Remove BOM if needed
+ $BOM = '/^/';
+ $fileContent = file_get_contents($file);
+ $fileContent = preg_replace($BOM, '', $fileContent, 1);
+
+ //Parse
+ $opml->parse($fileContent);
+
+ return $opml;
+ }
+
+ /**
+ * @param Opml $opml
+ * @param string $file
+ */
+ public static function save($opml, $file){
+ $out = '<?xml version="1.0"?>'."\n";
+ $out.= '<opml version="1.1">'."\n";
+ $out.= '<head>'."\n";
+ $out.= '<title>'.htmlspecialchars($opml->getTitle()).'</title>'."\n";
+ $out.= '<dateCreated>'.date('c').'</dateCreated>'."\n";
+ $out.= '<dateModified>'.date('c').'</dateModified>'."\n";
+ $out.= '</head>'."\n";
+ $out.= '<body>'."\n";
+ foreach ($opml->entries as $person) {
+ $out.= '<outline text="' . htmlspecialchars($person['name'], ENT_QUOTES) . '" htmlUrl="' . htmlspecialchars($person['website'], ENT_QUOTES) . '" xmlUrl="' . htmlspecialchars($person['feed'], ENT_QUOTES) . '" isDown="' . htmlspecialchars($person['isDown'], ENT_QUOTES) . '"/>'."\n";
+ }
+ $out.= '</body>'."\n";
+ $out.= '</opml>';
+
+ file_put_contents($file, $out);
+ }
+
+ public static function backup($file){
+ copy($file, $file.'.bak');
+ }
+}
diff --git a/common/app/classes/Planet.php b/common/app/classes/Planet.php
new file mode 100644
index 0000000..b146eaf
--- /dev/null
+++ b/common/app/classes/Planet.php
@@ -0,0 +1,266 @@
+<?php
+/*
+Copyright (c) 2006, Maurice Svay
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+* Redistributions of source code must retain the above copyright notice,
+this list of conditions and the following disclaimer.
+* Redistributions in binary form must reproduce the above copyright
+notice, this list of conditions and the following disclaimer in the
+documentation and/or other materials provided with the distribution.
+* Neither the name of Maurice Svay nor the names of its
+contributors may be used to endorse or promote products derived from
+this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
+IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
+PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
+OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+/**
+ * Planet, main app class
+ */
+class Planet
+{
+ /** @var PlanetConfig */
+ public $config;
+
+ /** @var PlanetItem[] */
+ public $items;
+
+ /** @var PlanetFeed[] */
+ public $people;
+
+ /** @var PlanetError[] */
+ public $errors;
+
+ /**
+ * Planet constructor.
+ *
+ * @param PlanetConfig $config
+ */
+ public function __construct($config=null)
+ {
+ $this->config = $config === null ? new PlanetConfig() : $config;
+
+ $this->items = [];
+ $this->people = [];
+ $this->errors = [];
+ }
+
+ /**
+ * Compare the supplied password with the known one.
+ *
+ * This functions uses a type-safe and timing-safe comparison, in order to
+ * improve the security of the authentication.
+ *
+ * Read more about this sort of attacks (used for the < PHP 5.6.0 implementation):
+ * - https://security.stackexchange.com/questions/83660/simple-string-comparisons-not-secure-against-timing-attacks
+ * - https://github.com/laravel/framework/blob/a1dc78820d2dbf207dbdf0f7075f17f7021c4ee8/src/Illuminate/Support/Str.php#L289
+ * - https://github.com/symfony/security-core/blob/master/Util/StringUtils.php#L39
+ *
+ * @param string $known
+ * @param string $supplied
+ * @return bool
+ */
+ public static function authenticateUser($known = '', $supplied = '')
+ {
+ // The hash_equals function was introduced in PHP 5.6.0. If it's not
+ // existing in the current context (PHP version too old), and to ensure
+ // compatibility with those old interpreters, we'll have to provide
+ // an PHP implementation of this function.
+ if (function_exists('hash_equals')) {
+ return hash_equals($known, $supplied);
+ }
+
+ // Some implementation references can be found on the function comment.
+ $knownLen = mb_strlen($known);
+ if ($knownLen !== mb_strlen($supplied)) {
+ return false;
+ }
+
+ // Ensure that all the characters are the same, and continue until the
+ // end of the string even if an difference was found.
+ for ($i = 0, $comparison = 0; $i < $knownLen; $i++) {
+ $comparison |= ord($known[$i]) ^ ord($supplied[$i]);
+ }
+
+ return ($comparison === 0);
+ }
+
+ /**
+ * Getters
+ */
+ public function getItems()
+ {
+ $this->items = $this->_filterItemsByCategory(
+ $this->items,
+ $this->config->getCategories());
+
+ return $this->items;
+ }
+
+ public function getPeople()
+ {
+ return $this->people;
+ }
+
+ /**
+ * Adds a feed to the planet.
+ *
+ * @param PlanetFeed $feed
+ */
+ public function addPerson(&$feed)
+ {
+ $this->people[] = $feed;
+ }
+
+ /**
+ * Load people from an OPML.
+ *
+ * @param string $file File to load the OPML from.
+ * @return integer Number of people loaded.
+ */
+ public function loadOpml($file)
+ {
+ if (!is_file($file)) {
+ $this->errors[] = new PlanetError(3, $file.' is missing.');
+ return 0;
+ }
+
+ $opml = OpmlManager::load($file);
+ $opml_people = $opml->getPeople();
+ foreach ($opml_people as $opml_person){
+ $person = new PlanetFeed(
+ $opml_person['name'],
+ $opml_person['feed'],
+ $opml_person['website'],
+ $opml_person['isDown']
+ );
+ $this->addPerson($person);
+ }
+ return count($opml_people);
+ }
+
+ /**
+ * Load feeds
+ */
+ public function loadFeeds()
+ {
+ foreach ($this->people as $feed) {
+ //Is down it's filled by cron.php, $Planet->download(1.0) proccess
+ if (!$feed->isDown) {
+ $feed->set_timeout(-1);
+ $feed->init();
+ $this->items = array_merge($this->items, $feed->get_items());
+ }
+
+ }
+ $this->sort();
+ }
+
+ /**
+ * Fetch feeds and see if new data is present.
+ *
+ * @param float $max_load Percentage of feeds to load
+ */
+ public function download($max_load=0.1)
+ {
+ $max_load_feeds = ceil(count($this->people) * $max_load);
+ $opml = OpmlManager::load(__DIR__.'/../../custom/people.opml');
+
+ foreach ($this->people as $feed) {
+ //Avoid mass loading with variable cache duration
+ $feed->set_cache_duration($this->config->getCacheTimeout());
+
+ //Load only a few feeds, force other to fetch from the cache
+ if (0 > $max_load_feeds--) {
+ $feed->set_timeout(-1);
+ $this->errors[] = new PlanetError(1, 'Forced from cache : '.$feed->getFeed());
+ }
+
+ // Bypass remote's SSL/TLS certificate if the user explicitly
+ // asked for it in the configuration.
+ if ($this->config->checkcerts === false) {
+ $feed->set_curl_options([
+ CURLOPT_SSL_VERIFYHOST => false,
+ CURLOPT_SSL_VERIFYPEER => false
+ ]);
+ }
+
+ $feed->init();
+ $isDown = '';
+
+ // http://simplepie.org/wiki/reference/simplepie/merge_items ?
+ if (($feed->data) && ($feed->get_item_quantity() > 0)){
+ $items = $feed->get_items();
+ $this->items = array_merge($this->items, $items);
+ } else {
+ $this->errors[] = new PlanetError(1, 'No items or down : ' . $feed->getFeed());
+ $isDown = '1';
+ }
+
+ foreach ($opml->entries as $key => $entrie) {
+ if ($feed->getFeed() === $entrie['feed']) {
+ $opml->entries[$key]['isDown'] = $isDown;
+ }
+ }
+ }
+
+ OpmlManager::save($opml, __DIR__.'/../../custom/people.opml');
+ }
+
+ public function sort()
+ {
+ usort($this->items, array('PlanetItem','compare'));
+ }
+
+ /**
+ * Filter out items that do not match at least one
+ * of the defined categories.
+ *
+ * If there's no category, return all items.
+ *
+ * @param array $items to filter
+ * @param string $categories to filter against; may be a single word
+ * or a comma-separated list of words.
+ *
+ * @return array resulting list of items
+ */
+ public function _filterItemsByCategory($items, $categories = null)
+ {
+ $categories = trim($categories);
+
+ if (empty($categories))
+ return $items;
+
+ $categories = array_map('trim', explode(',', strtolower($categories)));
+ $cb_category_filter =
+ function ($item) use ($categories)
+ {
+ if (!is_array($item_categories = $item->get_categories()))
+ return false;
+
+ $item_categories = array_map(
+ function ($i) { return strtolower($i->get_label()); },
+ $item_categories
+ );
+
+ return array_intersect($categories, $item_categories);
+ };
+
+ return array_values(array_filter($items, $cb_category_filter));
+ }
+}
diff --git a/common/app/classes/PlanetConfig.php b/common/app/classes/PlanetConfig.php
index a31938e..f3928bc 100644
--- a/common/app/classes/PlanetConfig.php
+++ b/common/app/classes/PlanetConfig.php
@@ -6,32 +6,45 @@
class PlanetConfig
{
- public $conf;
-
- public function __construct($array)
- {
- $defaultConfig = array(
- 'url' => 'http://www.example.com/',
- 'name' => '',
- 'locale' => 'en',
- 'items' => 10,
- 'shuffle' => 0,
- 'refresh' => 240,
- 'cache' => 10,
- 'nohtml' => 0,
- 'postmaxlength' => 0,
- 'cachedir' => './cache',
- );
-
- // User config
- $this->conf = $array;
-
- // Complete config with default config
- foreach ($defaultConfig as $key => $value) {
- if (!isset($this->conf[$key])) {
- $this->conf[$key] = $value;
- }
- }
+ protected $conf = [];
+
+ public static $defaultConfig = [
+ 'url' => 'http://www.example.com/',
+ 'name' => '',
+ 'locale' => 'en',
+ 'items' => 10,
+ 'shuffle' => 0,
+ 'refresh' => 240,
+ 'cache' => 10,
+ 'nohtml' => 0,
+ 'postmaxlength' => 0,
+ 'categories' => '',
+ 'cachedir' => './cache',
+ 'debug' => false,
+ 'checkcerts' => true,
+ ];
+
+ /**
+ * PlanetConfig constructor.
+ * @param array $userConfig
+ * @param bool $useDefaultConfig
+ */
+ public function __construct($userConfig = [], $useDefaultConfig = true)
+ {
+ $default = $useDefaultConfig ? self::$defaultConfig : array();
+ $this->conf = $this->merge($default, $userConfig);
+ }
+
+ /**
+ * Merge the configuration of the user in the default one.
+ *
+ * @param array $default
+ * @param array $user
+ * @return array
+ */
+ protected function merge($default = [], $user = [])
+ {
+ return array_merge($default, $this->normalizeArrayKeys($user));
}
public function getUrl()
@@ -58,7 +71,10 @@ class PlanetConfig
return $this->conf['cache'];
}
- //@TODO: drop this pref
+ /**
+ * @deprecated
+ * @return mixed
+ */
public function getShuffle()
{
return $this->conf['shuffle'];
@@ -69,20 +85,110 @@ class PlanetConfig
return $this->conf['items'];
}
- //@TODO: drop this pref
+ /**
+ * @deprecated
+ * @return mixed
+ */
public function getNoHTML()
{
return $this->conf['nohtml'];
}
- //@TODO: drop this pref
+ /**
+ * @deprecated
+ * @return mixed
+ */
public function getPostMaxLength()
{
return $this->conf['postmaxlength'];
}
+ public function getCategories()
+ {
+ return $this->conf['categories'];
+ }
+
public function toYaml()
{
- return Spyc::YAMLDump($this->conf,4);
+ return Spyc::YAMLDump($this->conf, 4);
+ }
+
+ /**
+ * @return array
+ */
+ public function toArray()
+ {
+ return $this->conf;
+ }
+
+ public function getDebug()
+ {
+ return $this->conf['debug'];
+ }
+
+ /**
+ * @return array
+ */
+ public function getDefaultConfig()
+ {
+ return self::$defaultConfig;
+ }
+
+ /**
+ * Normalize the name of a configuration key.
+ *
+ * @param string $key
+ * @return string
+ */
+ protected function normalizeKeyName($key = null)
+ {
+ return strtolower($key);
+ }
+
+ /**
+ * Normalize all the keys of the array.
+ *
+ * @param array $array
+ * @return array
+ */
+ protected function normalizeArrayKeys($array = [])
+ {
+ foreach ($array as $key => $value) {
+ $normalized = $this->normalizeKeyName($key);
+ if ($normalized !== $key) {
+ $array[$this->normalizeKeyName($key)] = $value;
+ unset($array[$key]);
+ }
+ }
+
+ return $array;
}
+
+ /**
+ * Generic configuration getter.
+ *
+ * @return mixed|null
+ */
+ public function __get($key)
+ {
+ $key = $this->normalizeKeyName($key);
+
+ return array_key_exists($key, $this->conf) ?
+ $this->conf[$key] :
+ null;
+ }
+
+ /**
+ * Generic configuration setter.
+ *
+ * @param $key
+ * @param $value
+ */
+ public function __set($key, $value)
+ {
+ $key = $this->normalizeKeyName($key);
+
+ $this->conf[$key] = $value;
+ }
+
}
diff --git a/common/app/classes/PlanetError.php b/common/app/classes/PlanetError.php
index 31923a3..e47ab1d 100644
--- a/common/app/classes/PlanetError.php
+++ b/common/app/classes/PlanetError.php
@@ -3,21 +3,30 @@
class PlanetError
{
public $level;
+ public $levels = array(
+ 1 => 'notice',
+ 2 => 'warning',
+ 3 => 'error',
+ );
public $message;
+ /**
+ * PlanetError constructor.
+ * @param $level
+ * @param $message
+ */
public function __construct($level, $message)
{
$this->level = (int) $level;
$this->message = $message;
}
- public function toString($format = '%1$s : %2$s')
+ /**
+ * @param string $format
+ * @return string
+ */
+ public function toString($format = '%1$s: %2$s')
{
- $levels = array(
- 1 => 'notice',
- 2 => 'warning',
- 3 => 'error',
- );
- return sprintf($format, $levels[$this->level], $this->message);
+ return sprintf($format, $this->levels[$this->level], $this->message);
}
}
diff --git a/common/app/classes/PlanetFeed.php b/common/app/classes/PlanetFeed.php
index a6c7aab..3d2ea2f 100644
--- a/common/app/classes/PlanetFeed.php
+++ b/common/app/classes/PlanetFeed.php
@@ -9,15 +9,17 @@ class PlanetFeed extends SimplePie
public $name;
public $feed;
public $website;
+ public $isDown;
- public function __construct($name, $feed, $website)
+ public function __construct($name, $feed, $website, $isDown)
{
$this->name = $name;
$this->feed = $feed;
$this->website = $website;
+ $this->isDown = $isDown;
parent::__construct();
$this->set_item_class('PlanetItem');
- $this->set_cache_location(dirname(__FILE__).'/../../cache');
+ $this->set_cache_location(__DIR__.'/../../cache');
$this->set_autodiscovery_level(SIMPLEPIE_LOCATOR_NONE);
$this->set_feed_url($this->getFeed());
$this->set_timeout(5);
@@ -39,7 +41,19 @@ class PlanetFeed extends SimplePie
return $this->website;
}
- public function compare($person1, $person2)
+ public function getIsDown()
+ {
+ return $this->isDown;
+ }
+
+ /**
+ * Compare two Person by their name.
+ *
+ * @param $person1
+ * @param $person2
+ * @return int
+ */
+ public static function compare($person1, $person2)
{
return strcasecmp($person1->name, $person2->name);
}
diff --git a/common/app/classes/PlanetItem.php b/common/app/classes/PlanetItem.php
index 039d0ca..7d55781 100644
--- a/common/app/classes/PlanetItem.php
+++ b/common/app/classes/PlanetItem.php
@@ -4,14 +4,19 @@
* Planet item
*/
-class PlanetItem
+class PlanetItem extends SimplePie_Item
{
public function __construct($feed, $data)
{
- parent::SimplePie_Item($feed, $data);
+ parent::__construct($feed, $data);
}
- public function compare($item1, $item2)
+ /**
+ * @param PlanetItem $item1
+ * @param PlanetItem $item2
+ * @return int
+ */
+ public static function compare($item1, $item2)
{
$item1_date = $item1->get_date('U');
$item2_date = $item2->get_date('U');
diff --git a/common/app/classes/Simplel10n.php b/common/app/classes/Simplel10n.php
new file mode 100755
index 0000000..79313b3
--- /dev/null
+++ b/common/app/classes/Simplel10n.php
@@ -0,0 +1,52 @@
+<?php
+
+
+class Simplel10n {
+
+ public $locale;
+ public $l10nFolder;
+
+ public function __construct($locale='en') {
+ $GLOBALS['locale'] = array();
+ $this->locale = $locale;
+ $this->l10nFolder = __DIR__ . '/../l10n/';
+ $this->load($this->l10nFolder . $this->locale);
+ }
+
+ public function setL1OnFolder($path) {
+ $this->l10nFolder = $path;
+ }
+
+ static function getString($str, $comment='') {
+ if(array_key_exists($str, $GLOBALS['locale'])) {
+ return trim(str_replace('{ok}', '', $GLOBALS['locale'][$str]));
+ } else {
+ return $str;
+ }
+ }
+
+ /*
+ * This is the same as getString except that we don't remove the {ok} string
+ * This is needed only for the extraction script
+ */
+ static function extractString($str, $comment='') {
+ if(array_key_exists($str, $GLOBALS['locale'])) {
+ return $GLOBALS['locale'][$str];
+ } else {
+ return $str;
+ }
+ }
+
+ static function load($pathToFile) {
+
+ if (!file_exists($pathToFile . '.lang')) return false;
+
+ $file = file($pathToFile . '.lang');
+
+ foreach ($file as $k => $v) {
+ if (substr($v,0,1) == ';' && !empty($file[$k+1])) {
+ $GLOBALS['locale'][trim(substr($v,1))] = trim($file[$k+1]);
+ }
+ }
+ }
+}
diff --git a/common/app/helpers.php b/common/app/helpers.php
new file mode 100644
index 0000000..e943252
--- /dev/null
+++ b/common/app/helpers.php
@@ -0,0 +1,130 @@
+<?php
+
+/**
+ * Register polyfills for old PHP versions.
+ *
+ * This way, the real function will only be called if it
+ * is available, and we won't force the use of our own
+ * implementation.
+ */
+function register_polyfills()
+{
+ if (!function_exists('hash_equals')) {
+ function hash_equals($known_string, $user_string) {
+ call_user_func_array('_hash_equals', func_get_args());
+ }
+ }
+
+ if (!function_exists('random_bytes')) {
+ // If this function does not exist, it will be exposed
+ // automatically by paragonie/random_compat.
+ }
+}
+
+register_polyfills();
+
+/**
+ * Path to the _custom_ directory.
+ *
+ * @param string $file Append this filename to the returned path.
+ * @return string
+ */
+function custom_path($file = '')
+{
+ return __DIR__.'/../custom' . (!empty($file) ? '/'.$file : '');
+}
+
+/**
+ * Path to the _views_ directory.
+ *
+ * @param string $file Append this filename to the returned path.
+ * @return string
+ */
+function views_path($file = '')
+{
+ return custom_path('views/' . $file);
+}
+
+/**
+ * Path to the _admin_ directory.
+ *
+ * @param string $file Append this filename to the returned path.
+ * @return string
+ */
+function admin_path($file = '')
+{
+ return __DIR__.'/../admin' . (!empty($file) ? '/'.$file : '');
+}
+
+/**
+ * Is moonmoon installed?
+ *
+ * @return bool
+ */
+function is_installed()
+{
+ return file_exists(custom_path('config.yml')) && file_exists(custom_path('people.opml'));
+}
+
+/**
+ * Shortcut to Simplel10n::getString().
+ *
+ * @param string $str
+ * @param string $comment
+ * @return string
+ */
+function _g($str, $comment='')
+{
+ return Simplel10n::getString($str, $comment);
+}
+
+/**
+ * Reset the moonmoon instance.
+ */
+function removeCustomFiles()
+{
+ $toRemove = [
+ custom_path('config.yml'),
+ custom_path('people.opml'),
+ custom_path('people.opml.bak'),
+ custom_path('cache')
+ ];
+
+ foreach ($toRemove as $path) {
+ if (file_exists($path)) {
+ unlink($path);
+ }
+ }
+}
+
+/**
+ * Compare two strings in a constant-time manner.
+ *
+ * It returns `true` if both strings are exactly the same
+ * (same size and same value).
+ *
+ * @param string $known_string
+ * @param string $user_string
+ * @return bool
+ */
+function _hash_equals($known_string = '', $user_string = '')
+{
+ // In our case, it's not problematic if `$known_string`'s
+ // size leaks, we will only compare password hashes and
+ // CSRF tokens—their size is already somehow public.
+ if (!is_string($known_string) || !is_string($user_string)
+ || strlen($known_string) !== strlen($user_string)) {
+ return false;
+ }
+
+ $ret = 0;
+
+ // Do not stop the comparison when a difference is found,
+ // always completely compare them.
+ for ($i = 0; $i < strlen($known_string); $i++) {
+ $ret |= (ord($known_string[$i]) ^ ord($user_string[$i]));
+ }
+
+ return !$ret;
+}
+
diff --git a/common/app/l10n/de.lang b/common/app/l10n/de.lang
new file mode 100644
index 0000000..628a12c
--- /dev/null
+++ b/common/app/l10n/de.lang
@@ -0,0 +1,219 @@
+;Are you sure you want to purge the cache?
+Sind Sie sicher, dass sie den Cache säubern wollen?
+
+
+;Clear cache
+Cache Löschen
+
+
+;Clear cache:
+Cache löschen:
+
+
+;Clear
+Löschen
+
+
+;Clearing the cache will make moonmoon reload all feeds.
+Wenn der Cache gelöscht wird, werden alle Feeds von moonmoon neu geladen.
+
+
+;Change administrator password
+Administratorpasswort ändern
+
+
+;New password:
+Neues Passwort:
+
+
+;Change password
+Passwort ändern
+
+
+;Add Feed
+Feed hinzufügen
+
+
+;Link:
+Link:
+
+
+;Accepted formats are RSS and ATOM. If the link is not a feed, moonmoon will try to autodiscover the feed.
+Akzeptierte Formate sind RSS und ATOM. Wenn der link kein Feed ist, wird moonmoon versuchen, den Feed selbst zu finden.
+
+
+;Manage existing feeds
+Verwalte bestehende Feeds
+
+
+;Number of feeds: %s
+Anzahl der feeds: %s
+
+
+;Save changes
+Änderungen Speichern
+
+
+;Delete selected Feeds
+Ausgewählte Feeds löschen
+
+
+;Select :
+Auswählen
+
+
+;All
+Alle
+
+
+;None
+Nichts
+
+
+;Selection
+Auswahl
+
+
+;Name
+Name
+
+
+;Last entry
+Letzter Eintrag
+
+
+;Website link
+Link der Webseite
+
+
+;Feed link
+Link des Feeds
+
+
+;Unavailable
+Nicht verfügbar
+
+;Not in cache
+Nicht im cache
+
+
+;Password:
+Passwort:
+
+
+;OK
+OK
+
+
+;moonmoon administration
+moonmoon Administration
+
+
+;Back to main page
+Zurück zur Hauptseite
+
+
+;Logout
+Abmelden
+
+
+;Feeds
+Feeds
+
+
+;Administration
+Administration
+
+
+;Powered by <a %s>moonmoon</a>
+Powered by <a %s>moonmoon</a>
+
+
+;No article
+Kein Artikel
+
+
+;No news, good news.
+Keine Neuigkeiten, gute Neuigkeiten.
+
+
+;Today
+Heute
+
+
+;Go to original place
+Gehe zur Originalseite
+
+
+;This week
+Diese Woche
+
+
+;This month
+Diesen Monat
+
+
+;Older items
+Ältere Einträge
+
+
+;People
+Personen
+
+
+;Feed
+Feed
+
+
+;Website
+Webseite
+
+
+;All feeds in OPML format
+Alle Feeds im OPML-Format
+
+
+;Syndicate
+Zusammenfassung
+
+
+;Feed (ATOM)
+Feed (ATOM)
+
+
+;Archives
+Archive
+
+
+;See all headlines
+Alle Schlagzeilen ansehen
+
+
+;Source:
+Quelle:
+
+
+;You might want to <a href="install.php">install moonmoon</a>.
+Sie könnten <a href="install.php">moonmoon installieren</a> wollen.
+
+
+;moonmoon installation
+moonmoon Installation
+
+
+;Congratulations! Your moonmoon is ready.
+Gratulation! Ihr moonmoon ist bereit.
+
+
+;What's next?
+Was nun?
+
+
+;<strong>Delete</strong> <code>install.php</code> with your FTP software.
+<strong>Entfernen Sie</strong> <code>install.php</code> mit Ihrer FTP-Software.
+
+
+;Use your password to go to the <a href="./admin/">administration panel</a>
+Verwenden Sie Ihr Passwort, um zum <a href="./admin/">Administrationsbereich</a> zu gehen.
+
+
diff --git a/common/app/l10n/en.lang b/common/app/l10n/en.lang
index 64d5a82..ff49307 100644
--- a/common/app/l10n/en.lang
+++ b/common/app/l10n/en.lang
@@ -114,6 +114,10 @@ Feed link
# Translation note: ** String needs translation **
+;Unavailable
+Unavailable
+
+# Translation note: ** String needs translation **
;Not in cache
Not in cache
diff --git a/common/app/l10n/es.lang b/common/app/l10n/es.lang
new file mode 100644
index 0000000..11b80df
--- /dev/null
+++ b/common/app/l10n/es.lang
@@ -0,0 +1,244 @@
+# Translation note: ¿Está seguro que desea vaciar la caché?
+;Are you sure you want to purge the cache?
+&iquest;Est&aacute; seguro de que desea vaciar la cach&eacute;?
+
+
+# Translation note: Vaciar la caché
+;Clear cache
+Vaciar la cach&eacute;
+
+
+# Translation note: Vaciar la caché:
+;Clear cache:
+Vaciar la cach&eacute;:
+
+
+;Clear
+Vaciar
+
+
+# Translation note: Vaciar la caché hará que moonmoon recargue todas las fuentes web.
+;Clearing the cache will make moonmoon reload all feeds.
+Vaciar la cach&eacute; har&aacute; que moonmoon recargue todas las fuentes web.
+
+
+# Translation note: Cambiar la contraseña de administración
+;Change administrator password
+Cambiar la contrase&ntilde;a de administraci&oacute;n
+
+
+# Translation note: Nueva contraseña:
+;New password:
+Nueva contrase&ntilde;a:
+
+
+# Translation note: Cambiar la contraseña
+;Change password
+Cambiar la contrase&ntilde;a
+
+
+# Translation note: Añadir una nueva fuente web - 'feed' = 'fuente web', 'fuente' or 'canal' in spanish, but you can use 'feed' anyway.
+;Add a new feed
+A&ntilde;adir una nueva fuente web
+
+
+# Translation note: Añadir fuente
+;Add Feed
+A&ntilde;adir fuente
+
+
+;Link:
+Enlace:
+
+
+# Translation note: Los formatos aceptados son RSS y ATOM. Si el enlace no es una fuente web, moonmoon tratará de encontrar la fuente de forma automática.
+;Accepted formats are RSS and ATOM. If the link is not a feed, moonmoon will try to autodiscover the feed.
+Los formatos aceptados son RSS y ATOM. Si el enlace no es una fuente web, moonmoon tratar&aacute; de encontrar la fuente de forma autom&aacute;tica.
+
+
+;Manage existing feeds
+Administrar las fuentes existentes
+
+
+# Translation note: Número de fuentes
+;Number of feeds: %s
+N&uacute;mero de fuentes: %s
+
+
+;Save changes
+Guardar cambios
+
+
+;Delete selected Feeds
+Borrar seleccionadas
+
+
+;Select :
+Seleccionar:
+
+
+;All
+Todas
+
+
+;None
+Ninguna
+
+
+# Translation note: Selección
+;Selection
+Selecci&oacute;n
+
+
+;Name
+Nombre
+
+# Translation note: Última entrada
+;Last entry
+&Uacute;ltima entrada
+
+
+;Website link
+Enlace a la web
+
+
+;Feed link
+Enlace a la fuente
+
+
+# Translation note: 'No está en caché' or 'No en caché'
+;Not in cache
+No est&aacute; en cach&eacute;
+
+
+# Translation note: Contraseña:
+;Password:
+Contrase&ntilde;a:
+
+
+;OK
+Entrar
+
+
+# Translation note: Administración de moonmoon
+;moonmoon administration
+Administraci&oacute;n de moonmoon
+
+
+# Translation note: Volver a la página principal
+;Back to main page
+Volver a la p&aacute;gina principal
+
+
+;Logout
+Salir
+
+
+;Feeds
+Fuentes
+
+
+# Translation note: Administración
+;Administration
+Administraci&oacute;n
+
+
+;Powered by <a %s>moonmoon</a>
+Desarrollado por <a %s>moonmoon</a>
+
+
+# Translation note: Sin artículos
+;No article
+Sin art&iacute;culos
+
+
+;No news, good news.
+No hay noticias, buena noticia.
+
+
+;Today
+Hoy
+
+
+# Translation note: Ir a la ubicación original
+;Go to original place
+Ir a la ubicaci&oacute;n original
+
+
+;This week
+Esta semana
+
+
+;This month
+Este mes
+
+
+;Older items
+Titulares anteriores
+
+
+# Translation note: 'People' = 'Personas', but 'fuentes'(sources), which in Spanish means origins, is more generic.
+;People
+Fuentes
+
+
+;Feed
+Fuente
+
+
+;Website
+Sitio web
+
+
+;All feeds in OPML format
+Todas las fuentes (OPML)
+
+
+# Translation note: Redifusión
+;Syndicate
+Redifusi&oacute;n
+
+
+;Feed (ATOM)
+Fuente (ATOM)
+
+
+;Archives
+Archivos
+
+
+;See all headlines
+Ver todos los titulares
+
+
+# Translation note: You may use 'Source' = 'Origen' too.
+;Source:
+Fuente:
+
+
+;You might want to <a href="install.php">install moonmoon</a>.
+Es posible que desee <a href="install.php">instalar moonmoon</a>.
+
+
+# Translation note: Instalación de moonmoon
+;moonmoon installation
+Instalaci&oacute;n de moonmoon
+
+
+# Translation note: ¡Felicidades! Su moonmoon está preparado.
+;Congratulations! Your moonmoon is ready.
+&iexcl;Felicidades! Su moonmoon est&aacute; preparado.
+
+
+# Translation note: ¿Y ahora?
+;What's next?
+&iquest;Y ahora?
+
+
+;<strong>Delete</strong> <code>install.php</code> with your FTP software.
+<strong>Borre</strong> <code>install.php</code> con su programa de FTP.
+
+
+# Translation note: Utilice su contraseña para acceder al panel de administración.
+;Use your password to go to the <a href="./admin/">administration panel</a>
+Utilice su contrase&ntilde;a para acceder al <a href="./admin/">panel de administraci&oacute;n</a>.
diff --git a/common/app/l10n/extract.php b/common/app/l10n/extract.php
index 1a04f00..1384827 100755
--- a/common/app/l10n/extract.php
+++ b/common/app/l10n/extract.php
@@ -13,13 +13,15 @@
* The script scans for the files in the l10n/ folder to know which locales are supported
*/
-// released versions of moonmoon should immediately return for security
-// return;
+$root = __DIR__ . '/../../';
-$root = dirname(__FILE__) . '/../../';
-$GLOBALS['english'] = array();
+require_once __DIR__ . '/../app.php';
-include_once $root . '/app/classes/Simplel10n.class.php';
+if ($conf['debug'] !== true) {
+ die('Please enable the debug mode to use extract.php.');
+}
+
+$GLOBALS['english'] = array();
/*
* This is a file parser to extract localizable strings (in .php files)
@@ -27,7 +29,6 @@ include_once $root . '/app/classes/Simplel10n.class.php';
*
*/
-
function extract_l10n_strings($file) {
$lines = file($file);
$patterns = array('/_g\([\'"](.*?)[\'"]\)/', '/getString\([\'"](.*?)[\'"]\)/',);
diff --git a/common/app/l10n/fr.lang b/common/app/l10n/fr.lang
index 6247652..4e2a568 100755
--- a/common/app/l10n/fr.lang
+++ b/common/app/l10n/fr.lang
@@ -90,6 +90,10 @@ Lien du site
Lien du Flux
+;Unavailable
+Indisponible
+
+
;Not in cache
Pas en cache