diff options
Diffstat (limited to 'common')
56 files changed, 3953 insertions, 355 deletions
diff --git a/common/CONTRIBUTING.md b/common/CONTRIBUTING.md new file mode 100644 index 0000000..1402a0c --- /dev/null +++ b/common/CONTRIBUTING.md @@ -0,0 +1,37 @@ +# Contributing  + +First of all, thanks a lot for considering to contribute! + +## Found a bug? + +* Nice catch! +* Make sure that this bug is not already present in our  +  [Issues](https://github.com/moonmoon/moonmoon/issues) tracker. +* If none matches, please  +  [create a new ticket](https://github.com/moonmoon/moonmoon/issues/new). + +## Fixing a bug with a patch you made? + +* Awesome! +* If the bug you are fixing is present in our  +  [Issues tracker](https://github.com/moonmoon/moonmoon/issues), don't +  forget to mention the related issue. +* Make sure to stick to the [PSR-2](http://www.php-fig.org/psr/psr-2/) +  coding style. + +## Adding a new feature?  + +* Marvelous! +* If it's not too late, please create a new ticket in the  +  [Issues tracker](https://github.com/moonmoon/moonmoon/issues/new) before +  developing something. Maybe it does not really fit into moonmoon and +  we may not merge it :(  +* Make sure to stick to the [PSR-2](http://www.php-fig.org/psr/psr-2/) +  coding style. + +## Seeking support ? + +* Is your problem already listed in our [FAQ](https://github.com/moonmoon/moonmoon/wiki/Faq)? +* If not, please create a ticket in our  +  [Issues tracker](https://github.com/moonmoon/moonmoon/issues/new). +* You more details you give (version, steps to reproduce…), the better we'll help you :-) diff --git a/common/LICENSE b/common/LICENSE index 8d83edd..bb0603a 100644 --- a/common/LICENSE +++ b/common/LICENSE @@ -30,3 +30,4 @@ Contributors:  François Granger  Pep, http://www.callmepep.org/  Pascal Chevrel +Javier Guerra diff --git a/common/README.md b/common/README.md new file mode 100644 index 0000000..0fcfda0 --- /dev/null +++ b/common/README.md @@ -0,0 +1,66 @@ +<p align="center"> +  <img src="https://github.com/moonmoon/moonmoon/raw/master/custom/img/moonmoon%40128w.png"> +</p> + + +moonmoon [](https://travis-ci.org/moonmoon/moonmoon) +======== + +Moonmoon is a web based aggregator similar to planetplanet. +It can be used to blend articles from different blogs with same interests into a single page. + +Moonmoon is simple: it only aggregates feeds and spits them out in one single page. +It does not archive articles, it does not do comments nor votes. + +Requirements +------------ +You will need a web hosting with at least PHP 5.6 (PHP 7 is also supported). + +If you are installing moonmoon on a Linux private server (VPS, dedicated host), +please note that you will need to install the package `php-xml`. + +Installing +---------- + +Installation steps (shared hosting or virtual / dedicated server) can be found +[in the wiki](https://github.com/moonmoon/moonmoon/wiki/How-to-install). + +Docker images are also available in [moonmoon/docker-images](https://github.com/moonmoon/docker-images). +Theses images are probably not production-ready but should work for manual testing. + +Contributing +------------ + +You want to contribute to moonmoon? Perfect! [We wrote some guidelines to help you +craft the best Issue / Pull Request possible](https://github.com/moonmoon/moonmoon/blob/master/CONTRIBUTING.md), +don't hesitate to take a look at it :-) + +License +------- + +Moonmoon is free software and is released under the [BSD license](https://github.com/moonmoon/moonmoon/blob/master/LICENSE). +Third-party code differently licensed is included in this project, in which case mention is always made of +the applicable license. + +[The logo](https://github.com/moonmoon/moonmoon/raw/master/custom/img/moonmoon.png) was designed by [@rakujira](https://twitter.com/rakujira). + +Configuration options +--------------------- +After installation, configuration is kept in a YAML formatted `custom/config.yml`: + +```%yaml +url: http://planet.example.net  # your planet base URL +name: My Planet                 # your planet front page name +locale: en                      # front page locale +items: 10                       # how many items to show +refresh: 240                    # feeds cache timeout (in seconds) +cache: 10                       # front page cache timeout (in seconds) +cachedir: ./cache               # where is cache stored +postmaxlength: 0                # deprecated +shuffle: 0                      # deprecated +nohtml: 0                       # deprecated +categories:                     # only list posts that have one +                                # of these (tag or category) +debug: false                    # debug mode (dangerous in production!) +checkcerts: true                # check feeds certificates +``` diff --git a/common/VERSION b/common/VERSION index 38f8e88..2a8cbc6 100644 --- a/common/VERSION +++ b/common/VERSION @@ -1 +1 @@ -dev +9.0.0-rc diff --git a/common/admin/administration.php b/common/admin/administration.php index 1202e91..26f6710 100755 --- a/common/admin/administration.php +++ b/common/admin/administration.php @@ -1,9 +1,10 @@  <?php -require_once dirname(__FILE__) . '/inc/auth.inc.php'; -require_once dirname(__FILE__) . '/../app/app.php'; +require_once __DIR__ . '/../app/app.php'; +require_once __DIR__ . '/inc/auth.inc.php'; -$opml         = OpmlManager::load(dirname(__FILE__) . '/../custom/people.opml'); + +$opml         = OpmlManager::load(__DIR__ . '/../custom/people.opml');  $opml_people  = $opml->getPeople();  $page_id      = 'admin-admin';  $header_extra = <<<"HTML" @@ -23,6 +24,7 @@ $page_content = <<<"FRAGMENT"              <div class="widget">                  <h3>{$l10n->getString('Clear cache')}</h3>                  <form action="purgecache.php" method="post" id="frmPurge"> +                    <input type="hidden" value="{$csrf->generate('frmPurge')}" name="_csrf">                      <p><label>{$l10n->getString('Clear cache:')}</label><input type="submit" class="submit delete" name="purge" id="purge" value="{$l10n->getString('Clear')}" /></p>                      <p class="help">{$l10n->getString('Clearing the cache will make moonmoon reload all feeds.')}</p>                  </form> @@ -31,6 +33,7 @@ $page_content = <<<"FRAGMENT"              <div class="widget">                  <h3>{$l10n->getString('Change administrator password')}</h3>                  <form action="changepassword.php" method="post" id="frmPassword"> +                    <input type="hidden" value="{$csrf->generate('frmPassword')}" name="_csrf">                      <p><label for="password">{$l10n->getString('New password:')}</label> <input type="password" class="text" value="" name="password" id="password" size="20" /> <input type="submit" class="submit delete" name="changepwd" id="changepwd" value="{$l10n->getString('Change password')}" /></p>                  </form>              </div> @@ -39,4 +42,4 @@ FRAGMENT;  $footer_extra = '';  $admin_access = 1; -require_once dirname(__FILE__) . '/template.php'; +require_once __DIR__ . '/template.php'; diff --git a/common/admin/changepassword.php b/common/admin/changepassword.php index 1fa505e..3b4500e 100644 --- a/common/admin/changepassword.php +++ b/common/admin/changepassword.php @@ -1,9 +1,11 @@  <?php -require_once dirname(__FILE__).'/inc/auth.inc.php'; -if (isset($_POST['password']) && ('' != $_POST['password'])){ +require_once __DIR__.'/../app/app.php'; +require_once __DIR__.'/inc/auth.inc.php'; + +if ($csrf->verify($_POST['_csrf'], 'frmPassword') && isset($_POST['password']) && ('' != $_POST['password'])) {      $out = '<?php $login="admin"; $password="'.md5($_POST['password']).'"; ?>'; -    file_put_contents(dirname(__FILE__).'/inc/pwd.inc.php', $out); +    file_put_contents(__DIR__.'/inc/pwd.inc.php', $out);      die("Password changed. <a href='administration.php'>Login</a>");  } else {      die('Can not change password'); diff --git a/common/admin/inc/auth.inc.php b/common/admin/inc/auth.inc.php index d21467b..0acf934 100644 --- a/common/admin/inc/auth.inc.php +++ b/common/admin/inc/auth.inc.php @@ -1,11 +1,13 @@  <?php -include (dirname(__FILE__).'/pwd.inc.php'); -if ( isset($_COOKIE['auth']) && $_COOKIE['auth'] == $password ) { -    //ok, cool -} else { -    setcookie('auth','', time()-3600); +include dirname(__FILE__).'/pwd.inc.php'; + +if (!class_exists('Planet')) { +    require __DIR__.'/../../vendor/autoload.php'; +} + +if (!Planet::authenticateUser($_COOKIE['auth'], $password)) { +    setcookie('auth', '', time() - 3600);      header('Location: login.php'); -    die; +    die();  } -?>
\ No newline at end of file diff --git a/common/admin/index.php b/common/admin/index.php index 28f7198..0118923 100755 --- a/common/admin/index.php +++ b/common/admin/index.php @@ -1,10 +1,10 @@  <?php -require_once dirname(__FILE__) . '/inc/auth.inc.php'; -require_once dirname(__FILE__) . '/../app/app.php'; +require_once __DIR__ . '/../app/app.php'; +require_once __DIR__ . '/inc/auth.inc.php';  //Load configuration -$config_file = dirname(__FILE__) . '/../custom/config.yml'; +$config_file = __DIR__ . '/../custom/config.yml';  if (is_file($config_file)){      $conf = Spyc::YAMLLoad($config_file); @@ -17,7 +17,7 @@ if (is_file($config_file)){  $Planet = new Planet($PlanetConfig);  //Load -if (0 < $Planet->loadOpml(dirname(__FILE__) . '/../custom/people.opml')) { +if (0 < $Planet->loadOpml(__DIR__ . '/../custom/people.opml')) {      $Planet->loadFeeds();      $items = $Planet->getItems();  } @@ -79,6 +79,7 @@ ob_start();                          <input type="submit" class="submit add" name="add" value="<?=_g('Add Feed')?>" />                      </fieldset>                      <p class="help"><?=_g('Accepted formats are RSS and ATOM. If the link is not a feed, moonmoon will try to autodiscover the feed.')?></p> +                <input type="hidden" value="<?php echo $csrf->generate('feedmanage'); ?>" name="_csrf">                  </form>              </div> @@ -87,6 +88,7 @@ ob_start();                  <form action="subscriptions.php" method="post" id="feedmanage">                  <p class="action">                  <span class="count"><?php echo sprintf(_g('Number of feeds: %s'), $count_feeds)?></span> +                <input type="hidden" value="<?php echo $csrf->generate('feedmanage'); ?>" name="_csrf">                  <input type="submit" class="submit save" name="save" id="save" value="<?=_g('Save changes')?>" />                  <input type="submit" class="submit delete" name="delete" id="delete" value="<?=_g('Delete selected Feeds')?>" />                  </p> @@ -99,6 +101,7 @@ ob_start();                              <th><?=_g('Last entry')?></th>                              <th><?=_g('Website link')?></th>                              <th><?=_g('Feed link')?></th> +                            <th><?=_g('Unavailable')?></th>                          </tr>                      </thead>                      <tbody> @@ -118,10 +121,12 @@ ob_start();                                  } else {                                      echo _g('Not in cache');                                  } +                                $check_is_down = $opml_person->getIsDown() === '1' ? 'checked="checked"' : '';                                  ?>                              </td>                              <td><input type="text" size="30" class="text" name="opml[<?=$i; ?>][website]" value="<?=$opml_person->getWebsite(); ?>" /></td>                              <td><input type="text" size="30" class="text" name="opml[<?=$i; ?>][feed]" value="<?=$opml_person->getFeed(); ?>" /></td> +                            <td><input type="checkbox" readonly="readonly" name="opml[<?=$i; ?>][isDown]" <?=$check_is_down?> value="1" /></td>                          </tr>                          <?php } ?>                      </tbody> @@ -133,4 +138,4 @@ $page_content = ob_get_contents();  ob_end_clean();  $admin_access = 1; -require_once dirname(__FILE__) . '/template.php'; +require_once __DIR__ . '/template.php'; diff --git a/common/admin/login.php b/common/admin/login.php index 796011f..a95e59f 100755 --- a/common/admin/login.php +++ b/common/admin/login.php @@ -1,10 +1,13 @@  <?php + +require_once __DIR__ . '/../app/app.php'; +  if (isset($_POST['password'])) { +    session_regenerate_id();      setcookie('auth',md5($_POST['password']));      header('Location: index.php');  } -require_once dirname(__FILE__) . '/../app/app.php';  $page_content = <<<FRAGMENT              <form action="" method="post" class="login">                  <fieldset> @@ -31,4 +34,4 @@ FRAGMENT;  $page_id      = 'admin-login';  $admin_access = 0; -require_once dirname(__FILE__) . '/template.php'; +require_once __DIR__ . '/template.php'; diff --git a/common/admin/logout.php b/common/admin/logout.php index 6dd32aa..adb843f 100644 --- a/common/admin/logout.php +++ b/common/admin/logout.php @@ -1,5 +1,10 @@  <?php + +require_once __DIR__ . '/../app/app.php'; +  setcookie('auth','', time()-3600); +session_destroy(); +session_regenerate_id(); +  header('Location: login.php'); -die; -?>
\ No newline at end of file +die(); diff --git a/common/admin/purgecache.php b/common/admin/purgecache.php index a5af5cf..23a5712 100644 --- a/common/admin/purgecache.php +++ b/common/admin/purgecache.php @@ -1,16 +1,18 @@  <?php -require_once dirname(__FILE__).'/inc/auth.inc.php'; + +require_once __DIR__.'/../app/app.php'; +require_once __DIR__.'/inc/auth.inc.php';  if (isset($_POST['purge'])){ -    $dir = dirname(__FILE__).'/../cache/'; -     +    $dir = __DIR__.'/../cache/'; +      $dh = opendir($dir); -     +      while ($filename = readdir($dh)) {          if ($filename == '.' OR $filename == '..') {              continue;          } -         +          if (filemtime($dir . DIRECTORY_SEPARATOR . $filename) < time()) {              @unlink($dir . DIRECTORY_SEPARATOR . $filename);          } @@ -18,4 +20,4 @@ if (isset($_POST['purge'])){  }  header('Location: administration.php'); -die();
\ No newline at end of file +die(); diff --git a/common/admin/subscriptions.php b/common/admin/subscriptions.php index ea2f113..f0fd896 100755 --- a/common/admin/subscriptions.php +++ b/common/admin/subscriptions.php @@ -1,21 +1,24 @@  <?php -require_once dirname(__FILE__) . '/inc/auth.inc.php'; -require_once dirname(__FILE__) . '/../app/app.php'; + +require_once __DIR__ . '/../app/app.php'; +require_once __DIR__ . '/inc/auth.inc.php';  function removeSlashes(&$item, $key){      $item = stripslashes($item);  } +if (!$csrf->verify($_POST['_csrf'], 'feedmanage')) { +    die('Invalid CSRF token!'); +} +  if (isset($_POST['opml']) || isset($_POST['add'])) { -    // Load config and old OPML -    $conf = Spyc::YAMLLoad(dirname(__FILE__).'/../custom/config.yml'); -    $PlanetConfig = new PlanetConfig($conf); +    // Load old OPML +    $oldOpml = OpmlManager::load(__DIR__.'/../custom/people.opml');      if ($PlanetConfig->getName() === '') {          $PlanetConfig->setName($oldOpml->getTitle());      } -    $oldOpml = OpmlManager::load(dirname(__FILE__).'/../custom/people.opml'); -    $newOpml = new opml(); +    $newOpml = new Opml();      $newOpml->title = $PlanetConfig->getName();      // Remove slashes if needed @@ -43,11 +46,18 @@ if (isset($_POST['opml']) || isset($_POST['add'])) {              $feed = new SimplePie();              $feed->enable_cache(false);              $feed->set_feed_url($_POST['url']); +            if ($conf['checkcerts'] === false) { +                $feed->set_curl_options([ +                    CURLOPT_SSL_VERIFYHOST => false, +                    CURLOPT_SSL_VERIFYPEER => false +                ]); +            }              $feed->init();              $feed->handle_content_type(); -            $person['name'] = $feed->get_title(); +            $person['name'] = html_entity_decode($feed->get_title());              $person['website'] = $feed->get_permalink();              $person['feed'] = $feed->feed_url; +            $person['isDown'] = '0';              $oldOpml->entries[] = $person;          } @@ -55,10 +65,10 @@ if (isset($_POST['opml']) || isset($_POST['add'])) {      }      // Backup old OPML -    OpmlManager::backup(dirname(__FILE__).'/../custom/people.opml'); +    OpmlManager::backup(__DIR__.'/../custom/people.opml');      // Save new OPML -    OpmlManager::save($newOpml, dirname(__FILE__).'/../custom/people.opml'); +    OpmlManager::save($newOpml, __DIR__.'/../custom/people.opml');  }  header("Location: index.php");  die(); 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? +¿Está seguro de que desea vaciar la caché? + + +# Translation note:  Vaciar la caché +;Clear cache +Vaciar la caché + + +# Translation note:  Vaciar la caché: +;Clear cache: +Vaciar la caché: + + +;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é hará que moonmoon recargue todas las fuentes web. + + +# Translation note:  Cambiar la contraseña de administración +;Change administrator password +Cambiar la contraseña de administración + + +# Translation note: Nueva contraseña: +;New password: +Nueva contraseña: + + +# Translation note:  Cambiar la contraseña +;Change password +Cambiar la contraseñ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ñadir una nueva fuente web + + +# Translation note: Añadir fuente  +;Add Feed +Añ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á de encontrar la fuente de forma automática. + + +;Manage existing feeds +Administrar las fuentes existentes + + +# Translation note:  Número de fuentes +;Number of feeds: %s +Nú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ón + + +;Name +Nombre + +# Translation note: Última entrada +;Last entry +Ú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á en caché + + +# Translation note:  Contraseña: +;Password: +Contraseña: + + +;OK +Entrar + + +# Translation note:  Administración de moonmoon +;moonmoon administration +Administración de moonmoon + + +# Translation note:  Volver a la página principal +;Back to main page +Volver a la página principal + + +;Logout +Salir + + +;Feeds +Fuentes + + +# Translation note:  Administración +;Administration +Administración + + +;Powered by <a %s>moonmoon</a> +Desarrollado por <a %s>moonmoon</a> + + +# Translation note:  Sin artÃculos +;No article +Sin artí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ó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ó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ón de moonmoon + + +# Translation note:  ¡Felicidades! Su moonmoon está preparado. +;Congratulations! Your moonmoon is ready. +¡Felicidades! Su moonmoon está preparado. + + +# Translation note: ¿Y ahora? +;What's next? +¿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ña para acceder al <a href="./admin/">panel de administració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 diff --git a/common/atom.php b/common/atom.php index 5122158..c80d987 100644 --- a/common/atom.php +++ b/common/atom.php @@ -1,15 +1,15 @@  <?php -include_once(dirname(__FILE__).'/app/app.php'); -include_once(dirname(__FILE__).'/app/lib/Cache.php'); +include_once(__DIR__.'/app/app.php'); +include_once(__DIR__.'/app/lib/Cache.php'); -if ($Planet->loadOpml(dirname(__FILE__).'/custom/people.opml') == 0) exit; +if ($Planet->loadOpml(__DIR__.'/custom/people.opml') == 0) exit;  $Planet->loadFeeds();  $items = $Planet->getItems();  $limit = $PlanetConfig->getMaxDisplay();  $count = 0; -header('Content-Type: application/atom+xml; charset=UTF-8'); +header('Content-Type: text/xml; charset=UTF-8');  echo '<?xml version="1.0" encoding="UTF-8" ?>';  ?>  <feed xmlns="http://www.w3.org/2005/Atom"> diff --git a/common/bin/build_release.sh b/common/bin/build_release.sh new file mode 100755 index 0000000..2f09661 --- /dev/null +++ b/common/bin/build_release.sh @@ -0,0 +1,20 @@ +#!/bin/bash + +set -eu + +tmp=$(mktemp -d -t moonmmon) + +echo "[*] Building into $tmp..." + +cd "$tmp" +git clone https://github.com/moonmoon/moonmoon.git --depth=1 --recursive -j8 +cd moonmoon +composer install --no-suggest --prefer-dist --no-dev +git describe --abbrev=0 --tags > VERSION +find . -name .DS_Store -exec rm {} \; +rm -rf .git .github .travis.yml .gitignore .gitmodules docs/.git/ +mkdir cache +cd .. +zip -r "moonmoon-$(cat moonmoon/VERSION).zip" . + +echo "[*] Grab the archive: ${tmp}/moonmoon-$(cat moonmoon/VERSION).zip" diff --git a/common/composer.json b/common/composer.json new file mode 100644 index 0000000..ea5032d --- /dev/null +++ b/common/composer.json @@ -0,0 +1,35 @@ +{ +    "name": "mauricesvay/moonmoon", +    "description": "Moonmoon is a simple feed aggregator.", +    "homepage": "http://moonmoon.org/", +    "type": "project", +    "license": "BSD", +    "authors": [ +        { +            "name": "Maurice Svay", +            "email": "maurice@svay.com" +        } +    ], +    "minimum-stability": "stable", +    "require": { +        "php": "^5.6 || ^7.0", +        "mustangostang/spyc": "0.5.1", +        "simplepie/simplepie": "^1.5", +        "paragonie/random_compat": "^2.0" +    }, +    "require-dev": { +        "guzzlehttp/guzzle": "^6.3", +        "phpunit/phpunit": "^5.7 || ^6.2" +    }, +    "scripts": { +        "test": "" +    }, +    "autoload": { +        "psr-0": { +            "": "app/classes/" +        }, +        "files": [ +            "app/helpers.php" +        ] +    } +} diff --git a/common/composer.lock b/common/composer.lock new file mode 100644 index 0000000..7959c9c --- /dev/null +++ b/common/composer.lock @@ -0,0 +1,1599 @@ +{ +    "_readme": [ +        "This file locks the dependencies of your project to a known state", +        "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file", +        "This file is @generated automatically" +    ], +    "content-hash": "a4fc919a4e8ef2463ff4a336940bc993", +    "packages": [ +        { +            "name": "mustangostang/spyc", +            "version": "0.5.1", +            "source": { +                "type": "git", +                "url": "https://github.com/mustangostang/spyc.git", +                "reference": "dc4785b4d7227fd9905e086d499fb8abfadf9977" +            }, +            "dist": { +                "type": "zip", +                "url": "https://api.github.com/repos/mustangostang/spyc/zipball/dc4785b4d7227fd9905e086d499fb8abfadf9977", +                "reference": "dc4785b4d7227fd9905e086d499fb8abfadf9977", +                "shasum": "" +            }, +            "require": { +                "php": ">=5.3.1" +            }, +            "type": "library", +            "extra": { +                "branch-alias": { +                    "dev-master": "0.5.x-dev" +                } +            }, +            "autoload": { +                "files": [ +                    "Spyc.php" +                ] +            }, +            "notification-url": "https://packagist.org/downloads/", +            "license": [ +                "MIT License" +            ], +            "authors": [ +                { +                    "name": "mustangostang", +                    "email": "vlad.andersen@gmail.com" +                } +            ], +            "description": "A simple YAML loader/dumper class for PHP", +            "homepage": "https://github.com/mustangostang/spyc/", +            "keywords": [ +                "spyc", +                "yaml", +                "yml" +            ], +            "time": "2013-02-21T10:52:01+00:00" +        }, +        { +            "name": "paragonie/random_compat", +            "version": "v2.0.11", +            "source": { +                "type": "git", +                "url": "https://github.com/paragonie/random_compat.git", +                "reference": "5da4d3c796c275c55f057af5a643ae297d96b4d8" +            }, +            "dist": { +                "type": "zip", +                "url": "https://api.github.com/repos/paragonie/random_compat/zipball/5da4d3c796c275c55f057af5a643ae297d96b4d8", +                "reference": "5da4d3c796c275c55f057af5a643ae297d96b4d8", +                "shasum": "" +            }, +            "require": { +                "php": ">=5.2.0" +            }, +            "require-dev": { +                "phpunit/phpunit": "4.*|5.*" +            }, +            "suggest": { +                "ext-libsodium": "Provides a modern crypto API that can be used to generate random bytes." +            }, +            "type": "library", +            "autoload": { +                "files": [ +                    "lib/random.php" +                ] +            }, +            "notification-url": "https://packagist.org/downloads/", +            "license": [ +                "MIT" +            ], +            "authors": [ +                { +                    "name": "Paragon Initiative Enterprises", +                    "email": "security@paragonie.com", +                    "homepage": "https://paragonie.com" +                } +            ], +            "description": "PHP 5.x polyfill for random_bytes() and random_int() from PHP 7", +            "keywords": [ +                "csprng", +                "pseudorandom", +                "random" +            ], +            "time": "2017-09-27T21:40:39+00:00" +        }, +        { +            "name": "simplepie/simplepie", +            "version": "1.5", +            "source": { +                "type": "git", +                "url": "https://github.com/simplepie/simplepie.git", +                "reference": "5de5551953f95feef12cf355a7a26a70f94aa3ab" +            }, +            "dist": { +                "type": "zip", +                "url": "https://api.github.com/repos/simplepie/simplepie/zipball/5de5551953f95feef12cf355a7a26a70f94aa3ab", +                "reference": "5de5551953f95feef12cf355a7a26a70f94aa3ab", +                "shasum": "" +            }, +            "require": { +                "php": ">=5.3.0" +            }, +            "require-dev": { +                "phpunit/phpunit": "~4 || ~5" +            }, +            "suggest": { +                "mf2/mf2": "Microformat module that allows for parsing HTML for microformats" +            }, +            "type": "library", +            "autoload": { +                "psr-0": { +                    "SimplePie": "library" +                } +            }, +            "notification-url": "https://packagist.org/downloads/", +            "license": [ +                "BSD-3-Clause" +            ], +            "authors": [ +                { +                    "name": "Ryan Parman", +                    "homepage": "http://ryanparman.com/", +                    "role": "Creator, alumnus developer" +                }, +                { +                    "name": "Geoffrey Sneddon", +                    "homepage": "http://gsnedders.com/", +                    "role": "Alumnus developer" +                }, +                { +                    "name": "Ryan McCue", +                    "email": "me@ryanmccue.info", +                    "homepage": "http://ryanmccue.info/", +                    "role": "Developer" +                } +            ], +            "description": "A simple Atom/RSS parsing library for PHP", +            "homepage": "http://simplepie.org/", +            "keywords": [ +                "atom", +                "feeds", +                "rss" +            ], +            "time": "2017-04-17T07:29:31+00:00" +        } +    ], +    "packages-dev": [ +        { +            "name": "dflydev/markdown", +            "version": "v1.0.0", +            "source": { +                "type": "git", +                "url": "https://github.com/dflydev/dflydev-markdown.git", +                "reference": "76501a808522dbe40a5a71d272bd08d54cbae03d" +            }, +            "dist": { +                "type": "zip", +                "url": "https://api.github.com/repos/dflydev/dflydev-markdown/zipball/76501a808522dbe40a5a71d272bd08d54cbae03d", +                "reference": "76501a808522dbe40a5a71d272bd08d54cbae03d", +                "shasum": "" +            }, +            "require": { +                "php": ">=5.3" +            }, +            "type": "library", +            "autoload": { +                "psr-0": { +                    "dflydev\\markdown": "src" +                } +            }, +            "notification-url": "https://packagist.org/downloads/", +            "license": [ +                "New BSD License" +            ], +            "authors": [ +                { +                    "name": "Dragonfly Development Inc.", +                    "email": "info@dflydev.com", +                    "homepage": "http://dflydev.com" +                }, +                { +                    "name": "Beau Simensen", +                    "email": "beau@dflydev.com", +                    "homepage": "http://beausimensen.com" +                }, +                { +                    "name": "Michel Fortin", +                    "homepage": "http://michelf.com" +                }, +                { +                    "name": "John Gruber", +                    "homepage": "http://daringfireball.net" +                } +            ], +            "description": "PHP Markdown & Extra", +            "homepage": "http://github.com/dflydev/dflydev-markdown", +            "keywords": [ +                "markdown" +            ], +            "abandoned": "michelf/php-markdown", +            "time": "2012-01-02T23:11:32+00:00" +        }, +        { +            "name": "doctrine/instantiator", +            "version": "1.0.4", +            "source": { +                "type": "git", +                "url": "https://github.com/doctrine/instantiator.git", +                "reference": "f976e5de371104877ebc89bd8fecb0019ed9c119" +            }, +            "dist": { +                "type": "zip", +                "url": "https://api.github.com/repos/doctrine/instantiator/zipball/f976e5de371104877ebc89bd8fecb0019ed9c119", +                "reference": "f976e5de371104877ebc89bd8fecb0019ed9c119", +                "shasum": "" +            }, +            "require": { +                "php": ">=5.3,<8.0-DEV" +            }, +            "require-dev": { +                "athletic/athletic": "~0.1.8", +                "ext-pdo": "*", +                "ext-phar": "*", +                "phpunit/phpunit": "~4.0", +                "squizlabs/php_codesniffer": "2.0.*@ALPHA" +            }, +            "type": "library", +            "extra": { +                "branch-alias": { +                    "dev-master": "1.0.x-dev" +                } +            }, +            "autoload": { +                "psr-0": { +                    "Doctrine\\Instantiator\\": "src" +                } +            }, +            "notification-url": "https://packagist.org/downloads/", +            "license": [ +                "MIT" +            ], +            "authors": [ +                { +                    "name": "Marco Pivetta", +                    "email": "ocramius@gmail.com", +                    "homepage": "http://ocramius.github.com/" +                } +            ], +            "description": "A small, lightweight utility to instantiate objects in PHP without invoking their constructors", +            "homepage": "https://github.com/doctrine/instantiator", +            "keywords": [ +                "constructor", +                "instantiate" +            ], +            "time": "2014-10-13T12:58:55+00:00" +        }, +        { +            "name": "guzzlehttp/guzzle", +            "version": "6.3.0", +            "source": { +                "type": "git", +                "url": "https://github.com/guzzle/guzzle.git", +                "reference": "f4db5a78a5ea468d4831de7f0bf9d9415e348699" +            }, +            "dist": { +                "type": "zip", +                "url": "https://api.github.com/repos/guzzle/guzzle/zipball/f4db5a78a5ea468d4831de7f0bf9d9415e348699", +                "reference": "f4db5a78a5ea468d4831de7f0bf9d9415e348699", +                "shasum": "" +            }, +            "require": { +                "guzzlehttp/promises": "^1.0", +                "guzzlehttp/psr7": "^1.4", +                "php": ">=5.5" +            }, +            "require-dev": { +                "ext-curl": "*", +                "phpunit/phpunit": "^4.0 || ^5.0", +                "psr/log": "^1.0" +            }, +            "suggest": { +                "psr/log": "Required for using the Log middleware" +            }, +            "type": "library", +            "extra": { +                "branch-alias": { +                    "dev-master": "6.2-dev" +                } +            }, +            "autoload": { +                "files": [ +                    "src/functions_include.php" +                ], +                "psr-4": { +                    "GuzzleHttp\\": "src/" +                } +            }, +            "notification-url": "https://packagist.org/downloads/", +            "license": [ +                "MIT" +            ], +            "authors": [ +                { +                    "name": "Michael Dowling", +                    "email": "mtdowling@gmail.com", +                    "homepage": "https://github.com/mtdowling" +                } +            ], +            "description": "Guzzle is a PHP HTTP client library", +            "homepage": "http://guzzlephp.org/", +            "keywords": [ +                "client", +                "curl", +                "framework", +                "http", +                "http client", +                "rest", +                "web service" +            ], +            "time": "2017-06-22T18:50:49+00:00" +        }, +        { +            "name": "guzzlehttp/promises", +            "version": "1.0.0", +            "source": { +                "type": "git", +                "url": "https://github.com/guzzle/promises.git", +                "reference": "01abc3232138f330d8a1eaa808fcbdf9b4292f47" +            }, +            "dist": { +                "type": "zip", +                "url": "https://api.github.com/repos/guzzle/promises/zipball/01abc3232138f330d8a1eaa808fcbdf9b4292f47", +                "reference": "01abc3232138f330d8a1eaa808fcbdf9b4292f47", +                "shasum": "" +            }, +            "require": { +                "php": ">=5.5.0" +            }, +            "require-dev": { +                "phpunit/phpunit": "^4.0" +            }, +            "type": "library", +            "extra": { +                "branch-alias": { +                    "dev-master": "1.0-dev" +                } +            }, +            "autoload": { +                "psr-4": { +                    "GuzzleHttp\\Promise\\": "src/" +                }, +                "files": [ +                    "src/functions.php" +                ] +            }, +            "notification-url": "https://packagist.org/downloads/", +            "license": [ +                "MIT" +            ], +            "authors": [ +                { +                    "name": "Michael Dowling", +                    "email": "mtdowling@gmail.com", +                    "homepage": "https://github.com/mtdowling" +                } +            ], +            "description": "Guzzle promises library", +            "keywords": [ +                "promise" +            ], +            "time": "2015-05-13T05:05:10+00:00" +        }, +        { +            "name": "guzzlehttp/psr7", +            "version": "1.4.0", +            "source": { +                "type": "git", +                "url": "https://github.com/guzzle/psr7.git", +                "reference": "04a6d1a00ea5da0727ee94309a9f0d3dbaecb569" +            }, +            "dist": { +                "type": "zip", +                "url": "https://api.github.com/repos/guzzle/psr7/zipball/04a6d1a00ea5da0727ee94309a9f0d3dbaecb569", +                "reference": "04a6d1a00ea5da0727ee94309a9f0d3dbaecb569", +                "shasum": "" +            }, +            "require": { +                "php": ">=5.4.0", +                "psr/http-message": "~1.0" +            }, +            "provide": { +                "psr/http-message-implementation": "1.0" +            }, +            "require-dev": { +                "phpunit/phpunit": "~4.0" +            }, +            "type": "library", +            "extra": { +                "branch-alias": { +                    "dev-master": "1.4-dev" +                } +            }, +            "autoload": { +                "psr-4": { +                    "GuzzleHttp\\Psr7\\": "src/" +                }, +                "files": [ +                    "src/functions_include.php" +                ] +            }, +            "notification-url": "https://packagist.org/downloads/", +            "license": [ +                "MIT" +            ], +            "authors": [ +                { +                    "name": "Michael Dowling", +                    "email": "mtdowling@gmail.com", +                    "homepage": "https://github.com/mtdowling" +                }, +                { +                    "name": "Tobias Schultze", +                    "homepage": "https://github.com/Tobion" +                } +            ], +            "description": "PSR-7 message implementation that also provides common utility methods", +            "keywords": [ +                "http", +                "message", +                "request", +                "response", +                "stream", +                "uri", +                "url" +            ], +            "time": "2017-02-21T01:20:32+00:00" +        }, +        { +            "name": "myclabs/deep-copy", +            "version": "1.3.0", +            "source": { +                "type": "git", +                "url": "https://github.com/myclabs/DeepCopy.git", +                "reference": "96fbdc07635989c35c5a1912379f4c4b2ab15fd5" +            }, +            "dist": { +                "type": "zip", +                "url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/96fbdc07635989c35c5a1912379f4c4b2ab15fd5", +                "reference": "96fbdc07635989c35c5a1912379f4c4b2ab15fd5", +                "shasum": "" +            }, +            "require": { +                "php": ">=5.4.0" +            }, +            "require-dev": { +                "doctrine/collections": "1.*", +                "phpunit/phpunit": "~4.1" +            }, +            "type": "library", +            "autoload": { +                "psr-4": { +                    "DeepCopy\\": "src/DeepCopy/" +                } +            }, +            "notification-url": "https://packagist.org/downloads/", +            "license": [ +                "MIT" +            ], +            "description": "Create deep copies (clones) of your objects", +            "homepage": "https://github.com/myclabs/DeepCopy", +            "keywords": [ +                "clone", +                "copy", +                "duplicate", +                "object", +                "object graph" +            ], +            "time": "2015-03-21T22:40:23+00:00" +        }, +        { +            "name": "phpdocumentor/reflection-docblock", +            "version": "2.0.0", +            "source": { +                "type": "git", +                "url": "https://github.com/phpDocumentor/ReflectionDocBlock.git", +                "reference": "66ae84e9d7c8ea85c979cb65977bd8e608baf0c5" +            }, +            "dist": { +                "type": "zip", +                "url": "https://api.github.com/repos/phpDocumentor/ReflectionDocBlock/zipball/66ae84e9d7c8ea85c979cb65977bd8e608baf0c5", +                "reference": "66ae84e9d7c8ea85c979cb65977bd8e608baf0c5", +                "shasum": "" +            }, +            "require": { +                "dflydev/markdown": "1.0.*", +                "php": ">=5.3.3" +            }, +            "require-dev": { +                "phpunit/phpunit": "3.7.*@stable" +            }, +            "type": "library", +            "extra": { +                "branch-alias": { +                    "dev-master": "2.0.x-dev" +                } +            }, +            "autoload": { +                "psr-0": { +                    "phpDocumentor": [ +                        "src/" +                    ] +                } +            }, +            "notification-url": "https://packagist.org/downloads/", +            "license": [ +                "MIT" +            ], +            "authors": [ +                { +                    "name": "Mike van Riel", +                    "email": "mike.vanriel@naenius.com" +                } +            ], +            "time": "2013-08-07T11:04:22+00:00" +        }, +        { +            "name": "phpspec/prophecy", +            "version": "v1.3.1", +            "source": { +                "type": "git", +                "url": "https://github.com/phpspec/prophecy.git", +                "reference": "9ca52329bcdd1500de24427542577ebf3fc2f1c9" +            }, +            "dist": { +                "type": "zip", +                "url": "https://api.github.com/repos/phpspec/prophecy/zipball/9ca52329bcdd1500de24427542577ebf3fc2f1c9", +                "reference": "9ca52329bcdd1500de24427542577ebf3fc2f1c9", +                "shasum": "" +            }, +            "require": { +                "doctrine/instantiator": "~1.0,>=1.0.2", +                "phpdocumentor/reflection-docblock": "~2.0" +            }, +            "require-dev": { +                "phpspec/phpspec": "~2.0" +            }, +            "type": "library", +            "extra": { +                "branch-alias": { +                    "dev-master": "1.2.x-dev" +                } +            }, +            "autoload": { +                "psr-0": { +                    "Prophecy\\": "src/" +                } +            }, +            "notification-url": "https://packagist.org/downloads/", +            "license": [ +                "MIT" +            ], +            "authors": [ +                { +                    "name": "Konstantin Kudryashov", +                    "email": "ever.zet@gmail.com", +                    "homepage": "http://everzet.com" +                }, +                { +                    "name": "Marcello Duarte", +                    "email": "marcello.duarte@gmail.com" +                } +            ], +            "description": "Highly opinionated mocking framework for PHP 5.3+", +            "homepage": "http://phpspec.org", +            "keywords": [ +                "Double", +                "Dummy", +                "fake", +                "mock", +                "spy", +                "stub" +            ], +            "time": "2014-11-17T16:23:49+00:00" +        }, +        { +            "name": "phpunit/php-code-coverage", +            "version": "4.0.3", +            "source": { +                "type": "git", +                "url": "https://github.com/sebastianbergmann/php-code-coverage.git", +                "reference": "903fd6318d0a90b4770a009ff73e4a4e9c437929" +            }, +            "dist": { +                "type": "zip", +                "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/903fd6318d0a90b4770a009ff73e4a4e9c437929", +                "reference": "903fd6318d0a90b4770a009ff73e4a4e9c437929", +                "shasum": "" +            }, +            "require": { +                "php": "^5.6 || ^7.0", +                "phpunit/php-file-iterator": "~1.3", +                "phpunit/php-text-template": "~1.2", +                "phpunit/php-token-stream": "^1.4.2", +                "sebastian/code-unit-reverse-lookup": "~1.0", +                "sebastian/environment": "^1.3.2 || ^2.0", +                "sebastian/version": "~1.0|~2.0" +            }, +            "require-dev": { +                "ext-xdebug": ">=2.1.4", +                "phpunit/phpunit": "^5.4" +            }, +            "suggest": { +                "ext-dom": "*", +                "ext-xdebug": ">=2.4.0", +                "ext-xmlwriter": "*" +            }, +            "type": "library", +            "extra": { +                "branch-alias": { +                    "dev-master": "4.0.x-dev" +                } +            }, +            "autoload": { +                "classmap": [ +                    "src/" +                ] +            }, +            "notification-url": "https://packagist.org/downloads/", +            "license": [ +                "BSD-3-Clause" +            ], +            "authors": [ +                { +                    "name": "Sebastian Bergmann", +                    "email": "sb@sebastian-bergmann.de", +                    "role": "lead" +                } +            ], +            "description": "Library that provides collection, processing, and rendering functionality for PHP code coverage information.", +            "homepage": "https://github.com/sebastianbergmann/php-code-coverage", +            "keywords": [ +                "coverage", +                "testing", +                "xunit" +            ], +            "time": "2016-11-28T16:00:31+00:00" +        }, +        { +            "name": "phpunit/php-file-iterator", +            "version": "1.4.0", +            "source": { +                "type": "git", +                "url": "https://github.com/sebastianbergmann/php-file-iterator.git", +                "reference": "a923bb15680d0089e2316f7a4af8f437046e96bb" +            }, +            "dist": { +                "type": "zip", +                "url": "https://api.github.com/repos/sebastianbergmann/php-file-iterator/zipball/a923bb15680d0089e2316f7a4af8f437046e96bb", +                "reference": "a923bb15680d0089e2316f7a4af8f437046e96bb", +                "shasum": "" +            }, +            "require": { +                "php": ">=5.3.3" +            }, +            "type": "library", +            "extra": { +                "branch-alias": { +                    "dev-master": "1.4.x-dev" +                } +            }, +            "autoload": { +                "classmap": [ +                    "src/" +                ] +            }, +            "notification-url": "https://packagist.org/downloads/", +            "license": [ +                "BSD-3-Clause" +            ], +            "authors": [ +                { +                    "name": "Sebastian Bergmann", +                    "email": "sb@sebastian-bergmann.de", +                    "role": "lead" +                } +            ], +            "description": "FilterIterator implementation that filters files based on a list of suffixes.", +            "homepage": "https://github.com/sebastianbergmann/php-file-iterator/", +            "keywords": [ +                "filesystem", +                "iterator" +            ], +            "time": "2015-04-02T05:19:05+00:00" +        }, +        { +            "name": "phpunit/php-text-template", +            "version": "1.2.0", +            "source": { +                "type": "git", +                "url": "https://github.com/sebastianbergmann/php-text-template.git", +                "reference": "206dfefc0ffe9cebf65c413e3d0e809c82fbf00a" +            }, +            "dist": { +                "type": "zip", +                "url": "https://api.github.com/repos/sebastianbergmann/php-text-template/zipball/206dfefc0ffe9cebf65c413e3d0e809c82fbf00a", +                "reference": "206dfefc0ffe9cebf65c413e3d0e809c82fbf00a", +                "shasum": "" +            }, +            "require": { +                "php": ">=5.3.3" +            }, +            "type": "library", +            "autoload": { +                "classmap": [ +                    "Text/" +                ] +            }, +            "notification-url": "https://packagist.org/downloads/", +            "include-path": [ +                "" +            ], +            "license": [ +                "BSD-3-Clause" +            ], +            "authors": [ +                { +                    "name": "Sebastian Bergmann", +                    "email": "sb@sebastian-bergmann.de", +                    "role": "lead" +                } +            ], +            "description": "Simple template engine.", +            "homepage": "https://github.com/sebastianbergmann/php-text-template/", +            "keywords": [ +                "template" +            ], +            "time": "2014-01-30T17:20:04+00:00" +        }, +        { +            "name": "phpunit/php-timer", +            "version": "1.0.6", +            "source": { +                "type": "git", +                "url": "https://github.com/sebastianbergmann/php-timer.git", +                "reference": "83fe1bdc5d47658b727595c14da140da92b3d66d" +            }, +            "dist": { +                "type": "zip", +                "url": "https://api.github.com/repos/sebastianbergmann/php-timer/zipball/83fe1bdc5d47658b727595c14da140da92b3d66d", +                "reference": "83fe1bdc5d47658b727595c14da140da92b3d66d", +                "shasum": "" +            }, +            "require": { +                "php": ">=5.3.3" +            }, +            "type": "library", +            "autoload": { +                "classmap": [ +                    "src/" +                ] +            }, +            "notification-url": "https://packagist.org/downloads/", +            "license": [ +                "BSD-3-Clause" +            ], +            "authors": [ +                { +                    "name": "Sebastian Bergmann", +                    "email": "sb@sebastian-bergmann.de", +                    "role": "lead" +                } +            ], +            "description": "Utility class for timing", +            "homepage": "https://github.com/sebastianbergmann/php-timer/", +            "keywords": [ +                "timer" +            ], +            "time": "2015-06-13T07:35:30+00:00" +        }, +        { +            "name": "phpunit/php-token-stream", +            "version": "1.4.2", +            "source": { +                "type": "git", +                "url": "https://github.com/sebastianbergmann/php-token-stream.git", +                "reference": "db63be1159c81df649cd0260e30249a586d4129e" +            }, +            "dist": { +                "type": "zip", +                "url": "https://api.github.com/repos/sebastianbergmann/php-token-stream/zipball/db63be1159c81df649cd0260e30249a586d4129e", +                "reference": "db63be1159c81df649cd0260e30249a586d4129e", +                "shasum": "" +            }, +            "require": { +                "ext-tokenizer": "*", +                "php": ">=5.3.3" +            }, +            "require-dev": { +                "phpunit/phpunit": "~4.2" +            }, +            "type": "library", +            "extra": { +                "branch-alias": { +                    "dev-master": "1.4-dev" +                } +            }, +            "autoload": { +                "classmap": [ +                    "src/" +                ] +            }, +            "notification-url": "https://packagist.org/downloads/", +            "license": [ +                "BSD-3-Clause" +            ], +            "authors": [ +                { +                    "name": "Sebastian Bergmann", +                    "email": "sebastian@phpunit.de" +                } +            ], +            "description": "Wrapper around PHP's tokenizer extension.", +            "homepage": "https://github.com/sebastianbergmann/php-token-stream/", +            "keywords": [ +                "tokenizer" +            ], +            "time": "2015-06-12T07:34:24+00:00" +        }, +        { +            "name": "phpunit/phpunit", +            "version": "5.7.0", +            "source": { +                "type": "git", +                "url": "https://github.com/sebastianbergmann/phpunit.git", +                "reference": "00d72b8dbd2bb7d6f02a820e6db5cb70df6ac55c" +            }, +            "dist": { +                "type": "zip", +                "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/00d72b8dbd2bb7d6f02a820e6db5cb70df6ac55c", +                "reference": "00d72b8dbd2bb7d6f02a820e6db5cb70df6ac55c", +                "shasum": "" +            }, +            "require": { +                "ext-dom": "*", +                "ext-json": "*", +                "ext-libxml": "*", +                "ext-mbstring": "*", +                "ext-xml": "*", +                "myclabs/deep-copy": "~1.3", +                "php": "^5.6 || ^7.0", +                "phpspec/prophecy": "^1.3.1", +                "phpunit/php-code-coverage": "^4.0.3", +                "phpunit/php-file-iterator": "~1.4", +                "phpunit/php-text-template": "~1.2", +                "phpunit/php-timer": "^1.0.6", +                "phpunit/phpunit-mock-objects": "^3.2", +                "sebastian/comparator": "~1.2.2", +                "sebastian/diff": "~1.2", +                "sebastian/environment": "^1.3.4 || ^2.0", +                "sebastian/exporter": "~2.0", +                "sebastian/global-state": "~1.0", +                "sebastian/object-enumerator": "~2.0", +                "sebastian/resource-operations": "~1.0", +                "sebastian/version": "~1.0|~2.0", +                "symfony/yaml": "~2.1|~3.0" +            }, +            "conflict": { +                "phpdocumentor/reflection-docblock": "3.0.2" +            }, +            "require-dev": { +                "ext-pdo": "*" +            }, +            "suggest": { +                "ext-xdebug": "*", +                "phpunit/php-invoker": "~1.1" +            }, +            "bin": [ +                "phpunit" +            ], +            "type": "library", +            "extra": { +                "branch-alias": { +                    "dev-master": "5.7.x-dev" +                } +            }, +            "autoload": { +                "classmap": [ +                    "src/" +                ] +            }, +            "notification-url": "https://packagist.org/downloads/", +            "license": [ +                "BSD-3-Clause" +            ], +            "authors": [ +                { +                    "name": "Sebastian Bergmann", +                    "email": "sebastian@phpunit.de", +                    "role": "lead" +                } +            ], +            "description": "The PHP Unit Testing framework.", +            "homepage": "https://phpunit.de/", +            "keywords": [ +                "phpunit", +                "testing", +                "xunit" +            ], +            "time": "2016-12-01T17:04:00+00:00" +        }, +        { +            "name": "phpunit/phpunit-mock-objects", +            "version": "3.4.1", +            "source": { +                "type": "git", +                "url": "https://github.com/sebastianbergmann/phpunit-mock-objects.git", +                "reference": "45026c8383187ad1dcb14fbfec77dced265b9cfc" +            }, +            "dist": { +                "type": "zip", +                "url": "https://api.github.com/repos/sebastianbergmann/phpunit-mock-objects/zipball/45026c8383187ad1dcb14fbfec77dced265b9cfc", +                "reference": "45026c8383187ad1dcb14fbfec77dced265b9cfc", +                "shasum": "" +            }, +            "require": { +                "doctrine/instantiator": "^1.0.2", +                "php": "^5.6 || ^7.0", +                "phpunit/php-text-template": "^1.2", +                "sebastian/exporter": "^1.2 || ^2.0" +            }, +            "conflict": { +                "phpunit/phpunit": "<5.4.0" +            }, +            "require-dev": { +                "phpunit/phpunit": "^5.4" +            }, +            "suggest": { +                "ext-soap": "*" +            }, +            "type": "library", +            "extra": { +                "branch-alias": { +                    "dev-master": "3.2.x-dev" +                } +            }, +            "autoload": { +                "classmap": [ +                    "src/" +                ] +            }, +            "notification-url": "https://packagist.org/downloads/", +            "license": [ +                "BSD-3-Clause" +            ], +            "authors": [ +                { +                    "name": "Sebastian Bergmann", +                    "email": "sb@sebastian-bergmann.de", +                    "role": "lead" +                } +            ], +            "description": "Mock Object library for PHPUnit", +            "homepage": "https://github.com/sebastianbergmann/phpunit-mock-objects/", +            "keywords": [ +                "mock", +                "xunit" +            ], +            "time": "2016-11-19T09:07:46+00:00" +        }, +        { +            "name": "psr/http-message", +            "version": "1.0", +            "source": { +                "type": "git", +                "url": "https://github.com/php-fig/http-message.git", +                "reference": "85d63699f0dbedb190bbd4b0d2b9dc707ea4c298" +            }, +            "dist": { +                "type": "zip", +                "url": "https://api.github.com/repos/php-fig/http-message/zipball/85d63699f0dbedb190bbd4b0d2b9dc707ea4c298", +                "reference": "85d63699f0dbedb190bbd4b0d2b9dc707ea4c298", +                "shasum": "" +            }, +            "require": { +                "php": ">=5.3.0" +            }, +            "type": "library", +            "extra": { +                "branch-alias": { +                    "dev-master": "1.0.x-dev" +                } +            }, +            "autoload": { +                "psr-4": { +                    "Psr\\Http\\Message\\": "src/" +                } +            }, +            "notification-url": "https://packagist.org/downloads/", +            "license": [ +                "MIT" +            ], +            "authors": [ +                { +                    "name": "PHP-FIG", +                    "homepage": "http://www.php-fig.org/" +                } +            ], +            "description": "Common interface for HTTP messages", +            "keywords": [ +                "http", +                "http-message", +                "psr", +                "psr-7", +                "request", +                "response" +            ], +            "time": "2015-05-04T20:22:00+00:00" +        }, +        { +            "name": "sebastian/code-unit-reverse-lookup", +            "version": "1.0.0", +            "source": { +                "type": "git", +                "url": "https://github.com/sebastianbergmann/code-unit-reverse-lookup.git", +                "reference": "c36f5e7cfce482fde5bf8d10d41a53591e0198fe" +            }, +            "dist": { +                "type": "zip", +                "url": "https://api.github.com/repos/sebastianbergmann/code-unit-reverse-lookup/zipball/c36f5e7cfce482fde5bf8d10d41a53591e0198fe", +                "reference": "c36f5e7cfce482fde5bf8d10d41a53591e0198fe", +                "shasum": "" +            }, +            "require": { +                "php": ">=5.6" +            }, +            "require-dev": { +                "phpunit/phpunit": "~5" +            }, +            "type": "library", +            "extra": { +                "branch-alias": { +                    "dev-master": "1.0.x-dev" +                } +            }, +            "autoload": { +                "classmap": [ +                    "src/" +                ] +            }, +            "notification-url": "https://packagist.org/downloads/", +            "license": [ +                "BSD-3-Clause" +            ], +            "authors": [ +                { +                    "name": "Sebastian Bergmann", +                    "email": "sebastian@phpunit.de" +                } +            ], +            "description": "Looks up which function or method a line of code belongs to", +            "homepage": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/", +            "time": "2016-02-13T06:45:14+00:00" +        }, +        { +            "name": "sebastian/comparator", +            "version": "1.2.2", +            "source": { +                "type": "git", +                "url": "https://github.com/sebastianbergmann/comparator.git", +                "reference": "6a1ed12e8b2409076ab22e3897126211ff8b1f7f" +            }, +            "dist": { +                "type": "zip", +                "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/6a1ed12e8b2409076ab22e3897126211ff8b1f7f", +                "reference": "6a1ed12e8b2409076ab22e3897126211ff8b1f7f", +                "shasum": "" +            }, +            "require": { +                "php": ">=5.3.3", +                "sebastian/diff": "~1.2", +                "sebastian/exporter": "~1.2 || ~2.0" +            }, +            "require-dev": { +                "phpunit/phpunit": "~4.4" +            }, +            "type": "library", +            "extra": { +                "branch-alias": { +                    "dev-master": "1.2.x-dev" +                } +            }, +            "autoload": { +                "classmap": [ +                    "src/" +                ] +            }, +            "notification-url": "https://packagist.org/downloads/", +            "license": [ +                "BSD-3-Clause" +            ], +            "authors": [ +                { +                    "name": "Jeff Welch", +                    "email": "whatthejeff@gmail.com" +                }, +                { +                    "name": "Volker Dusch", +                    "email": "github@wallbash.com" +                }, +                { +                    "name": "Bernhard Schussek", +                    "email": "bschussek@2bepublished.at" +                }, +                { +                    "name": "Sebastian Bergmann", +                    "email": "sebastian@phpunit.de" +                } +            ], +            "description": "Provides the functionality to compare PHP values for equality", +            "homepage": "http://www.github.com/sebastianbergmann/comparator", +            "keywords": [ +                "comparator", +                "compare", +                "equality" +            ], +            "time": "2016-11-19T09:18:40+00:00" +        }, +        { +            "name": "sebastian/diff", +            "version": "1.2.0", +            "source": { +                "type": "git", +                "url": "https://github.com/sebastianbergmann/diff.git", +                "reference": "5843509fed39dee4b356a306401e9dd1a931fec7" +            }, +            "dist": { +                "type": "zip", +                "url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/5843509fed39dee4b356a306401e9dd1a931fec7", +                "reference": "5843509fed39dee4b356a306401e9dd1a931fec7", +                "shasum": "" +            }, +            "require": { +                "php": ">=5.3.3" +            }, +            "require-dev": { +                "phpunit/phpunit": "~4.2" +            }, +            "type": "library", +            "extra": { +                "branch-alias": { +                    "dev-master": "1.2-dev" +                } +            }, +            "autoload": { +                "classmap": [ +                    "src/" +                ] +            }, +            "notification-url": "https://packagist.org/downloads/", +            "license": [ +                "BSD-3-Clause" +            ], +            "authors": [ +                { +                    "name": "Kore Nordmann", +                    "email": "mail@kore-nordmann.de" +                }, +                { +                    "name": "Sebastian Bergmann", +                    "email": "sebastian@phpunit.de" +                } +            ], +            "description": "Diff implementation", +            "homepage": "http://www.github.com/sebastianbergmann/diff", +            "keywords": [ +                "diff" +            ], +            "time": "2014-08-15T10:29:00+00:00" +        }, +        { +            "name": "sebastian/environment", +            "version": "1.3.4", +            "source": { +                "type": "git", +                "url": "https://github.com/sebastianbergmann/environment.git", +                "reference": "22aa49baa48886f40b060e061a7967436f44a249" +            }, +            "dist": { +                "type": "zip", +                "url": "https://api.github.com/repos/sebastianbergmann/environment/zipball/22aa49baa48886f40b060e061a7967436f44a249", +                "reference": "22aa49baa48886f40b060e061a7967436f44a249", +                "shasum": "" +            }, +            "require": { +                "php": ">=5.3.3" +            }, +            "require-dev": { +                "phpunit/phpunit": "~4.4" +            }, +            "type": "library", +            "extra": { +                "branch-alias": { +                    "dev-master": "1.3.x-dev" +                } +            }, +            "autoload": { +                "classmap": [ +                    "src/" +                ] +            }, +            "notification-url": "https://packagist.org/downloads/", +            "license": [ +                "BSD-3-Clause" +            ], +            "authors": [ +                { +                    "name": "Sebastian Bergmann", +                    "email": "sebastian@phpunit.de" +                } +            ], +            "description": "Provides functionality to handle HHVM/PHP environments", +            "homepage": "http://www.github.com/sebastianbergmann/environment", +            "keywords": [ +                "Xdebug", +                "environment", +                "hhvm" +            ], +            "time": "2016-02-26T11:40:57+00:00" +        }, +        { +            "name": "sebastian/exporter", +            "version": "2.0.0", +            "source": { +                "type": "git", +                "url": "https://github.com/sebastianbergmann/exporter.git", +                "reference": "ce474bdd1a34744d7ac5d6aad3a46d48d9bac4c4" +            }, +            "dist": { +                "type": "zip", +                "url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/ce474bdd1a34744d7ac5d6aad3a46d48d9bac4c4", +                "reference": "ce474bdd1a34744d7ac5d6aad3a46d48d9bac4c4", +                "shasum": "" +            }, +            "require": { +                "php": ">=5.3.3", +                "sebastian/recursion-context": "~2.0" +            }, +            "require-dev": { +                "ext-mbstring": "*", +                "phpunit/phpunit": "~4.4" +            }, +            "type": "library", +            "extra": { +                "branch-alias": { +                    "dev-master": "2.0.x-dev" +                } +            }, +            "autoload": { +                "classmap": [ +                    "src/" +                ] +            }, +            "notification-url": "https://packagist.org/downloads/", +            "license": [ +                "BSD-3-Clause" +            ], +            "authors": [ +                { +                    "name": "Jeff Welch", +                    "email": "whatthejeff@gmail.com" +                }, +                { +                    "name": "Volker Dusch", +                    "email": "github@wallbash.com" +                }, +                { +                    "name": "Bernhard Schussek", +                    "email": "bschussek@2bepublished.at" +                }, +                { +                    "name": "Sebastian Bergmann", +                    "email": "sebastian@phpunit.de" +                }, +                { +                    "name": "Adam Harvey", +                    "email": "aharvey@php.net" +                } +            ], +            "description": "Provides the functionality to export PHP variables for visualization", +            "homepage": "http://www.github.com/sebastianbergmann/exporter", +            "keywords": [ +                "export", +                "exporter" +            ], +            "time": "2016-11-19T08:54:04+00:00" +        }, +        { +            "name": "sebastian/global-state", +            "version": "1.0.0", +            "source": { +                "type": "git", +                "url": "https://github.com/sebastianbergmann/global-state.git", +                "reference": "c7428acdb62ece0a45e6306f1ae85e1c05b09c01" +            }, +            "dist": { +                "type": "zip", +                "url": "https://api.github.com/repos/sebastianbergmann/global-state/zipball/c7428acdb62ece0a45e6306f1ae85e1c05b09c01", +                "reference": "c7428acdb62ece0a45e6306f1ae85e1c05b09c01", +                "shasum": "" +            }, +            "require": { +                "php": ">=5.3.3" +            }, +            "require-dev": { +                "phpunit/phpunit": "~4.2" +            }, +            "suggest": { +                "ext-uopz": "*" +            }, +            "type": "library", +            "extra": { +                "branch-alias": { +                    "dev-master": "1.0-dev" +                } +            }, +            "autoload": { +                "classmap": [ +                    "src/" +                ] +            }, +            "notification-url": "https://packagist.org/downloads/", +            "license": [ +                "BSD-3-Clause" +            ], +            "authors": [ +                { +                    "name": "Sebastian Bergmann", +                    "email": "sebastian@phpunit.de" +                } +            ], +            "description": "Snapshotting of global state", +            "homepage": "http://www.github.com/sebastianbergmann/global-state", +            "keywords": [ +                "global state" +            ], +            "time": "2014-10-06T09:23:50+00:00" +        }, +        { +            "name": "sebastian/object-enumerator", +            "version": "2.0.0", +            "source": { +                "type": "git", +                "url": "https://github.com/sebastianbergmann/object-enumerator.git", +                "reference": "96f8a3f257b69e8128ad74d3a7fd464bcbaa3b35" +            }, +            "dist": { +                "type": "zip", +                "url": "https://api.github.com/repos/sebastianbergmann/object-enumerator/zipball/96f8a3f257b69e8128ad74d3a7fd464bcbaa3b35", +                "reference": "96f8a3f257b69e8128ad74d3a7fd464bcbaa3b35", +                "shasum": "" +            }, +            "require": { +                "php": ">=5.6", +                "sebastian/recursion-context": "~2.0" +            }, +            "require-dev": { +                "phpunit/phpunit": "~5" +            }, +            "type": "library", +            "extra": { +                "branch-alias": { +                    "dev-master": "2.0.x-dev" +                } +            }, +            "autoload": { +                "classmap": [ +                    "src/" +                ] +            }, +            "notification-url": "https://packagist.org/downloads/", +            "license": [ +                "BSD-3-Clause" +            ], +            "authors": [ +                { +                    "name": "Sebastian Bergmann", +                    "email": "sebastian@phpunit.de" +                } +            ], +            "description": "Traverses array structures and object graphs to enumerate all referenced objects", +            "homepage": "https://github.com/sebastianbergmann/object-enumerator/", +            "time": "2016-11-19T07:35:10+00:00" +        }, +        { +            "name": "sebastian/recursion-context", +            "version": "2.0.0", +            "source": { +                "type": "git", +                "url": "https://github.com/sebastianbergmann/recursion-context.git", +                "reference": "2c3ba150cbec723aa057506e73a8d33bdb286c9a" +            }, +            "dist": { +                "type": "zip", +                "url": "https://api.github.com/repos/sebastianbergmann/recursion-context/zipball/2c3ba150cbec723aa057506e73a8d33bdb286c9a", +                "reference": "2c3ba150cbec723aa057506e73a8d33bdb286c9a", +                "shasum": "" +            }, +            "require": { +                "php": ">=5.3.3" +            }, +            "require-dev": { +                "phpunit/phpunit": "~4.4" +            }, +            "type": "library", +            "extra": { +                "branch-alias": { +                    "dev-master": "2.0.x-dev" +                } +            }, +            "autoload": { +                "classmap": [ +                    "src/" +                ] +            }, +            "notification-url": "https://packagist.org/downloads/", +            "license": [ +                "BSD-3-Clause" +            ], +            "authors": [ +                { +                    "name": "Jeff Welch", +                    "email": "whatthejeff@gmail.com" +                }, +                { +                    "name": "Sebastian Bergmann", +                    "email": "sebastian@phpunit.de" +                }, +                { +                    "name": "Adam Harvey", +                    "email": "aharvey@php.net" +                } +            ], +            "description": "Provides functionality to recursively process PHP variables", +            "homepage": "http://www.github.com/sebastianbergmann/recursion-context", +            "time": "2016-11-19T07:33:16+00:00" +        }, +        { +            "name": "sebastian/resource-operations", +            "version": "1.0.0", +            "source": { +                "type": "git", +                "url": "https://github.com/sebastianbergmann/resource-operations.git", +                "reference": "ce990bb21759f94aeafd30209e8cfcdfa8bc3f52" +            }, +            "dist": { +                "type": "zip", +                "url": "https://api.github.com/repos/sebastianbergmann/resource-operations/zipball/ce990bb21759f94aeafd30209e8cfcdfa8bc3f52", +                "reference": "ce990bb21759f94aeafd30209e8cfcdfa8bc3f52", +                "shasum": "" +            }, +            "require": { +                "php": ">=5.6.0" +            }, +            "type": "library", +            "extra": { +                "branch-alias": { +                    "dev-master": "1.0.x-dev" +                } +            }, +            "autoload": { +                "classmap": [ +                    "src/" +                ] +            }, +            "notification-url": "https://packagist.org/downloads/", +            "license": [ +                "BSD-3-Clause" +            ], +            "authors": [ +                { +                    "name": "Sebastian Bergmann", +                    "email": "sebastian@phpunit.de" +                } +            ], +            "description": "Provides a list of PHP built-in functions that operate on resources", +            "homepage": "https://www.github.com/sebastianbergmann/resource-operations", +            "time": "2015-07-28T20:34:47+00:00" +        }, +        { +            "name": "sebastian/version", +            "version": "1.0.0", +            "source": { +                "type": "git", +                "url": "https://github.com/sebastianbergmann/version.git", +                "reference": "16b021aed448b654ae05846e394e057e9a6f04cb" +            }, +            "dist": { +                "type": "zip", +                "url": "https://api.github.com/repos/sebastianbergmann/version/zipball/16b021aed448b654ae05846e394e057e9a6f04cb", +                "reference": "16b021aed448b654ae05846e394e057e9a6f04cb", +                "shasum": "" +            }, +            "type": "library", +            "autoload": { +                "classmap": [ +                    "src/" +                ] +            }, +            "notification-url": "https://packagist.org/downloads/", +            "license": [ +                "BSD-3-Clause" +            ], +            "authors": [ +                { +                    "name": "Sebastian Bergmann", +                    "email": "sebastian@phpunit.de", +                    "role": "lead" +                } +            ], +            "description": "Library that helps with managing the version number of Git-hosted PHP projects", +            "homepage": "https://github.com/sebastianbergmann/version", +            "time": "2013-01-05T14:27:32+00:00" +        }, +        { +            "name": "symfony/yaml", +            "version": "v2.1.0", +            "target-dir": "Symfony/Component/Yaml", +            "source": { +                "type": "git", +                "url": "https://github.com/symfony/yaml.git", +                "reference": "f18e004fc975707bb4695df1dbbe9b0d8c8b7715" +            }, +            "dist": { +                "type": "zip", +                "url": "https://api.github.com/repos/symfony/yaml/zipball/f18e004fc975707bb4695df1dbbe9b0d8c8b7715", +                "reference": "f18e004fc975707bb4695df1dbbe9b0d8c8b7715", +                "shasum": "" +            }, +            "require": { +                "php": ">=5.3.3" +            }, +            "type": "library", +            "extra": { +                "branch-alias": { +                    "dev-master": "2.1-dev" +                } +            }, +            "autoload": { +                "psr-0": { +                    "Symfony\\Component\\Yaml": "" +                } +            }, +            "notification-url": "https://packagist.org/downloads/", +            "license": [ +                "MIT" +            ], +            "authors": [ +                { +                    "name": "Symfony Community", +                    "homepage": "http://symfony.com/contributors" +                }, +                { +                    "name": "Fabien Potencier", +                    "email": "fabien@symfony.com" +                } +            ], +            "description": "Symfony Yaml Component", +            "homepage": "http://symfony.com", +            "time": "2012-08-22T13:48:41+00:00" +        } +    ], +    "aliases": [], +    "minimum-stability": "stable", +    "stability-flags": [], +    "prefer-stable": false, +    "prefer-lowest": false, +    "platform": { +        "php": "^5.6 || ^7.0" +    }, +    "platform-dev": [] +} diff --git a/common/cron.php b/common/cron.php index 77d9d8f..9cc56a6 100644 --- a/common/cron.php +++ b/common/cron.php @@ -1,9 +1,13 @@  <?php -include_once(dirname(__FILE__).'/app/app.php'); +include_once(__DIR__.'/app/app.php');  //Load OPML -if (0 < $Planet->loadOpml(dirname(__FILE__).'/custom/people.opml')) { +if (0 < $Planet->loadOpml(__DIR__.'/custom/people.opml')) {      $Planet->download(1.0);  } -var_dump($Planet->errors); +if ($conf['debug'] === true) { +    foreach ($Planet->errors as $error) { +        echo $error->toString() . "\n"; +    } +}
\ No newline at end of file diff --git a/common/custom/img/moonmoon.png b/common/custom/img/moonmoon.png Binary files differnew file mode 100644 index 0000000..10f9736 --- /dev/null +++ b/common/custom/img/moonmoon.png diff --git a/common/custom/img/moonmoon@128w.png b/common/custom/img/moonmoon@128w.png Binary files differnew file mode 100644 index 0000000..2a164aa --- /dev/null +++ b/common/custom/img/moonmoon@128w.png diff --git a/common/custom/img/moonmoon@256w.png b/common/custom/img/moonmoon@256w.png Binary files differnew file mode 100644 index 0000000..b52c4bb --- /dev/null +++ b/common/custom/img/moonmoon@256w.png diff --git a/common/custom/style/default.css b/common/custom/style/default.css index de813ac..12268ef 100644 --- a/common/custom/style/default.css +++ b/common/custom/style/default.css @@ -1,128 +1,158 @@ -html{ +@viewport { +    width: device-width; +    initial-scale: 1.0; +} + +html {      font-family: "Lucida Grande", "Segoe UI", sans-serif;      color: #111;      background: #EEE;      font-size: 62.5%;  } -body{ +body {      margin: 0;      padding: 0;      font-size: 1.3em;      line-height: 1.5em;  } -a{ +a {      color: #669;  } -a:visited{ +a:visited {      color: #444;  } -a:hover{ +a:hover {      color: #000;  } -a img{ +a img {      border: none;  } -pre, code{ +pre, code {      max-height: 100%;      width: 100%;      font-size: 1.2em;      overflow: auto;  } -h1, h2{ +h1, h2 {      font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;  } -h2{ +h2 {      margin: 0;  }  #page { -    width: 890px; -    margin: 0 auto; +    margin: 0 5em;  }  /* Header  *******************************************************************************/ +  #top {      color: #FFF;      padding: 10px 30px;  } -    h1 a, -    h1 a:visited { -        color: #665; -        text-decoration: none; -    } -    h1 a:hover { -        color: #444; -    } + +h1 a, h1 a:visited { +    color: #665; +    text-decoration: none; +} + +h1 a:hover { +    color: #444; +}  /* Content  *******************************************************************************/ +  #content { -    width: 590px; +    width: 75%;      float: left;  } +#content img { +    max-width: 100%; +    height: auto; +} +  /* Sidebar  *******************************************************************************/ +  #sidebar { -    width: 300px; +    width: 18%;      float: left;  } -    #sidebar div { -        padding: 1em 30px; -    } -    #sidebar h2 { -        color: #665; -    } -    #sidebar ul { -        list-style: none; -        padding: 0; -        margin: 1em 0; -    } -     + +#sidebar div { +    padding: 1em 30px; +} + +#sidebar h2 { +    color: #665; +} + +#sidebar ul { +    list-style: none; +    padding: 0; +    margin: 1em 0; +} +  /* Article  *******************************************************************************/ -.article{ + +.article {      margin-bottom: 2em;      padding: 30px;      background: #FFF;      border: 1px solid #DDD; +    overflow: hidden;  } -    .article-title, -    .article-title a{ -        color: #111; -        text-decoration: none; -    } +.article-title, .article-title a { +    color: #111; +    text-decoration: none; +} -    .article-info{ -        margin: 0 0 1em 0; -        color: #999; -    } -     -    .article-content { -        font-family: Georgia, serif; -    } +.article-info { +    margin: 0 0 1em 0; +    color: #999; +} -    .article img{ -        max-width: 440px; -    } +.article-content { +    font-family: Georgia, serif; +} -    .article .collapsed{ -        display: none; -    } +.article img { +    max-width: 440px; +} + +.article .collapsed { +    display: none; +}  /* Footer  *******************************************************************************/ +  #footer {      clear: both;      padding: 0 30px; +} + +@media screen and (max-width: 1023px) { +    #page { +        margin: 0px; +        display: flex; +        flex-direction: column; +    } +    #content, #sidebar { +        float: none; +        width: 100%; +    }  }
\ No newline at end of file diff --git a/common/custom/views/archive/footer.tpl.php b/common/custom/views/archive/footer.tpl.php index bfc0210..c9c84b1 100755 --- a/common/custom/views/archive/footer.tpl.php +++ b/common/custom/views/archive/footer.tpl.php @@ -1,3 +1,3 @@          <div id="footer"> -            <p><? echo str_replace('%s', 'href="http://moonmoon.org"', _g('Powered by <a %s>moonmoon</a>'))?> | <a href="./admin/"><?=_g('Administration')?></a></p> +            <p><?php echo str_replace('%s', 'href="http://moonmoon.org"', _g('Powered by <a %s>moonmoon</a>'))?> | <a href="./admin/"><?=_g('Administration')?></a></p>          </div> diff --git a/common/custom/views/archive/index.tpl.php b/common/custom/views/archive/index.tpl.php index 2bb8f36..16bc6ab 100755 --- a/common/custom/views/archive/index.tpl.php +++ b/common/custom/views/archive/index.tpl.php @@ -29,12 +29,12 @@ header('Content-type: text/html; charset=UTF-8');      <meta http-equiv="Content-Style-Type" content="text/css" />      <title><?php echo $PlanetConfig->getName(); ?></title> -    <?php include(dirname(__FILE__).'/head.tpl.php'); ?> +    <?php include(__DIR__.'/head.tpl.php'); ?>  </head>  <body>      <div id="page"> -        <?php include(dirname(__FILE__).'/top.tpl.php'); ?> +        <?php include(__DIR__.'/top.tpl.php'); ?>          <div id="content">              <?php if (0 == count($items)) :?> @@ -106,9 +106,9 @@ header('Content-type: text/html; charset=UTF-8');              <?php endif; ?>          </div> -        <?php include_once(dirname(__FILE__).'/sidebar.tpl.php'); ?> +        <?php include_once(__DIR__.'/sidebar.tpl.php'); ?> -        <?php include(dirname(__FILE__).'/footer.tpl.php'); ?> +        <?php include(__DIR__.'/footer.tpl.php'); ?>      </div>  </body>  </html> diff --git a/common/custom/views/archive/sidebar.tpl.php b/common/custom/views/archive/sidebar.tpl.php index e21f377..7a5d080 100755 --- a/common/custom/views/archive/sidebar.tpl.php +++ b/common/custom/views/archive/sidebar.tpl.php @@ -1,5 +1,5 @@  <?php -$all_people = &$Planet->getPeople(); +$all_people = $Planet->getPeople();  usort($all_people, array('PlanetFeed', 'compare'));  ?>  <div id="sidebar"> diff --git a/common/custom/views/default/footer.tpl.php b/common/custom/views/default/footer.tpl.php index bfc0210..c9c84b1 100755 --- a/common/custom/views/default/footer.tpl.php +++ b/common/custom/views/default/footer.tpl.php @@ -1,3 +1,3 @@          <div id="footer"> -            <p><? echo str_replace('%s', 'href="http://moonmoon.org"', _g('Powered by <a %s>moonmoon</a>'))?> | <a href="./admin/"><?=_g('Administration')?></a></p> +            <p><?php echo str_replace('%s', 'href="http://moonmoon.org"', _g('Powered by <a %s>moonmoon</a>'))?> | <a href="./admin/"><?=_g('Administration')?></a></p>          </div> diff --git a/common/custom/views/default/index.tpl.php b/common/custom/views/default/index.tpl.php index 1bf037a..c33ffa5 100755 --- a/common/custom/views/default/index.tpl.php +++ b/common/custom/views/default/index.tpl.php @@ -10,9 +10,10 @@ header('Content-type: text/html; charset=UTF-8');      <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />      <meta http-equiv="Content-Script-Type" content="text/javascript" />      <meta http-equiv="Content-Style-Type" content="text/css" /> +    <meta name="viewport" content="width=device-width, initial-scale=1.0" />      <title><?php echo $PlanetConfig->getName(); ?></title> -    <?php include(dirname(__FILE__).'/head.tpl.php'); ?> +    <?php include(__DIR__.'/head.tpl.php'); ?>  </head>  <body> @@ -20,7 +21,7 @@ header('Content-type: text/html; charset=UTF-8');      document.body.className += 'js';      </script>      <div id="page"> -        <?php include(dirname(__FILE__).'/top.tpl.php'); ?> +        <?php include(__DIR__.'/top.tpl.php'); ?>          <div id="content">              <?php if (0 == count($items)) : ?> @@ -41,8 +42,7 @@ header('Content-type: text/html; charset=UTF-8');                              <a href="<?php echo $item->get_permalink(); ?>" title="Go to original place"><?php echo $item->get_title(); ?></a>                          </h2>                          <p class="article-info"> - -                            <?php echo ($item->get_author()? $item->get_author()->get_name() : 'Anonymous'); ?>, +                            <?php echo strip_tags(($item->get_author()? $item->get_author()->get_name() : 'Anonymous')); ?>,                              <?php                              $ago = time() - $item->get_date('U');                              //echo '<span title="'.Duration::toString($ago).' ago" class="date">'.date('d/m/Y', $item->get_date('U')).'</span>'; @@ -65,9 +65,9 @@ header('Content-type: text/html; charset=UTF-8');              <?php endif; ?>          </div> -        <?php include_once(dirname(__FILE__).'/sidebar.tpl.php'); ?> +        <?php include_once(__DIR__.'/sidebar.tpl.php'); ?> -        <?php include(dirname(__FILE__).'/footer.tpl.php'); ?> +        <?php include(__DIR__.'/footer.tpl.php'); ?>      </div>  </body>  </html> diff --git a/common/custom/views/default/sidebar.tpl.php b/common/custom/views/default/sidebar.tpl.php index 830f3c6..13ae626 100755 --- a/common/custom/views/default/sidebar.tpl.php +++ b/common/custom/views/default/sidebar.tpl.php @@ -1,5 +1,5 @@  <?php -$all_people = &$Planet->getPeople(); +$all_people = $Planet->getPeople();  usort($all_people, array('PlanetFeed', 'compare'));  ?>  <div id="sidebar" class="aside"> diff --git a/common/custom/views/default/top.tpl.php b/common/custom/views/default/top.tpl.php index dd6206f..6db425c 100644 --- a/common/custom/views/default/top.tpl.php +++ b/common/custom/views/default/top.tpl.php @@ -1,3 +1,3 @@          <div id="header"> -            <h1 id="top"><a href="<?php echo $PlanetConfig->getUrl() ?>"><?php echo $PlanetConfig->getName(); ?></a></h1> +            <h1 id="top"><a href="<?php echo $PlanetConfig->getUrl(); ?>"><?php echo $PlanetConfig->getName(); ?></a></h1>          </div>
\ No newline at end of file diff --git a/common/custom/views/install.tpl.php b/common/custom/views/install.tpl.php new file mode 100644 index 0000000..9eaff65 --- /dev/null +++ b/common/custom/views/install.tpl.php @@ -0,0 +1,119 @@ +<!DOCTYPE html> +<html lang="en"> +<meta charset="utf-8"/> +<head> +    <title><?=_g('moonmoon installation')?></title> +    <style> +        body { +            font: normal 1em sans-serif; +            width: 500px; +            margin: 0 auto; +        } + +        /* Error */ +        td.ok { +            color: #090; +        } + +        td.fail { +            color: #900; +            font-weight: bold; +        } +        th { +            text-align: left; +        } + +        /* Install */ +        .field label { +            display: block; +        } + +        .submit { +            font-size: 2em; +        } + +    </style> +</head> + +<body> +<h1><?=_g('moonmoon installation')?></h1> + +<?php if ($status == 'error') : ?> +    <div id="compatibility"> +        <h2>Sorry, your server is not compatible with moonmoon.</h2> + +        <h3>Your server does not fulfill the requirements</h3> +        <table> +            <thead> +            <tr> +                <th>Test</th> +                <th>Result</th> +            </tr> +            </thead> +            <tbody> +            <?php echo $strInstall ?> +            </tbody> +        </table> + +        <h3>Troubleshooting</h3> +        <p>To install moonmoon, try the following changes:</p> +        <ul> +            <?php echo $strRecommendation; ?> +        </ul> +    </div> + +<?php elseif ($status == 'install') : ?> +    <div> +        <form method="post" action=""> +            <fieldset> +                <input type="hidden" id="url" name="url" value="" readonly="readonly"/> +                <script> +                    <!-- +                    document.forms[0].elements[1].value = document.URL.replace('install.php',''); +                    --> +                </script> + +                <p class="field"> +                    <label for="title">Title:</label> +                    <input type="text" id="title" name="title" value="My website"/> +                </p> +                <!-- +                <p class="field"> +                    <label>Administrator login:</label> <code>admin</code> +                </p> +                --> +                <p class="field"> +                    <label for="password">Administrator password:</label> +                    <input type="text" id="password" name="password" class="text password" value="admin" /> +                </p> +                <p class="field"> +                    <label for="locale">Language:</label> +                    <select name="locale" id="locale"> +                        <option selected="selected" value="en">English</option> +                        <option value="es">Español</option> +                        <option value="fr">Français</option> +                        <option value="en">Deutsch</option> +                    </select> +                </p> +                <p> +                    <input type="submit" class="submit" value="Install"/> +                </p> +            </fieldset> +        </form> +    </div> + +<?php elseif ($status =='installed'): ?> + +    <p><?=_g('Congratulations! Your moonmoon is ready.')?></p> +    <h3><?=_g("What's next?")?></h3> +    <ol> +        <li> +            <?=_g('<strong>Delete</strong> <code>install.php</code> with your FTP software.')?> +        </li> +        <li> +            <?=_g('Use your password to go to the <a href="./admin/">administration panel</a>')?> +        </li> +    </ol> +<?php endif; ?> +</body> +</html> diff --git a/common/index.php b/common/index.php index 60d5e77..3a2c1e4 100755 --- a/common/index.php +++ b/common/index.php @@ -1,6 +1,6 @@  <?php -include_once(dirname(__FILE__).'/app/app.php'); -include_once(dirname(__FILE__).'/app/lib/Cache.php'); +include_once(__DIR__.'/app/app.php'); +include_once(__DIR__.'/app/lib/Cache.php');  //Installed ?  if (!isset($Planet)) { @@ -10,7 +10,7 @@ if (!isset($Planet)) {  //Load from cache  $items = Array(); -if (0 < $Planet->loadOpml(dirname(__FILE__).'/custom/people.opml')) { +if (0 < $Planet->loadOpml(__DIR__.'/custom/people.opml')) {      $Planet->loadFeeds();      $items = $Planet->getItems();  } @@ -21,7 +21,7 @@ $cache_key      = (count($items)) ? $items[0]->get_id()   : '';  $last_modified  = (count($items)) ? $items[0]->get_date() : '';  $cache_duration = $PlanetConfig->getOutputTimeout()*60; -Cache::setStore(dirname(__FILE__) . '/' . $conf['cachedir'] . '/'); +Cache::setStore(__DIR__ . '/' . $conf['cachedir'] . '/');  if (isset($_GET['type']) && $_GET['type'] == 'atom10') {      /* XXX: Redirect old ATOM feeds to new url to make sure our users don't @@ -35,16 +35,18 @@ if (isset($_GET['type']) && $_GET['type'] == 'atom10') {  //Go display  if (!isset($_GET['type']) || -    !is_file(dirname(__FILE__).'/custom/views/'.$_GET['type'].'/index.tpl.php') || -    strpos($_GET['type'], DIRECTORY_SEPARATOR)){ +    !is_file(__DIR__.'/custom/views/'.$_GET['type'].'/index.tpl.php') || +    strpos($_GET['type'], DIRECTORY_SEPARATOR) || strpos($GET['type'], '..')){      $_GET['type'] = 'default';  }  if (!OutputCache::Start($_GET['type'], $cache_key, $cache_duration)) { -    include_once(dirname(__FILE__).'/custom/views/'.$_GET['type'].'/index.tpl.php'); +    include_once(__DIR__.'/custom/views/'.$_GET['type'].'/index.tpl.php');      OutputCache::End();  } -echo "<!--"; -var_dump($Planet->errors); -echo "-->"; +if ($conf['debug'] === true) { +    echo "<!-- \$Planet->errors:\n"; +    var_dump($Planet->errors); +    echo "-->"; +} diff --git a/common/install.php b/common/install.php index 3a3eae7..86e1123 100755 --- a/common/install.php +++ b/common/install.php @@ -1,6 +1,6 @@  <?php -require_once dirname(__FILE__) . '/app/app.php'; +require_once __DIR__ . '/app/app.php';  // This is an helper function returning an html table row to avoid code duplication  function installStatus($str, $msg, $result) { @@ -8,31 +8,32 @@ function installStatus($str, $msg, $result) {      return '<tr><td>' . $str . '</td><td class="' . $class . '">' . $msg . '</td></tr>';  } -// If the password and config files exist, moonmoon is already installed -if (file_exists(dirname(__FILE__) . '/custom/config.yml') -    && file_exists(dirname(__FILE__) . '/admin/inc/pwd.inc.php')) { +// If the config file exists and the auth variables are set, moonmoon is already installed +if (is_installed()) {      $status = 'installed'; -} elseif (isset($_REQUEST['url'])) { +} elseif (isset($_POST['url'])) { + +    // Do no try to use the file of an invalid locale +    if (strstr($_POST['locale'], '..') !== false +    || !file_exists(__DIR__ . "/app/l10n/${_REQUEST['locale']}.lang")) { +        $_POST['locale'] = 'en'; +    } +      $save = array();      //Save config file -    $config = array( -        'url'           => filter_var($_REQUEST['url'],   FILTER_SANITIZE_ENCODED), -        'name'          => filter_var($_REQUEST['title'], FILTER_SANITIZE_SPECIAL_CHARS), -        'locale'        => filter_var($_REQUEST['locale'], FILTER_SANITIZE_SPECIAL_CHARS), -        'items'         => 10, -        'shuffle'       => 0, -        'refresh'       => 240, -        'cache'         => 10, -        'nohtml'        => 0, -        'postmaxlength' => 0, -        'cachedir'      => './cache' -    ); +    $config = array_merge(PlanetConfig::$defaultConfig, [ +        'url'    => $_POST['url'], +        'name'   => filter_var($_POST['title'], FILTER_SANITIZE_SPECIAL_CHARS), +        'locale' => $_POST['locale'], +    ]);      $CreatePlanetConfig = new PlanetConfig($config); -    $save['config'] = file_put_contents(dirname(__FILE__).'/custom/config.yml', $CreatePlanetConfig->toYaml()); +    $save['config'] = file_put_contents(custom_path('config.yml'), $CreatePlanetConfig->toYaml()); + +    OpmlManager::save(new Opml(), custom_path('people.opml'));      //Save password -    $save['password'] = file_put_contents(dirname(__FILE__).'/admin/inc/pwd.inc.php', '<?php $login="admin"; $password="'.md5($_REQUEST['password']).'"; ?>'); +    $save['password'] = file_put_contents(admin_path('inc/pwd.inc.php'), '<?php $login="admin"; $password="'.md5($_POST['password']).'"; ?>');      if (0 != ($save['config'] + $save['password'])) {          $status = 'installed'; @@ -40,12 +41,12 @@ if (file_exists(dirname(__FILE__) . '/custom/config.yml')  } else {      // We start by malking sure we have PHP5 as a base requirement -    if(phpversion() >= 5) { -        $strInstall = installStatus('Server is running PHP5', 'OK',true); +    if(version_compare(PHP_VERSION, '5.6.0') >= 0) { +        $strInstall = installStatus('Server is running at least PHP 5.6', 'OK',true);          $strRecommendation = '';      } else { -        $strInstall = installStatus('Server is running PHP5', 'FAIL',false); -        $strRecommendation = '<li>Check your server documentation to activate PHP5</li>'; +        $strInstall = installStatus('Server is running at least PHP 5.6', 'FAIL',false); +        $strRecommendation = '<li>Check your server documentation to activate at least PHP 5.6</li>';      }      // Writable file requirements @@ -56,10 +57,11 @@ if (file_exists(dirname(__FILE__) . '/custom/config.yml')          '/cache',      ); -    // We now test that all required files are writable +    // We now test that all required files and directories are writable.      foreach ($tests as $v) { -        if(is_writable(dirname(__FILE__) . $v)) { +        if(touch(__DIR__ . $v)) {              $strInstall .= installStatus("<code>$v</code> is writable", 'OK', true); +            unlink(__DIR__.$v);          } else {              $strInstall .= installStatus("<code>$v</code> is writable", 'FAIL',false);              $strRecommendation .= "<li>Make <code>$v</code> writable with CHMOD</li>"; @@ -70,121 +72,5 @@ if (file_exists(dirname(__FILE__) . '/custom/config.yml')      $status = ($strRecommendation != '') ? 'error' : 'install';  } -?> -<!DOCTYPE html> -<html lang="en"> -<meta charset="utf-8"> -<head> -    <title><?=_g('moonmoon installation')?></title> -    <style> -    body { -        font: normal 1em sans-serif; -        width: 500px; -        margin: 0 auto; -    } - -    /* Error */ -    td.ok { -        color: #090; -    } - -    td.fail { -        color: #900; -        font-weight: bold; -    } -    th { -        text-align: left; -    } - -    /* Install */ -    .field label { -        display: block; -    } - -    .submit { -        font-size: 2em; -    } - -    </style> -</head> - -<body> -    <h1><?=_g('moonmoon installation')?></h1> - -    <?php if ($status == 'error') : ?> -    <div id="compatibility"> -        <h2>Sorry, your server is not compatible with moonmoon.</h2> - -        <h3>Your server does not fulfill the requirements</h3> -        <table> -            <thead> -                <tr> -                    <th>Test</th> -                    <th>Result</th> -                </tr> -            </thead> -            <tbody> -                <?php echo $strInstall ?> -            </tbody> -        </table> - -        <h3>Troubleshooting</h3> -        <p>To install moonmoon, try the following changes:</p> -        <ul> -            <?php echo $strRecommendation; ?> -        </ul> -    </div> - -    <?php elseif ($status == 'install') : ?> -    <div> -        <form method="post" action=""> -            <fieldset> -                <input type="hidden" id="url" name="url" value="" readonly="readonly"/> -                <script> -                <!-- -                document.forms[0].elements[1].value = document.URL.replace('install.php',''); -                --> -                </script> - -                <p class="field"> -                    <label for="title">Title:</label> -                    <input type="text" id="title" name="title" value="My website"/> -                </p> -                <!-- -                <p class="field"> -                    <label>Administrator login:</label> <code>admin</code> -                </p> -                --> -                <p class="field"> -                    <label for="password">Administrator password:</label> -                    <input type="text" id="password" name="password" class="text password" value="admin" /> -                </p> -                <p class="field"> -                    <label for="locale">Language:</label> -                    <select name="locale" id="locale"> -                        <option selected="selected" value="en">English</option> -                        <option value="fr">Français</option> -                    </select> -                </p> -                <p> -                    <input type="submit" class="submit" value="Install"/> -                </p> -            </fieldset> -        </form> -    </div> - -    <?php elseif ($status =='installed'): ?> -    <p><?=_g('Congratulations! Your moonmoon is ready.')?></p> -    <h3><?=_g("What's next?")?></h3> -    <ol> -        <li> -            <?=_g('<strong>Delete</strong> <code>install.php</code> with your FTP software.')?> -        </li> -        <li> -            <?=_g('Use your password to go to the <a href="./admin/">administration panel</a>')?> -        </li> -    </ol> -    <?php endif; ?> -</body> -</html> +require_once views_path('install.tpl.php');
\ No newline at end of file diff --git a/common/phpunit.xml b/common/phpunit.xml new file mode 100644 index 0000000..835272f --- /dev/null +++ b/common/phpunit.xml @@ -0,0 +1,20 @@ +<?xml version="1.0" encoding="UTF-8"?> +<phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" +         xsi:noNamespaceSchemaLocation="https://schema.phpunit.de/5.7/phpunit.xsd" +         bootstrap="vendor/autoload.php" +         backupGlobals="false" +         beStrictAboutCoversAnnotation="true" +         beStrictAboutOutputDuringTests="true" +         beStrictAboutTestsThatDoNotTestAnything="true" +         beStrictAboutTodoAnnotatedTests="true" +         verbose="true"> +    <testsuite> +        <directory suffix="Test.php">tests</directory> +    </testsuite> + +    <filter> +        <whitelist processUncoveredFilesFromWhitelist="true"> +            <directory suffix=".php">.</directory> +        </whitelist> +    </filter> +</phpunit> diff --git a/common/postload.php b/common/postload.php index 8f18678..7341b82 100644 --- a/common/postload.php +++ b/common/postload.php @@ -1,16 +1,30 @@  <?php -include_once(dirname(__FILE__).'/app/app.php'); -$Planet->addPerson( -    new PlanetFeed( -        '', -        htmlspecialchars_decode($_GET['url'], ENT_QUOTES), -        '' -    ) -); +require_once __DIR__.'/app/app.php'; -//Load feeds -$Planet->download(1); -header("Content-type: image/png"); -readfile(dirname(__FILE__)."/custom/img/feed.png"); -die(); +if (!is_installed()) { +    die(); +} + +$xml = new SimpleXMLElement(file_get_contents(custom_path('people.opml'))); + +foreach ($xml->xpath('/opml/body/outline[@xmlUrl]') as $element) +{ +    if ($element->attributes()->xmlUrl == $_GET['url']) +    { +        $person = new PlanetFeed( +            '', +            $_GET['url'], +            '', +            false +        ); +        $Planet->addPerson($person); + +        $Planet->download(1); +        header('Content-type: image/png'); +        readfile(custom_path('img/feed.png')); +        die(); +    } +} + +echo 'Updating this URL is not allowed.';
\ No newline at end of file diff --git a/common/tests/GuzzleHarness.php b/common/tests/GuzzleHarness.php new file mode 100644 index 0000000..a3f2ac4 --- /dev/null +++ b/common/tests/GuzzleHarness.php @@ -0,0 +1,20 @@ +<?php + +use \PHPUnit\Framework\TestCase; +use \GuzzleHttp\Client; + +class GuzzleHarness extends TestCase +{ + +    /** @var GuzzleHttp\Client */ +    protected $client = null; + +    public function setUp() +    { +        $this->client = new Client([ +            'base_uri' => 'http://127.0.0.1:8081', +            'timeout' => 1, +        ]); +    } + +}
\ No newline at end of file diff --git a/common/tests/HelpersTest.php b/common/tests/HelpersTest.php new file mode 100644 index 0000000..141e604 --- /dev/null +++ b/common/tests/HelpersTest.php @@ -0,0 +1,15 @@ +<?php + +use PHPUnit\Framework\TestCase; + +class HelpersTest extends TestCase +{ +    function test_constant_time_compare() +    { +        $this->assertTrue(_hash_equals('abc', 'abc')); +        $this->assertFalse(_hash_equals('abc', 'ab')); +        $this->assertFalse(_hash_equals('ab', 'abc')); +        $this->assertFalse(_hash_equals('abcd', 'adbc')); +        $this->assertFalse(_hash_equals(0, 0)); +    } +} diff --git a/common/tests/InstallTest.php b/common/tests/InstallTest.php new file mode 100644 index 0000000..7615f18 --- /dev/null +++ b/common/tests/InstallTest.php @@ -0,0 +1,61 @@ +<?php + +require_once 'GuzzleHarness.php'; + +class InstallTest extends GuzzleHarness { + +    public function setUp() +    { +        parent::setUp(); +        removeCustomFiles(); +    } + +    public function tearDown() +    { +        parent::tearDown(); +        removeCustomFiles(); +    } + +    public function test_index_page_tells_moonmoon_is_not_installed() +    { +        $res = $this->client->get('/index.php'); +        $this->assertEquals(200, $res->getStatusCode()); +        $this->assertContains('install moonmoon', (string) $res->getBody()); +    } + +    public function test_install_page_loads_without_error() +    { +        $res = $this->client->get('/install.php'); +        $this->assertEquals(200, $res->getStatusCode()); +        $this->assertContains('Administrator password', (string) $res->getBody()); +    } + +    /** +     * Regression test, `people.opml` was created by requesting `/install.php` +     * even if the site was not installed: `touch()` was called to see if +     * the path was writable but the file was not removed. +     */ +    public function test_get_install_page_should_not_create_custom_files() +    { +        $this->client->get('/install.php'); +        $this->assertFalse(file_exists(custom_path('people.opml'))); +        $this->assertFalse(file_exists(custom_path('config.yml'))); +        $this->assertFalse(file_exists(custom_path('inc/pwc.inc.php'))); +    } + +    public function test_install_button() +    { +        $data = [ +            'url' => 'http://127.0.0.1:8081/', +            'title'	=> 'My website', +            'password' => 'admin', +            'locale' => 'en', +        ]; + +        $res = $this->client->request('POST', '/install.php', [ +            'form_params' => $data +        ]); +        $this->assertEquals(200, $res->getStatusCode()); +        $this->assertContains('Your moonmoon is ready.', (string) $res->getBody()); +    } +}
\ No newline at end of file diff --git a/common/tests/PlanetConfigTest.php b/common/tests/PlanetConfigTest.php new file mode 100644 index 0000000..4db6e90 --- /dev/null +++ b/common/tests/PlanetConfigTest.php @@ -0,0 +1,73 @@ +<?php + +use PHPUnit\Framework\TestCase; + +class PlanetConfigTest extends TestCase +{ +    public function test_default_configuration_values() +    { +        $conf = new PlanetConfig(); +        $this->assertEquals('http://www.example.com/', $conf->getUrl()); +    } + +    public function test_merge_user_configuration_with_default_one() +    { +        $conf = new PlanetConfig(['url' => 'http://foobar.tld']); +        $this->assertEquals('http://foobar.tld', $conf->getUrl()); +    } + +    public function test_generic_getter() +    { +        $conf = new PlanetConfig(['foo' => 'bar']); +        $this->assertEquals('bar', $conf->foo); +    } + +    public function test_generic_setter() +    { +        $conf = new PlanetConfig(); +        $conf->foo = 'bar'; +        $this->assertEquals('bar', $conf->foo); +    } + +    public function test_normalize_key_name_on_merge() +    { +        $conf = new PlanetConfig(['FOO' => 'bar']); +        $this->assertEquals('bar', $conf->foo); +    } + +    public function test_normalize_key_name_on_generic_getter() +    { +        $conf = new PlanetConfig(['foo' => 'bar']); +        $this->assertEquals('bar', $conf->FOO); +    } + +    public function test_normalize_key_name_on_generic_setter() +    { +        $conf = new PlanetConfig(); +        $conf->FOO = 'bar'; +        $this->assertEquals('bar', $conf->foo); +    } + +    public function test_to_array() +    { +        $conf = new PlanetConfig(['foo' => 'bar']); +        $this->assertEquals('bar', $conf->toArray()['foo']); +        $this->assertEquals('http://www.example.com/', $conf->toArray()['url']); +    } + +    public function test_constructor_without_default_config() +    { +        $conf = new PlanetConfig(['foo' => 'bar'], false); +        $this->assertEquals('bar', $conf->foo); +        $this->assertEquals(1, sizeof($conf->toArray())); +    } + +    public function test_to_yaml() +    { +        $conf = new PlanetConfig([], false); +        $this->assertEquals("---\n", $conf->toYaml()); + +        $conf = new PlanetConfig(['foo' => 'bar'], false); +        $this->assertEquals("---\nfoo: bar\n", $conf->toYaml()); +    } +} diff --git a/common/tests/PlanetErrorTest.php b/common/tests/PlanetErrorTest.php new file mode 100644 index 0000000..d2f4599 --- /dev/null +++ b/common/tests/PlanetErrorTest.php @@ -0,0 +1,12 @@ +<?php + +use PHPUnit\Framework\TestCase; + +class PlanetErrorTest extends TestCase +{ +    public function test_to_string() +    { +        $error = new PlanetError(1, 'foo'); +        $this->assertEquals('notice: foo', $error->toString()); +    } +}
\ No newline at end of file diff --git a/common/tests/PlanetTest.php b/common/tests/PlanetTest.php new file mode 100644 index 0000000..5cec1ce --- /dev/null +++ b/common/tests/PlanetTest.php @@ -0,0 +1,84 @@ +<?php + +use PHPUnit\Framework\TestCase; + +class FoolCategory { + +    protected $name; + +    function __construct($name) +    { +        $this->name = $name; +    } + +    function get_label() +    { +        return $this->name; +    } +} + +class FoolItem +{ +    protected $categories; + +    function __construct($categories) +    { +        foreach ($categories as $c) +            $this->categories[] = new FoolCategory($c); +    } + +    function get_categories() { +        return $this->categories; +    } +} + +class PlanetTest extends TestCase +{ + +    protected $planet; +    protected $items; + +    public function setUp() +    { +        $this->planet = new Planet(); + +        $this->items = array( +            new FoolItem(array('catA', 'catB', 'catC')), +            new FoolItem(array('catB')), +            new FoolItem(array('catA')), +            new FoolItem(array('catC')) +        ); +    } + +    protected function _after() +    { +        unset($this->planet); +    } + +    public function testFilterItemsByCategoryWithInvalidCategory() +    { +        $this->assertEquals(count($this->planet->_filterItemsByCategory($this->items, null)), count($this->items)); +        $this->assertEquals(count($this->planet->_filterItemsByCategory($this->items, ' ')), count($this->items)); +    } + +    public function testFilterItemsByCategoryWithNonUsedCategory() +    { +        $this->assertEquals(count($this->planet->_filterItemsByCategory($this->items, 'catD')), 0); +    } + +    public function testFilterItemsByCategoryWithValidCategory() +    { +        $this->assertEquals(count($this->planet->_filterItemsByCategory($this->items, 'catA')), 2); +        $this->assertEquals(count($this->planet->_filterItemsByCategory($this->items, 'catB')), 2); +        $this->assertEquals(count($this->planet->_filterItemsByCategory($this->items, 'catC')), 2); +        $this->assertEquals(count($this->planet->_filterItemsByCategory($this->items, 'CATA')), 2); +    } + +    public function testFilterItemsByCategoryWithMultipleCategory() +    { +        $this->assertEquals(count($this->planet->_filterItemsByCategory($this->items, 'catA,catB')), 3); +        $this->assertEquals(count($this->planet->_filterItemsByCategory($this->items, 'catA,catB,catC')), 4); +        $this->assertEquals(count($this->planet->_filterItemsByCategory($this->items, 'catA, catB')), 3); +    } + +}  | 
