From: Nicolas Lœuillet Date: Thu, 8 Aug 2013 16:36:10 +0000 (-0700) Subject: Merge pull request #109 from inthepoche/dev X-Git-Tag: 1.0-beta3~3 X-Git-Url: https://git.immae.eu/?a=commitdiff_plain;h=9a8b4ff4edf84d7df60de1b6fd1e493b59f88273;hp=85ebc80c7eaf88e4d57a52adb8e4c32d8cc34b64;p=github%2Fwallabag%2Fwallabag.git Merge pull request #109 from inthepoche/dev merge dev into master --- diff --git a/.gitignore b/.gitignore new file mode 100644 index 00000000..5e992c2e --- /dev/null +++ b/.gitignore @@ -0,0 +1,5 @@ +vendor +composer.phar +db/poche.sqlite +output +phpdoc* \ No newline at end of file diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 00000000..9d6ba132 --- /dev/null +++ b/.travis.yml @@ -0,0 +1,15 @@ +language: php + +php: + - 5.4 + +branches: + only: + - dev + +before_script: + - composer install + +notifications: + email: + - nicolas.loeuillet@gmail.com \ No newline at end of file diff --git a/CREDITS b/CREDITS index c917a012..a6dedce4 100644 --- a/CREDITS +++ b/CREDITS @@ -1,16 +1,14 @@ poche is based on : -* ReadItYourself http://www.memiks.fr/readityourself/ -* PHP Readability http://www.keyvan.net/2010/08/php-readability/ +* PHP Readability https://bitbucket.org/fivefilters/php-readability * Encoding https://github.com/neitanod/forceutf8 * logo by Brightmix http://www.iconfinder.com/icondetails/43256/128/jeans_monotone_pocket_icon * icons http://icomoon.io * PHP Simple HTML DOM Parser (for Pocket import) http://simplehtmldom.sourceforge.net/ * Session https://github.com/tontof/kriss_feed/blob/master/src/class/Session.php +* Twig http://twig.sensiolabs.org +* Flash messages https://github.com/plasticbrain/PHP-Flash-Messages +* Pagination https://github.com/daveismyname/pagination poche is developed by Nicolas Lœuillet under the Do What the Fuck You Want to Public License -Contributors : -Nicolas Lœuillet aka nico_somb -Tom.C. aka tmos -PeaceCopathe -Gregoire_M \ No newline at end of file +Contributors : https://github.com/inthepoche/poche/graphs/contributors \ No newline at end of file diff --git a/INSTALL.md b/INSTALL.md new file mode 100644 index 00000000..cf027282 --- /dev/null +++ b/INSTALL.md @@ -0,0 +1,53 @@ +# Installing poche + +Get the [latest dev version](https://github.com/inthepoche/poche/archive/dev.zip) of poche on github. Unzip it and upload it on your server. + +your datas can be stored on sqlite, postgres or mysql databases. + +Edit /inc/poche/config.inc.php : + +```php +define ('STORAGE','sqlite'); # postgres, mysql, sqlite +define ('STORAGE_SERVER', 'localhost'); # leave blank for sqlite +define ('STORAGE_DB', 'poche'); # only for postgres & mysql +define ('STORAGE_SQLITE', './db/poche.sqlite'); +define ('STORAGE_USER', 'user'); # leave blank for sqlite +define ('STORAGE_PASSWORD', 'pass'); # leave blank for sqlite +``` + +poche must have write access on assets, cache and db directories. + +[PHP cURL](http://www.php.net/manual/en/book.curl.php) & [tidy_parse_string](http://www.php.net/manual/en/tidy.parsestring.php) are recommended. + +## twig +poche now uses twig for templating. You have to install twig. + +Install composer in your project : +```bash +curl -s http://getcomposer.org/installer | php +``` +Install via composer : +```bash +php composer.phar install +``` + +If you don't want to install twig by yourself, you can download [this file](http://static.inthepoche.com/files/poche-1.0-latest-with-twig.zip). + +## storage in sqlite +You have to install [sqlite for php](http://www.php.net/manual/en/book.sqlite.php) on your server. + +Copy /install/poche.sqlite in /db + +## storage in mysql +Execute /install/mysql.sql file in your database. + +## storage in postgres +Execute /install/postgres.sql file in your database. + +## upgrading from poche <= 0.3 +With poche <= 0.3, all your datas were stored in a sqlite file. The structure of this file changed. + +You have to execute http://yourpoche/install/update_sqlite_from_0_to_1.php before using this new version. + +## installing poche +you can go on your poche http://yourpoche. You have to fill the fields and that's all ! \ No newline at end of file diff --git a/README.md b/README.md index b44e7d36..5bea7ca4 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,5 @@ # poche -Abandon Pocket, Instapaper and other Readability service : adopt poche. It is the same, but it is open source. +Abandon Pocket, Instapaper and other Readability service : adopt poche. It is the same, but it is open source. Moreover, you can migrate from Pocket & Readability. ![poche](http://inthepoche.com/img/logo.png) @@ -11,24 +11,6 @@ To get news from poche, [follow us on twitter](http://twitter.com/getpoche) or [ [![flattr](http://api.flattr.com/button/flattr-badge-large.png)](http://flattr.com/thing/1265480/poche-a-read-it-later-open-source-system) -## Usage -You can easily add a "poched" page with the bookmarklet. - -poche save the entire content of a poched links : text and pictures are stored on your server. - -You can : -* read a page in a comfortable reading view -* archive a link -* put a link in favorite -* delete a link - -## Requirements & installation -You have to install [sqlite for php](http://www.php.net/manual/en/book.sqlite.php) on your server. - -Get the [latest version](https://github.com/inthepoche/poche) of poche on github. Unzip it and upload it on your server. poche must have write access on assets, cache and db directories. - -That's all, **poche works** ! - ## Security You **have** to protect your db/poche.sqlite file. Modify the virtual host of your website to add this condition : ```apache @@ -46,12 +28,14 @@ location ~ /(db) { } ``` -## Import from Pocket +## Usage +See the documentation on our website : [inthepoche.com](http://inthepoche.com). -If you want to import your Pocket datas, [export them here](https://getpocket.com/export). Put the HTML file in your poche directory, execute import.php file locally by following instructions. Be careful, the script can take a very long time. +## Travis +[![Build Status](https://api.travis-ci.org/inthepoche/poche.png?branch=dev)](http://travis-ci.org/#!/inthepoche/poche) ## License -Copyright © 2010-2013 Nicolas Lœuillet +Copyright © 2010-2013 Nicolas Lœuillet This work is free. You can redistribute it and/or modify it under the terms of the Do What The Fuck You Want To Public License, Version 2, -as published by Sam Hocevar. See the COPYING file for more details. +as published by Sam Hocevar. See the COPYING file for more details. \ No newline at end of file diff --git a/TODO.md b/TODO.md new file mode 100644 index 00000000..ac3c0e98 --- /dev/null +++ b/TODO.md @@ -0,0 +1,11 @@ +# TODO + +pouvoir annuler la suppression +conventions codage ? phing ? vérifier error_log qui trainent +phpDocumentor +minifier css +revoir tous les css +barre fixe d'admin sur la page d'un billet ? +revoir export (export vers pocket &cie ? ) +raccourcis clavier +date d'ajout d'un lien \ No newline at end of file diff --git a/composer.json b/composer.json new file mode 100644 index 00000000..6c69e48d --- /dev/null +++ b/composer.json @@ -0,0 +1,7 @@ +{ + "require": { + "twig/twig": "1.*", + "twig/extensions": "1.0.*", + "umpirsky/twig-gettext-extractor": "1.1.*" + } +} \ No newline at end of file diff --git a/composer.lock b/composer.lock new file mode 100644 index 00000000..9e17fca9 --- /dev/null +++ b/composer.lock @@ -0,0 +1,744 @@ +{ + "_readme": [ + "This file locks the dependencies of your project to a known state", + "Read more about it at http://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file" + ], + "hash": "1c8badb14d91f4f3ef1cfae23252a2c4", + "packages": [ + { + "name": "symfony/event-dispatcher", + "version": "v2.3.2", + "target-dir": "Symfony/Component/EventDispatcher", + "source": { + "type": "git", + "url": "https://github.com/symfony/EventDispatcher.git", + "reference": "v2.3.2" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/EventDispatcher/zipball/v2.3.2", + "reference": "v2.3.2", + "shasum": "" + }, + "require": { + "php": ">=5.3.3" + }, + "require-dev": { + "symfony/dependency-injection": "~2.0" + }, + "suggest": { + "symfony/dependency-injection": "", + "symfony/http-kernel": "" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.3-dev" + } + }, + "autoload": { + "psr-0": { + "Symfony\\Component\\EventDispatcher\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "http://symfony.com/contributors" + } + ], + "description": "Symfony EventDispatcher Component", + "homepage": "http://symfony.com", + "time": "2013-05-13 14:36:40" + }, + { + "name": "symfony/filesystem", + "version": "v2.3.2", + "target-dir": "Symfony/Component/Filesystem", + "source": { + "type": "git", + "url": "https://github.com/symfony/Filesystem.git", + "reference": "v2.3.2" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/Filesystem/zipball/v2.3.2", + "reference": "v2.3.2", + "shasum": "" + }, + "require": { + "php": ">=5.3.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.3-dev" + } + }, + "autoload": { + "psr-0": { + "Symfony\\Component\\Filesystem\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "http://symfony.com/contributors" + } + ], + "description": "Symfony Filesystem Component", + "homepage": "http://symfony.com", + "time": "2013-06-04 15:02:05" + }, + { + "name": "symfony/form", + "version": "v2.3.2", + "target-dir": "Symfony/Component/Form", + "source": { + "type": "git", + "url": "https://github.com/symfony/Form.git", + "reference": "v2.3.2" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/Form/zipball/v2.3.2", + "reference": "v2.3.2", + "shasum": "" + }, + "require": { + "php": ">=5.3.3", + "symfony/event-dispatcher": "~2.1", + "symfony/intl": "~2.3", + "symfony/options-resolver": "~2.1", + "symfony/property-access": "~2.2" + }, + "require-dev": { + "symfony/http-foundation": "~2.2", + "symfony/validator": "~2.2" + }, + "suggest": { + "symfony/http-foundation": "", + "symfony/validator": "" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.3-dev" + } + }, + "autoload": { + "psr-0": { + "Symfony\\Component\\Form\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "http://symfony.com/contributors" + } + ], + "description": "Symfony Form Component", + "homepage": "http://symfony.com", + "time": "2013-07-01 12:24:43" + }, + { + "name": "symfony/icu", + "version": "v1.0.0", + "target-dir": "Symfony/Component/Icu", + "source": { + "type": "git", + "url": "https://github.com/symfony/Icu.git", + "reference": "v1.0.0" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/Icu/zipball/v1.0.0", + "reference": "v1.0.0", + "shasum": "" + }, + "require": { + "php": ">=5.3.3", + "symfony/intl": ">=2.3,<3.0" + }, + "type": "library", + "autoload": { + "psr-0": { + "Symfony\\Component\\Icu\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Symfony Community", + "homepage": "http://symfony.com/contributors" + }, + { + "name": "Bernhard Schussek", + "email": "bschussek@gmail.com" + } + ], + "description": "Contains an excerpt of the ICU data and classes to load it.", + "homepage": "http://symfony.com", + "keywords": [ + "icu", + "intl" + ], + "time": "2013-06-03 18:32:07" + }, + { + "name": "symfony/intl", + "version": "v2.3.2", + "target-dir": "Symfony/Component/Intl", + "source": { + "type": "git", + "url": "https://github.com/symfony/Intl.git", + "reference": "v2.3.2" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/Intl/zipball/v2.3.2", + "reference": "v2.3.2", + "shasum": "" + }, + "require": { + "php": ">=5.3.3", + "symfony/icu": "~1.0-RC" + }, + "require-dev": { + "symfony/filesystem": ">=2.1" + }, + "suggest": { + "ext-intl": "to use the component with locales other than \"en\"" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.3-dev" + } + }, + "autoload": { + "psr-0": { + "Symfony\\Component\\Intl\\": "" + }, + "classmap": [ + "Symfony/Component/Intl/Resources/stubs" + ], + "files": [ + "Symfony/Component/Intl/Resources/stubs/functions.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Symfony Community", + "homepage": "http://symfony.com/contributors" + }, + { + "name": "Igor Wiedler", + "email": "igor@wiedler.ch", + "homepage": "http://wiedler.ch/igor/" + }, + { + "name": "Bernhard Schussek", + "email": "bschussek@gmail.com" + }, + { + "name": "Eriksen Costa", + "email": "eriksen.costa@infranology.com.br" + } + ], + "description": "A PHP replacement layer for the C intl extension that includes additional data from the ICU library.", + "homepage": "http://symfony.com", + "keywords": [ + "i18n", + "icu", + "internationalization", + "intl", + "l10n", + "localization" + ], + "time": "2013-07-08 13:00:35" + }, + { + "name": "symfony/options-resolver", + "version": "v2.3.2", + "target-dir": "Symfony/Component/OptionsResolver", + "source": { + "type": "git", + "url": "https://github.com/symfony/OptionsResolver.git", + "reference": "v2.3.2" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/OptionsResolver/zipball/v2.3.2", + "reference": "v2.3.2", + "shasum": "" + }, + "require": { + "php": ">=5.3.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.3-dev" + } + }, + "autoload": { + "psr-0": { + "Symfony\\Component\\OptionsResolver\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "http://symfony.com/contributors" + } + ], + "description": "Symfony OptionsResolver Component", + "homepage": "http://symfony.com", + "keywords": [ + "config", + "configuration", + "options" + ], + "time": "2013-04-11 06:50:46" + }, + { + "name": "symfony/property-access", + "version": "v2.3.2", + "target-dir": "Symfony/Component/PropertyAccess", + "source": { + "type": "git", + "url": "https://github.com/symfony/PropertyAccess.git", + "reference": "v2.3.2" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/PropertyAccess/zipball/v2.3.2", + "reference": "v2.3.2", + "shasum": "" + }, + "require": { + "php": ">=5.3.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.3-dev" + } + }, + "autoload": { + "psr-0": { + "Symfony\\Component\\PropertyAccess\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "http://symfony.com/contributors" + } + ], + "description": "Symfony PropertyAccess Component", + "homepage": "http://symfony.com", + "keywords": [ + "access", + "array", + "extraction", + "index", + "injection", + "object", + "property", + "property path", + "reflection" + ], + "time": "2013-07-01 12:24:43" + }, + { + "name": "symfony/routing", + "version": "v2.3.2", + "target-dir": "Symfony/Component/Routing", + "source": { + "type": "git", + "url": "https://github.com/symfony/Routing.git", + "reference": "v2.3.2" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/Routing/zipball/v2.3.2", + "reference": "v2.3.2", + "shasum": "" + }, + "require": { + "php": ">=5.3.3" + }, + "require-dev": { + "doctrine/common": "~2.2", + "psr/log": "~1.0", + "symfony/config": "~2.2", + "symfony/yaml": "~2.0" + }, + "suggest": { + "doctrine/common": "", + "symfony/config": "", + "symfony/yaml": "" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.3-dev" + } + }, + "autoload": { + "psr-0": { + "Symfony\\Component\\Routing\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "http://symfony.com/contributors" + } + ], + "description": "Symfony Routing Component", + "homepage": "http://symfony.com", + "time": "2013-06-23 08:16:02" + }, + { + "name": "symfony/translation", + "version": "v2.3.2", + "target-dir": "Symfony/Component/Translation", + "source": { + "type": "git", + "url": "https://github.com/symfony/Translation.git", + "reference": "v2.3.2" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/Translation/zipball/v2.3.2", + "reference": "v2.3.2", + "shasum": "" + }, + "require": { + "php": ">=5.3.3" + }, + "require-dev": { + "symfony/config": "~2.0", + "symfony/yaml": "~2.2" + }, + "suggest": { + "symfony/config": "", + "symfony/yaml": "" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.3-dev" + } + }, + "autoload": { + "psr-0": { + "Symfony\\Component\\Translation\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "http://symfony.com/contributors" + } + ], + "description": "Symfony Translation Component", + "homepage": "http://symfony.com", + "time": "2013-05-13 14:36:40" + }, + { + "name": "symfony/twig-bridge", + "version": "v2.3.2", + "target-dir": "Symfony/Bridge/Twig", + "source": { + "type": "git", + "url": "https://github.com/symfony/TwigBridge.git", + "reference": "v2.3.2" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/TwigBridge/zipball/v2.3.2", + "reference": "v2.3.2", + "shasum": "" + }, + "require": { + "php": ">=5.3.3", + "twig/twig": "~1.11" + }, + "require-dev": { + "symfony/form": "2.2.*", + "symfony/http-kernel": "~2.2", + "symfony/routing": "~2.2", + "symfony/security": "~2.0", + "symfony/templating": "~2.1", + "symfony/translation": "~2.2", + "symfony/yaml": "~2.0" + }, + "suggest": { + "symfony/form": "", + "symfony/http-kernel": "", + "symfony/routing": "", + "symfony/security": "", + "symfony/templating": "", + "symfony/translation": "", + "symfony/yaml": "" + }, + "type": "symfony-bridge", + "extra": { + "branch-alias": { + "dev-master": "2.3-dev" + } + }, + "autoload": { + "psr-0": { + "Symfony\\Bridge\\Twig\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "http://symfony.com/contributors" + } + ], + "description": "Symfony Twig Bridge", + "homepage": "http://symfony.com", + "time": "2013-05-16 10:19:58" + }, + { + "name": "twig/extensions", + "version": "v1.0.0", + "source": { + "type": "git", + "url": "https://github.com/fabpot/Twig-extensions.git", + "reference": "v1.0.0" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/fabpot/Twig-extensions/zipball/v1.0.0", + "reference": "v1.0.0", + "shasum": "" + }, + "require": { + "twig/twig": "1.*" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + } + }, + "autoload": { + "psr-0": { + "Twig_Extensions_": "lib/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + } + ], + "description": "Common additional features for Twig that do not directly belong in core", + "homepage": "https://github.com/fabpot/Twig-extensions", + "keywords": [ + "debug", + "i18n", + "text" + ], + "time": "2013-02-28 14:21:30" + }, + { + "name": "twig/twig", + "version": "v1.13.2", + "source": { + "type": "git", + "url": "https://github.com/fabpot/Twig.git", + "reference": "v1.13.2" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/fabpot/Twig/zipball/v1.13.2", + "reference": "v1.13.2", + "shasum": "" + }, + "require": { + "php": ">=5.2.4" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.13-dev" + } + }, + "autoload": { + "psr-0": { + "Twig_": "lib/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Armin Ronacher", + "email": "armin.ronacher@active-4.com" + } + ], + "description": "Twig, the flexible, fast, and secure template language for PHP", + "homepage": "http://twig.sensiolabs.org", + "keywords": [ + "templating" + ], + "time": "2013-08-03 15:35:31" + }, + { + "name": "umpirsky/twig-gettext-extractor", + "version": "1.1.3", + "source": { + "type": "git", + "url": "https://github.com/umpirsky/Twig-Gettext-Extractor.git", + "reference": "1.1.3" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/umpirsky/Twig-Gettext-Extractor/zipball/1.1.3", + "reference": "1.1.3", + "shasum": "" + }, + "require": { + "php": ">=5.3.3", + "symfony/filesystem": ">=2.0,<3.0", + "symfony/form": ">=2.0,<3.0", + "symfony/routing": ">=2.0,<3.0", + "symfony/translation": ">=2.0,<3.0", + "symfony/twig-bridge": ">=2.0,<3.0", + "twig/extensions": "1.0.*", + "twig/twig": ">=1.2.0,<2.0-dev" + }, + "require-dev": { + "symfony/config": "2.1.*" + }, + "bin": [ + "twig-gettext-extractor" + ], + "type": "application", + "autoload": { + "psr-0": { + "Twig\\Gettext": "." + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Саша Стаменковић", + "email": "umpirsky@gmail.com", + "homepage": "http://umpirsky.com" + } + ], + "description": "The Twig Gettext Extractor is Poedit friendly tool which extracts translations from twig templates.", + "time": "2013-02-14 16:41:48" + } + ], + "packages-dev": [ + + ], + "aliases": [ + + ], + "minimum-stability": "stable", + "stability-flags": [ + + ], + "platform": [ + + ], + "platform-dev": [ + + ] +} diff --git a/css/style-dark.css b/css/style-dark.css deleted file mode 100644 index 813c291d..00000000 --- a/css/style-dark.css +++ /dev/null @@ -1,90 +0,0 @@ -/*** GENERAL ***/ -body { - color: #fff; - background-color: #0d0d0d; -} - -a, a:hover, a:visited { - color: #fff; -} - -#main ul#links li a.current { - background-color: #000; - color: #fff; -} - -#links a:hover, .backhome a:hover{ - background-color: #fff; - color: #000; -} - -input[type=submit].delete { - background : url('../img/dark/remove.png') no-repeat center center; - color : transparent; -} - -#main .entrie { - color: #fff; - background-color: #000; - border: 1px solid #fff; -} - -#main .entrie h2 a:hover { - color: #29B1E3; -} - -a.fav span { - background: url('../img/dark/star-on.png') no-repeat; -} - -a.fav span:hover { - background: url('../img/dark/star-off.png') no-repeat; -} - -a.fav-off span { - background: url('../img/dark/star-off.png') no-repeat; -} - -a.fav-off span:hover { - background: url('../img/dark/star-on.png') no-repeat; -} - -a.archive span { - background: url('../img/dark/checkmark-on.png') no-repeat; -} - -a.archive span:hover { - background: url('../img/dark/checkmark-off.png') no-repeat; -} - -a.archive-off span { - background: url('../img/dark/checkmark-off.png') no-repeat; -} - -a.archive-off span:hover { - background: url('../img/dark/checkmark-on.png') no-repeat; -} - -/*** ***/ -/*** ARTICLE PAGE ***/ - -body.article { - color: #fff; - background-color: #0d0d0d; -} - -#article header { - border-bottom: 1px solid #222222; -} - -#article article { - border-bottom: 1px solid #222222; -} - -.vieworiginal a { - color: #888888; -} - -.entrie { - background-color: #fff; -} diff --git a/css/style-light.css b/css/style-light.css deleted file mode 100644 index cd2384c3..00000000 --- a/css/style-light.css +++ /dev/null @@ -1,100 +0,0 @@ -/*** GENERAL ***/ -body { - color: #222222; - background-color: #F1F1F1; -} - -a, a:hover, a:visited { - color: #000; -} - -.bouton { - background-color: #000; - color: #fff; - border: none; -} -.bouton:hover { - background-color: #222222; - color: #F1F1F1; -} - -#main ul#links li a.current { - background-color: #000; - color: #fff; -} - -#links a:hover, .backhome a:hover{ - background-color: #040707; - color: #F1F1F1; -} - -input[type=submit].delete { - background : url('../img/light/remove.png') no-repeat center center; - color : transparent; -} - -#main .entrie { - color: #2e2e2e; - background-color: #ffffff; - border: 1px solid #000; -} - -#main .entrie h2 a:hover { - color: #F5BE00; -} - -a.fav span { - background: url('../img/light/star-on.png') no-repeat; -} - -a.fav span:hover { - background: url('../img/light/star-off.png') no-repeat; -} - -a.fav-off span { - background: url('../img/light/star-off.png') no-repeat; -} - -a.fav-off span:hover { - background: url('../img/light/star-on.png') no-repeat; -} - -a.archive span { - background: url('../img/light/checkmark-on.png') no-repeat; -} - -a.archive span:hover { - background: url('../img/light/checkmark-off.png') no-repeat; -} - -a.archive-off span { - background: url('../img/light/checkmark-off.png') no-repeat; -} - -a.archive-off span:hover { - background: url('../img/light/checkmark-on.png') no-repeat; -} - -/*** ***/ -/*** ARTICLE PAGE ***/ - -body.article { - color: #222222; - background-color: #F1F1F1; -} - -#article header { - border-bottom: 1px solid #222222; -} - -#article article { - border-bottom: 1px solid #222222; -} - -.vieworiginal a { - color: #888888; -} - -.entrie { - background-color: #fff; -} diff --git a/css/style.css b/css/style.css deleted file mode 100644 index 9fadfa96..00000000 --- a/css/style.css +++ /dev/null @@ -1,215 +0,0 @@ -/*** GENERAL ***/ -body { - font: 20px/1.3em Palatino,Georgia,serif; - margin: 10px; -} - -header { - text-align: center; -} - -.bouton { - border-radius: 2px; -} - -#main ul#links { - padding: 0; - list-style-type: none; - text-align: center; -} - -#main ul#links li { - display: inline; -} - -#main ul#links li a.current { - -webkit-border-radius: 2px; - border-radius: 2px; -} - -#main ul#sort { - padding: 0; - list-style-type: none; - text-align: center; -} - -#main ul#sort li { - display: inline; - font-size: 0.9em; -} - -#main ul#sort img:hover { - cursor: pointer; -} - -#main, #article { - margin: 0 auto; -} - -#links a, .backhome a{ - text-decoration: none; - padding: 5px 10px; -} -#links a:hover, .backhome a:hover{ - -webkit-border-radius: 2px; - border-radius: 2px; -} - -footer { - text-align: right; -} - -/*** ***/ -/*** LINKS DISPLAY ***/ - -#main a.tool { - text-decoration: none; - cursor: pointer; -} - -input[type=submit].delete { - width : 16px; - height :16px; - border : none; - cursor: pointer; - font-size : 0; -} - -#main #content { - margin-top: 20px; -} - -#main .entrie { - padding: 15px; - min-height: 8em; - border: 1px solid; -} - -#main .entrie h2 a { - text-decoration: none; -} - -.tools { - text-align: right; -} - -.tools ul { - padding: 0; margin: 0; - list-style-type: none; -} - -.tools ul li { - line-height: 20px; -} - -.tools a.tool { - cursor: pointer; -} - -#article .tools { - position: relative; - display: inline; - top: 0px; - right: 0px; - width: 100%; - text-align: left; -} - -#article .tools ul li{ - display: inline; -} - -#main .entrie .tools a.tool span, #article .tools a.tool span { - display: inline-block; - width: 16px; - height: 16px; -} - - -/*** ***/ -/*** ARTICLE PAGE ***/ - -body.article { - font: 20px/1.3em Palatino,Georgia,serif; -} - -#article header { - text-align: left; -} - -#article header a { - text-decoration: none; -} - -.vieworiginal a { - text-decoration: none; -} - -.backhome { - display: inline; -} - -/*** ***/ - -#main -{ - max-width: 60em; /* 960 px */ - margin: 0 auto; -} -#content -{ - width: 103.125%; /* 990px */ - overflow: hidden; - margin-left: -1.562%; /* 15px */ - margin-bottom: -1.875em; /* 30px */ -} - -.entrie -{ - width: 30.303%; /* 300px */ - background-color: #fff; - float: left; - margin: 0 1.515% 1.875em; /* 15px 30px */ -} - -@media only screen and ( max-width: 40em ) /* 640px */ -{ - .entrie - { - width: 46.876%; /* 305px */ - margin-bottom: 0.938em; /* 15px */ - } -} - -@media only screen and ( max-width: 20em ) /* 320px */ -{ - #content - { - width: 100%; - margin-left: 0; - } - - .entrie - { - width: 100%; - margin-left: 0; - margin-right: 0; - } -} - -/*** ***/ -/*** MESSAGES ***/ - -.messages { width: 100%; -moz-border-radius: 4px; border-radius: 4px; display: block; padding: 10px 0; margin: 10px auto 10px; clear: both; } -.messages a.closeMessage { margin: -14px -8px 0 0; display:none; width: 16px; height: 16px; float: right; background: url(../img/messages/close.png) no-repeat; } -/*.messages:hover a.closeMessage { visibility:visible; }*/ -.messages p { margin: 3px 0 3px 10px !important; padding: 0 10px 0 23px !important; font-size: 14px; line-height: 16px; } -.messages.error { border: 1px solid #C42608; color: #c00 !important; background: #FFF0EF; } -.messages.error p { background: url(../img/messages/cross.png ) no-repeat 0px 50%; color:#c00 !important; } -.messages.success {background: #E0FBCC; border: 1px solid #6DC70C; } -.messages.success p { background: url(../img/messages/tick.png) no-repeat 0px 50%; color: #2B6301 !important; } -.messages.warning { background: #FFFCD3; border: 1px solid #EBCD41; color: #000; } -.messages.warning p { background: url(../img/messages/warning.png ) no-repeat 0px 50%; color: #5F4E01; } -.messages.information, .messages.info { background: #DFEBFB; border: 1px solid #82AEE7; } -.messages.information p, .messages.info p { background: url(../img/messages/help.png ) no-repeat 0px 50%; color: #064393; } -.messages.information a { text-decoration: underline; } diff --git a/img/dark/checkmark-off.png b/img/dark/checkmark-off.png deleted file mode 100644 index efc3439f..00000000 Binary files a/img/dark/checkmark-off.png and /dev/null differ diff --git a/img/dark/checkmark-on.png b/img/dark/checkmark-on.png deleted file mode 100644 index 24391c2e..00000000 Binary files a/img/dark/checkmark-on.png and /dev/null differ diff --git a/img/dark/down.png b/img/dark/down.png deleted file mode 100644 index 41ea9604..00000000 Binary files a/img/dark/down.png and /dev/null differ diff --git a/img/dark/logo.png b/img/dark/logo.png deleted file mode 100644 index 9fba0642..00000000 Binary files a/img/dark/logo.png and /dev/null differ diff --git a/img/dark/remove.png b/img/dark/remove.png deleted file mode 100644 index 41786fd7..00000000 Binary files a/img/dark/remove.png and /dev/null differ diff --git a/img/dark/star-off.png b/img/dark/star-off.png deleted file mode 100644 index 90651b54..00000000 Binary files a/img/dark/star-off.png and /dev/null differ diff --git a/img/dark/star-on.png b/img/dark/star-on.png deleted file mode 100644 index 7fc14477..00000000 Binary files a/img/dark/star-on.png and /dev/null differ diff --git a/img/dark/up.png b/img/dark/up.png deleted file mode 100644 index 1679e18f..00000000 Binary files a/img/dark/up.png and /dev/null differ diff --git a/img/logo.png b/img/logo.png deleted file mode 100644 index f917857f..00000000 Binary files a/img/logo.png and /dev/null differ diff --git a/import.php b/import.php deleted file mode 100644 index 72e3eac7..00000000 --- a/import.php +++ /dev/null @@ -1,50 +0,0 @@ - - * @copyright 2013 - * @license http://www.wtfpl.net/ see COPYING file - */ - -set_time_limit(0); - -include dirname(__FILE__).'/inc/config.php'; -include dirname(__FILE__).'/inc/simple_html_dom.php'; - -if (!isset($_GET['start'])) { - echo 'Please execute the import script locally, it can take a very long time.
Bye bye Pocket, let\'s go !'; -} -else { - $html = new simple_html_dom(); - $html->load_file('ril_export.html'); - - $read = 0; - $errors = array(); - foreach($html->find('ul') as $ul) - { - foreach($ul->find('li') as $li) - { - $a = $li->find('a'); - $url = $a[0]->href; - - - action_to_do('add', $url); - if ($read == '1') { - $last_id = $db->getHandle()->lastInsertId(); - $sql_update = "UPDATE entries SET is_read=~is_read WHERE id=?"; - $params_update = array($last_id); - $query_update = $db->getHandle()->prepare($sql_update); - $query_update->execute($params_update); - } - } - # Pocket génère un fichier HTML avec deux
    - # Le premier concerne les éléments non lus - # Le second concerne les éléments archivés - $read = 1; - } - - echo 'Import from Pocket completed. Welcome to #poche !'; - logm('import from pocket completed'); -} \ No newline at end of file diff --git a/inc/Encoding.php b/inc/3rdparty/Encoding.php similarity index 100% rename from inc/Encoding.php rename to inc/3rdparty/Encoding.php diff --git a/inc/JSLikeHTMLElement.php b/inc/3rdparty/JSLikeHTMLElement.php similarity index 97% rename from inc/JSLikeHTMLElement.php rename to inc/3rdparty/JSLikeHTMLElement.php index dfcc1be5..238ba8a8 100644 --- a/inc/JSLikeHTMLElement.php +++ b/inc/3rdparty/JSLikeHTMLElement.php @@ -4,7 +4,7 @@ * * This class extends PHP's DOMElement to allow * users to get and set the innerHTML property of -* HTML elements in the same way it's done in +* HTML elements in the same way it's done in * JavaScript. * * Example usage: @@ -15,16 +15,16 @@ * $doc->registerNodeClass('DOMElement', 'JSLikeHTMLElement'); * $doc->loadHTML('

    Para 1

    Para 2

    '); * $elem = $doc->getElementsByTagName('div')->item(0); -* +* * // print innerHTML * echo $elem->innerHTML; // prints '

    Para 1

    Para 2

    ' * echo "\n\n"; -* +* * // set innerHTML * $elem->innerHTML = 'FiveFilters.org'; * echo $elem->innerHTML; // prints 'FiveFilters.org' * echo "\n\n"; -* +* * // print document (with our changes) * echo $doc->saveXML(); * @endcode @@ -59,7 +59,7 @@ class JSLikeHTMLElement extends DOMElement $value = mb_convert_encoding($value, 'HTML-ENTITIES', 'UTF-8'); // Using will generate a warning, but so will bad HTML // (and by this point, bad HTML is what we've got). - // We use it (and suppress the warning) because an HTML fragment will + // We use it (and suppress the warning) because an HTML fragment will // be wrapped around tags which we don't really want to keep. // Note: despite the warning, if loadHTML succeeds it will return true. $result = @$f->loadHTML(''.$value.''); @@ -86,7 +86,7 @@ class JSLikeHTMLElement extends DOMElement * @code * $string = $div->innerHTML; * @endcode - */ + */ public function __get($name) { if ($name == 'innerHTML') { diff --git a/inc/Readability.php b/inc/3rdparty/Readability.php similarity index 96% rename from inc/Readability.php rename to inc/3rdparty/Readability.php index d28d28f9..e1e8738b 100644 --- a/inc/Readability.php +++ b/inc/3rdparty/Readability.php @@ -1,5 +1,5 @@ '/((\s| ?)*){1,}/', 'video' => '!//(player\.|www\.)?(youtube|vimeo|viddler)\.com!i', 'skipFootnoteLink' => '/^\s*(\[?[a-z0-9]{1,2}\]?|^|edit|citation needed)\s*$/i' - ); - + ); + /* constants */ const FLAG_STRIP_UNLIKELYS = 1; const FLAG_WEIGHT_CLASSES = 2; const FLAG_CLEAN_CONDITIONALLY = 4; - + /** * Create instance of Readability * @param string UTF-8 encoded string * @param string (optional) URL associated with HTML (used for footnotes) * @param string which parser to use for turning raw HTML into a DOMDocument (either 'libxml' or 'html5lib') - */ + */ function __construct($html, $url=null, $parser='libxml') { $this->url = $url; @@ -135,18 +135,18 @@ class Readability public function getTitle() { return $this->articleTitle; } - + /** * Get article content element * @return DOMElement */ public function getContent() { return $this->articleContent; - } - + } + /** * Runs readability. - * + * * Workflow: * 1. Prep the document by removing script tags, css, etc. * 2. Build readability's DOM tree. @@ -161,7 +161,7 @@ class Readability if (!isset($this->dom->documentElement)) return false; $this->removeScripts($this->dom); //die($this->getInnerHTML($this->dom->documentElement)); - + // Assume successful outcome $this->success = true; @@ -176,7 +176,7 @@ class Readability } $this->prepDocument(); - + //die($this->dom->documentElement->parentNode->nodeType); //$this->setInnerHTML($this->dom->documentElement, $this->getInnerHTML($this->dom->documentElement)); //die($this->getInnerHTML($this->dom->documentElement)); @@ -191,9 +191,9 @@ class Readability $this->success = false; $articleContent = $this->dom->createElement('div'); $articleContent->setAttribute('id', 'readability-content'); - $articleContent->innerHTML = '

    Sorry, Readability was unable to parse this page for content.

    '; + $articleContent->innerHTML = '

    Sorry, Readability was unable to parse this page for content.

    '; } - + $overlay->setAttribute('id', 'readOverlay'); $innerDiv->setAttribute('id', 'readInner'); @@ -201,7 +201,7 @@ class Readability $innerDiv->appendChild($articleTitle); $innerDiv->appendChild($articleContent); $overlay->appendChild($innerDiv); - + /* Clear the old HTML, insert the new content. */ $this->body->innerHTML = ''; $this->body->appendChild($overlay); @@ -209,21 +209,21 @@ class Readability $this->body->removeAttribute('style'); $this->postProcessContent($articleContent); - + // Set title and content instance variables $this->articleTitle = $articleTitle; $this->articleContent = $articleContent; - + return $this->success; } - + /** * Debug */ protected function dbg($msg) { if ($this->debug) echo '* ',$msg, "\n"; } - + /** * Run any post-process modifications to article content as necessary. * @@ -231,11 +231,11 @@ class Readability * @return void */ public function postProcessContent($articleContent) { - if ($this->convertLinksToFootnotes && !preg_match('/wikipedia\.org/', @$this->url)) { + if ($this->convertLinksToFootnotes && !preg_match('/wikipedia\.org/', @$this->url)) { $this->addFootnotes($articleContent); } } - + /** * Get the article title as an H1. * @@ -248,11 +248,11 @@ class Readability try { $curTitle = $origTitle = $this->getInnerText($this->dom->getElementsByTagName('title')->item(0)); } catch(Exception $e) {} - + if (preg_match('/ [\|\-] /', $curTitle)) { $curTitle = preg_replace('/(.*)[\|\-] .*/i', '$1', $origTitle); - + if (count(explode(' ', $curTitle)) < 3) { $curTitle = preg_replace('/[^\|\-]*[\|\-](.*)/i', '$1', $origTitle); } @@ -279,17 +279,17 @@ class Readability if (count(explode(' ', $curTitle)) <= 4) { $curTitle = $origTitle; } - + $articleTitle = $this->dom->createElement('h1'); $articleTitle->innerHTML = $curTitle; - + return $articleTitle; } - + /** * Prepare the HTML document for readability to scrape it. * This includes things like stripping javascript, CSS, and handling terrible markup. - * + * * @return void **/ protected function prepDocument() { @@ -328,13 +328,13 @@ class Readability $footnotesWrapper = $this->dom->createElement('div'); $footnotesWrapper->setAttribute('id', 'readability-footnotes'); $footnotesWrapper->innerHTML = '

    References

    '; - + $articleFootnotes = $this->dom->createElement('ol'); $articleFootnotes->setAttribute('id', 'readability-footnotes-list'); $footnotesWrapper->appendChild($articleFootnotes); - + $articleLinks = $articleContent->getElementsByTagName('a'); - + $linkCount = 0; for ($i = 0; $i < $articleLinks->length; $i++) { @@ -346,11 +346,11 @@ class Readability if (!$linkDomain && isset($this->url)) $linkDomain = @parse_url($this->url, PHP_URL_HOST); //linkDomain = footnoteLink.host ? footnoteLink.host : document.location.host, $linkText = $this->getInnerText($articleLink); - + if ((strpos($articleLink->getAttribute('class'), 'readability-DoNotFootnote') !== false) || preg_match($this->regexps['skipFootnoteLink'], $linkText)) { continue; } - + $linkCount++; /** Add a superscript reference after the article link */ @@ -358,7 +358,7 @@ class Readability $refLink->innerHTML = '[' . $linkCount . ']'; $refLink->setAttribute('class', 'readability-DoNotFootnote'); $refLink->setAttribute('style', 'color: inherit;'); - + //TODO: does this work or should we use DOMNode.isSameNode()? if ($articleLink->parentNode->lastChild == $articleLink) { $articleLink->parentNode->appendChild($refLink); @@ -373,15 +373,15 @@ class Readability $footnoteLink->innerHTML = ($footnoteLink->getAttribute('title') != '' ? $footnoteLink->getAttribute('title') : $linkText); $footnoteLink->setAttribute('name', 'readabilityFootnoteLink-' . $linkCount); - + $footnote->appendChild($footnoteLink); if ($linkDomain) $footnote->innerHTML = $footnote->innerHTML . ' (' . $linkDomain . ')'; - + $articleFootnotes->appendChild($footnote); } if ($linkCount > 0) { - $articleContent->appendChild($footnotesWrapper); + $articleContent->appendChild($footnotesWrapper); } } @@ -404,7 +404,7 @@ class Readability //} } } - + /** * Prepare the article node for display. Clean out any inline styles, * iframes, forms, strip extraneous

    tags, etc. @@ -429,7 +429,7 @@ class Readability * as a header and not a subheader, so remove it since we already have a header. ***/ if (!$this->lightClean && ($articleContent->getElementsByTagName('h2')->length == 1)) { - $this->clean($articleContent, 'h2'); + $this->clean($articleContent, 'h2'); } $this->clean($articleContent, 'iframe'); @@ -448,7 +448,7 @@ class Readability $embedCount = $articleParagraphs->item($i)->getElementsByTagName('embed')->length; $objectCount = $articleParagraphs->item($i)->getElementsByTagName('object')->length; $iframeCount = $articleParagraphs->item($i)->getElementsByTagName('iframe')->length; - + if ($imgCount === 0 && $embedCount === 0 && $objectCount === 0 && $iframeCount === 0 && $this->getInnerText($articleParagraphs->item($i), false) == '') { $articleParagraphs->item($i)->parentNode->removeChild($articleParagraphs->item($i)); @@ -457,13 +457,13 @@ class Readability try { $articleContent->innerHTML = preg_replace('/]*>\s*

    innerHTML); - //articleContent.innerHTML = articleContent.innerHTML.replace(/]*>\s*

    ]*>\s*

    dbg("Cleaning innerHTML of breaks failed. This is an IE strict-block-elements bug. Ignoring.: " . $e); } } - + /** * Initialize a node with the readability object. Also checks the * className/id for special names to add to its score. @@ -474,7 +474,7 @@ class Readability protected function initializeNode($node) { $readability = $this->dom->createAttribute('readability'); $readability->value = 0; // this is our contentScore - $node->setAttributeNode($readability); + $node->setAttributeNode($readability); switch (strtoupper($node->tagName)) { // unsure if strtoupper is needed, but using it just in case case 'DIV': @@ -486,7 +486,7 @@ class Readability case 'BLOCKQUOTE': $readability->value += 3; break; - + case 'ADDRESS': case 'OL': case 'UL': @@ -510,7 +510,7 @@ class Readability } $readability->value += $this->getClassWeight($node); } - + /*** * grabArticle - Using a variety of metrics (content score, classname, element types), find the content that is * most likely to be the stuff a user wants to read. Then return it wrapped up in a div. @@ -548,7 +548,7 @@ class Readability $node->parentNode->removeChild($node); $nodeIndex--; continue; - } + } } if ($tagName == 'P' || $tagName == 'TD' || $tagName == 'PRE') { @@ -589,7 +589,7 @@ class Readability } } } - + /** * Loop through all paragraphs, and assign a score to them based on how content-y they look. * Then add their score to their parent node. @@ -613,7 +613,7 @@ class Readability } /* Initialize readability data for the parent. */ - if (!$parentNode->hasAttribute('readability')) + if (!$parentNode->hasAttribute('readability')) { $this->initializeNode($parentNode); $candidates[] = $parentNode; @@ -633,15 +633,15 @@ class Readability /* Add points for any commas within this paragraph */ $contentScore += count(explode(',', $innerText)); - + /* For every 100 characters in this paragraph, add another point. Up to 3 points. */ $contentScore += min(floor(strlen($innerText) / 100), 3); - + /* Add the score to the parent. The grandparent gets half. */ $parentNode->getAttributeNode('readability')->value += $contentScore; if ($grandParentNode) { - $grandParentNode->getAttributeNode('readability')->value += $contentScore/2; + $grandParentNode->getAttributeNode('readability')->value += $contentScore/2; } } @@ -727,12 +727,12 @@ class Readability { $append = true; } - + if (strtoupper($siblingNode->nodeName) == 'P') { $linkDensity = $this->getLinkDensity($siblingNode); $nodeContent = $this->getInnerText($siblingNode); $nodeLength = strlen($nodeContent); - + if ($nodeLength > 80 && $linkDensity < 0.25) { $append = true; @@ -751,7 +751,7 @@ class Readability $sibNodeName = strtoupper($siblingNode->nodeName); if ($sibNodeName != 'DIV' && $sibNodeName != 'P') { /* We have a node that isn't a common block level element, like a form or td tag. Turn it into a div so it doesn't get filtered out later by accident. */ - + $this->dbg('Altering siblingNode of ' . $sibNodeName . ' to div.'); $nodeToAppend = $this->dom->createElement('div'); try { @@ -770,7 +770,7 @@ class Readability $s--; $sl--; } - + /* To ensure a node does not interfere with readability styles, remove its classnames */ $nodeToAppend->removeAttribute('class'); @@ -796,14 +796,14 @@ class Readability // in the meantime, we check and create an empty element if it's not there. if (!isset($this->body->childNodes)) $this->body = $this->dom->createElement('body'); $this->body->innerHTML = $this->bodyCache; - + if ($this->flagIsActive(self::FLAG_STRIP_UNLIKELYS)) { $this->removeFlag(self::FLAG_STRIP_UNLIKELYS); return $this->grabArticle($this->body); } else if ($this->flagIsActive(self::FLAG_WEIGHT_CLASSES)) { $this->removeFlag(self::FLAG_WEIGHT_CLASSES); - return $this->grabArticle($this->body); + return $this->grabArticle($this->body); } else if ($this->flagIsActive(self::FLAG_CLEAN_CONDITIONALLY)) { $this->removeFlag(self::FLAG_CLEAN_CONDITIONALLY); @@ -815,7 +815,7 @@ class Readability } return $articleContent; } - + /** * Remove script tags from document * @@ -829,7 +829,7 @@ class Readability $scripts->item($i)->parentNode->removeChild($scripts->item($i)); } } - + /** * Get the inner text of a node. * This also strips out any excess whitespace to be found. @@ -878,11 +878,11 @@ class Readability $elem->removeAttribute('style'); } } - + /** * Get the density of links as a percentage of the content * This is the amount of text that is inside a link divided by the total text in the node. - * + * * @param DOMElement $e * @return number (float) */ @@ -900,9 +900,9 @@ class Readability return 0; } } - + /** - * Get an elements class/id weight. Uses regular expressions to tell if this + * Get an elements class/id weight. Uses regular expressions to tell if this * element looks good or bad. * * @param DOMElement $e @@ -964,7 +964,7 @@ class Readability public function clean($e, $tag) { $targetList = $e->getElementsByTagName($tag); $isEmbed = ($tag == 'iframe' || $tag == 'object' || $tag == 'embed'); - + for ($y=$targetList->length-1; $y >= 0; $y--) { /* Allow youtube and vimeo videos through as people usually want to see those. */ if ($isEmbed) { @@ -972,7 +972,7 @@ class Readability for ($i=0, $il=$targetList->item($y)->attributes->length; $i < $il; $i++) { $attributeValues .= $targetList->item($y)->attributes->item($i)->value . '|'; // DOMAttr? (TODO: test) } - + /* First, check the elements attributes to see if any of them contain youtube or vimeo */ if (preg_match($this->regexps['video'], $attributeValues)) { continue; @@ -986,10 +986,10 @@ class Readability $targetList->item($y)->parentNode->removeChild($targetList->item($y)); } } - + /** * Clean an element of all tags of type "tag" if they look fishy. - * "Fishy" is an algorithm based on content length, classnames, + * "Fishy" is an algorithm based on content length, classnames, * link density, number of images & embeds, etc. * * @param DOMElement $e @@ -1013,7 +1013,7 @@ class Readability for ($i=$curTagsLength-1; $i >= 0; $i--) { $weight = $this->getClassWeight($tagsList->item($i)); $contentScore = ($tagsList->item($i)->hasAttribute('readability')) ? (int)$tagsList->item($i)->getAttribute('readability') : 0; - + $this->dbg('Cleaning Conditionally ' . $tagsList->item($i)->tagName . ' (' . $tagsList->item($i)->getAttribute('class') . ':' . $tagsList->item($i)->getAttribute('id') . ')' . (($tagsList->item($i)->hasAttribute('readability')) ? (' with score ' . $tagsList->item($i)->getAttribute('readability')) : '')); if ($weight + $contentScore < 0) { @@ -1034,13 +1034,13 @@ class Readability $embeds = $tagsList->item($i)->getElementsByTagName('embed'); for ($ei=0, $il=$embeds->length; $ei < $il; $ei++) { if (preg_match($this->regexps['video'], $embeds->item($ei)->getAttribute('src'))) { - $embedCount++; + $embedCount++; } } $embeds = $tagsList->item($i)->getElementsByTagName('iframe'); for ($ei=0, $il=$embeds->length; $ei < $il; $ei++) { if (preg_match($this->regexps['video'], $embeds->item($ei)->getAttribute('src'))) { - $embedCount++; + $embedCount++; } } @@ -1058,7 +1058,7 @@ class Readability $toRemove = true; } else if ( $input > floor($p/3) ) { $this->dbg(' too many elements'); - $toRemove = true; + $toRemove = true; } else if ($contentLength < 25 && ($embedCount === 0 && ($img === 0 || $img > 2))) { $this->dbg(' content length less than 25 chars, 0 embeds and either 0 images or more than 2 images'); $toRemove = true; @@ -1082,7 +1082,7 @@ class Readability $toRemove = true; } else if ( $input > floor($p/3) ) { $this->dbg(' too many elements'); - $toRemove = true; + $toRemove = true; } else if ($contentLength < 25 && ($img === 0 || $img > 2) ) { $this->dbg(' content length less than 25 chars and 0 images, or more than 2 images'); $toRemove = true; @@ -1126,11 +1126,11 @@ class Readability public function flagIsActive($flag) { return ($this->flags & $flag) > 0; } - + public function addFlag($flag) { $this->flags = $this->flags | $flag; } - + public function removeFlag($flag) { $this->flags = $this->flags & ~$flag; } diff --git a/inc/Session.class.php b/inc/3rdparty/Session.class.php similarity index 99% rename from inc/Session.class.php rename to inc/3rdparty/Session.class.php index eff924cc..3162f507 100644 --- a/inc/Session.class.php +++ b/inc/3rdparty/Session.class.php @@ -93,7 +93,7 @@ class Session // Force logout public static function logout() { - unset($_SESSION['uid'],$_SESSION['info'],$_SESSION['expires_on'],$_SESSION['tokens'], $_SESSION['login'], $_SESSION['pass']); + unset($_SESSION['uid'],$_SESSION['info'],$_SESSION['expires_on'],$_SESSION['tokens'], $_SESSION['login'], $_SESSION['pass'], $_SESSION['poche_user']); } // Make sure user is logged in. diff --git a/inc/class.messages.php b/inc/3rdparty/class.messages.php old mode 100644 new mode 100755 similarity index 96% rename from inc/class.messages.php rename to inc/3rdparty/class.messages.php index 6d515bf6..e60bd3a1 --- a/inc/class.messages.php +++ b/inc/3rdparty/class.messages.php @@ -1,231 +1,231 @@ -\n%s\n"; - var $msgBefore = '

    '; - var $msgAfter = "

    \n"; - - - /** - * Constructor - * @author Mike Everhart - */ - public function __construct() { - - // Generate a unique ID for this user and session - $this->msgId = md5(uniqid()); - - // Create the session array if it doesnt already exist - if( !array_key_exists('flash_messages', $_SESSION) ) $_SESSION['flash_messages'] = array(); - - } - - /** - * Add a message to the queue - * - * @author Mike Everhart - * - * @param string $type The type of message to add - * @param string $message The message - * @param string $redirect_to (optional) If set, the user will be redirected to this URL - * @return bool - * - */ - public function add($type, $message, $redirect_to=null) { - - if( !isset($_SESSION['flash_messages']) ) return false; - - if( !isset($type) || !isset($message[0]) ) return false; - - // Replace any shorthand codes with their full version - if( strlen(trim($type)) == 1 ) { - $type = str_replace( array('h', 'i', 'w', 'e', 's'), array('help', 'info', 'warning', 'error', 'success'), $type ); - - // Backwards compatibility... - } elseif( $type == 'information' ) { - $type = 'info'; - } - - // Make sure it's a valid message type - if( !in_array($type, $this->msgTypes) ) die('"' . strip_tags($type) . '" is not a valid message type!' ); - - // If the session array doesn't exist, create it - if( !array_key_exists( $type, $_SESSION['flash_messages'] ) ) $_SESSION['flash_messages'][$type] = array(); - - $_SESSION['flash_messages'][$type][] = $message; - - if( !is_null($redirect_to) ) { - header("Location: $redirect_to"); - exit(); - } - - return true; - - } - - //----------------------------------------------------------------------------------------------- - // display() - // print queued messages to the screen - //----------------------------------------------------------------------------------------------- - /** - * Display the queued messages - * - * @author Mike Everhart - * - * @param string $type Which messages to display - * @param bool $print True = print the messages on the screen - * @return mixed - * - */ - public function display($type='all', $print=true) { - $messages = ''; - $data = ''; - - if( !isset($_SESSION['flash_messages']) ) return false; - - if( $type == 'g' || $type == 'growl' ) { - $this->displayGrowlMessages(); - return true; - } - - // Print a certain type of message? - if( in_array($type, $this->msgTypes) ) { - foreach( $_SESSION['flash_messages'][$type] as $msg ) { - $messages .= $this->msgBefore . $msg . $this->msgAfter; - } - - $data .= sprintf($this->msgWrapper, $this->msgClass, $type, $messages); - - // Clear the viewed messages - $this->clear($type); - - // Print ALL queued messages - } elseif( $type == 'all' ) { - foreach( $_SESSION['flash_messages'] as $type => $msgArray ) { - $messages = ''; - foreach( $msgArray as $msg ) { - $messages .= $this->msgBefore . $msg . $this->msgAfter; - } - $data .= sprintf($this->msgWrapper, $this->msgClass, $type, $messages); - } - - // Clear ALL of the messages - $this->clear(); - - // Invalid Message Type? - } else { - return false; - } - - // Print everything to the screen or return the data - if( $print ) { - echo $data; - } else { - return $data; - } - } - - - /** - * Check to see if there are any queued error messages - * - * @author Mike Everhart - * - * @return bool true = There ARE error messages - * false = There are NOT any error messages - * - */ - public function hasErrors() { - return empty($_SESSION['flash_messages']['error']) ? false : true; - } - - /** - * Check to see if there are any ($type) messages queued - * - * @author Mike Everhart - * - * @param string $type The type of messages to check for - * @return bool - * - */ - public function hasMessages($type=null) { - if( !is_null($type) ) { - if( !empty($_SESSION['flash_messages'][$type]) ) return $_SESSION['flash_messages'][$type]; - } else { - foreach( $this->msgTypes as $type ) { - if( !empty($_SESSION['flash_messages']) ) return true; - } - } - return false; - } - - /** - * Clear messages from the session data - * - * @author Mike Everhart - * - * @param string $type The type of messages to clear - * @return bool - * - */ - public function clear($type='all') { - if( $type == 'all' ) { - unset($_SESSION['flash_messages']); - } else { - unset($_SESSION['flash_messages'][$type]); - } - return true; - } - - public function __toString() { return $this->hasMessages(); } - - public function __destruct() { - //$this->clear(); - } - - -} // end class +X\n%s\n"; + var $msgBefore = '

    '; + var $msgAfter = "

    \n"; + + + /** + * Constructor + * @author Mike Everhart + */ + public function __construct() { + + // Generate a unique ID for this user and session + $this->msgId = md5(uniqid()); + + // Create the session array if it doesnt already exist + if( !array_key_exists('flash_messages', $_SESSION) ) $_SESSION['flash_messages'] = array(); + + } + + /** + * Add a message to the queue + * + * @author Mike Everhart + * + * @param string $type The type of message to add + * @param string $message The message + * @param string $redirect_to (optional) If set, the user will be redirected to this URL + * @return bool + * + */ + public function add($type, $message, $redirect_to=null) { + + if( !isset($_SESSION['flash_messages']) ) return false; + + if( !isset($type) || !isset($message[0]) ) return false; + + // Replace any shorthand codes with their full version + if( strlen(trim($type)) == 1 ) { + $type = str_replace( array('h', 'i', 'w', 'e', 's'), array('help', 'info', 'warning', 'error', 'success'), $type ); + + // Backwards compatibility... + } elseif( $type == 'information' ) { + $type = 'info'; + } + + // Make sure it's a valid message type + if( !in_array($type, $this->msgTypes) ) die('"' . strip_tags($type) . '" is not a valid message type!' ); + + // If the session array doesn't exist, create it + if( !array_key_exists( $type, $_SESSION['flash_messages'] ) ) $_SESSION['flash_messages'][$type] = array(); + + $_SESSION['flash_messages'][$type][] = $message; + + if( !is_null($redirect_to) ) { + header("Location: $redirect_to"); + exit(); + } + + return true; + + } + + //----------------------------------------------------------------------------------------------- + // display() + // print queued messages to the screen + //----------------------------------------------------------------------------------------------- + /** + * Display the queued messages + * + * @author Mike Everhart + * + * @param string $type Which messages to display + * @param bool $print True = print the messages on the screen + * @return mixed + * + */ + public function display($type='all', $print=true) { + $messages = ''; + $data = ''; + + if( !isset($_SESSION['flash_messages']) ) return false; + + if( $type == 'g' || $type == 'growl' ) { + $this->displayGrowlMessages(); + return true; + } + + // Print a certain type of message? + if( in_array($type, $this->msgTypes) ) { + foreach( $_SESSION['flash_messages'][$type] as $msg ) { + $messages .= $this->msgBefore . $msg . $this->msgAfter; + } + + $data .= sprintf($this->msgWrapper, $this->msgClass, $type, $messages); + + // Clear the viewed messages + $this->clear($type); + + // Print ALL queued messages + } elseif( $type == 'all' ) { + foreach( $_SESSION['flash_messages'] as $type => $msgArray ) { + $messages = ''; + foreach( $msgArray as $msg ) { + $messages .= $this->msgBefore . $msg . $this->msgAfter; + } + $data .= sprintf($this->msgWrapper, $this->msgClass, $type, $messages); + } + + // Clear ALL of the messages + $this->clear(); + + // Invalid Message Type? + } else { + return false; + } + + // Print everything to the screen or return the data + if( $print ) { + echo $data; + } else { + return $data; + } + } + + + /** + * Check to see if there are any queued error messages + * + * @author Mike Everhart + * + * @return bool true = There ARE error messages + * false = There are NOT any error messages + * + */ + public function hasErrors() { + return empty($_SESSION['flash_messages']['error']) ? false : true; + } + + /** + * Check to see if there are any ($type) messages queued + * + * @author Mike Everhart + * + * @param string $type The type of messages to check for + * @return bool + * + */ + public function hasMessages($type=null) { + if( !is_null($type) ) { + if( !empty($_SESSION['flash_messages'][$type]) ) return $_SESSION['flash_messages'][$type]; + } else { + foreach( $this->msgTypes as $type ) { + if( !empty($_SESSION['flash_messages']) ) return true; + } + } + return false; + } + + /** + * Clear messages from the session data + * + * @author Mike Everhart + * + * @param string $type The type of messages to clear + * @return bool + * + */ + public function clear($type='all') { + if( $type == 'all' ) { + unset($_SESSION['flash_messages']); + } else { + unset($_SESSION['flash_messages'][$type]); + } + return true; + } + + public function __toString() { return $this->hasMessages(); } + + public function __destruct() { + //$this->clear(); + } + + +} // end class ?> \ No newline at end of file diff --git a/inc/3rdparty/paginator.php b/inc/3rdparty/paginator.php new file mode 100644 index 00000000..306756c0 --- /dev/null +++ b/inc/3rdparty/paginator.php @@ -0,0 +1,202 @@ +_instance = $instance; + $this->_perPage = $perPage; + $this->set_instance(); + } + + /** + * get_start + * + * creates the starting point for limiting the dataset + * @return numeric + */ + private function get_start(){ + return ($this->_page * $this->_perPage) - $this->_perPage; + } + + /** + * set_instance + * + * sets the instance parameter, if numeric value is 0 then set to 1 + * + * @var numeric + */ + private function set_instance(){ + $this->_page = (int) (!isset($_GET[$this->_instance]) ? 1 : $_GET[$this->_instance]); + $this->_page = ($this->_page == 0 ? 1 : $this->_page); + } + + /** + * set_total + * + * collect a numberic value and assigns it to the totalRows + * + * @var numeric + */ + public function set_total($_totalRows){ + $this->_totalRows = $_totalRows; + } + + /** + * get_limit + * + * returns the limit for the data source, calling the get_start method and passing in the number of items perp page + * + * @return string + */ + public function get_limit(){ + if (STORAGE == 'postgres') { + return "LIMIT ".$this->_perPage." OFFSET ".$this->get_start(); + } else { + return "LIMIT ".$this->get_start().",".$this->_perPage; + } + } + + /** + * page_links + * + * create the html links for navigating through the dataset + * + * @var sting $path optionally set the path for the link + * @var sting $ext optionally pass in extra parameters to the GET + * @return string returns the html menu + */ + public function page_links($path='?',$ext=null) + { + $adjacents = "2"; + $prev = $this->_page - 1; + $next = $this->_page + 1; + $lastpage = ceil($this->_totalRows/$this->_perPage); + $lpm1 = $lastpage - 1; + + $pagination = ""; + if($lastpage > 1) + { + $pagination .= "\n"; + } + + + return $pagination; + } +} diff --git a/inc/simple_html_dom.php b/inc/3rdparty/simple_html_dom.php similarity index 100% rename from inc/simple_html_dom.php rename to inc/3rdparty/simple_html_dom.php diff --git a/inc/MyTool.class.php b/inc/MyTool.class.php deleted file mode 100644 index 1f5051a4..00000000 --- a/inc/MyTool.class.php +++ /dev/null @@ -1,265 +0,0 @@ - - * @copyright 2013 - * @license http://www.wtfpl.net/ see COPYING file - */ - -class MyTool -{ - public static function initPhp() - { - define('START_TIME', microtime(true)); - - if (phpversion() < 5) { - die("Argh you don't have PHP 5 !"); - } - - error_reporting(E_ALL); - - function stripslashesDeep($value) { - return is_array($value) - ? array_map('stripslashesDeep', $value) - : stripslashes($value); - } - - if (get_magic_quotes_gpc()) { - $_POST = array_map('stripslashesDeep', $_POST); - $_GET = array_map('stripslashesDeep', $_GET); - $_COOKIE = array_map('stripslashesDeep', $_COOKIE); - } - - ob_start(); - register_shutdown_function('ob_end_flush'); - } - - public static function isUrl($url) - { - // http://neo22s.com/check-if-url-exists-and-is-online-php/ - $pattern='|^http(s)?://[a-z0-9-]+(.[a-z0-9-]+)*(:[0-9]+)?(/.*)?$|i'; - - return preg_match($pattern, $url); - } - - public static function isEmail($email) - { - $pattern = "/^[A-Z0-9._%-]+@[A-Z0-9.-]+\.[A-Z]{2, 4}$/i"; - - return (preg_match($pattern, $email)); - } - - public static function formatBBCode($text) - { - $replace = array( - '/\[m\](.+?)\[\/m\]/is' - => '/* moderate */', - '/\[b\](.+?)\[\/b\]/is' - => '$1', - '/\[i\](.+?)\[\/i\]/is' - => '$1', - '/\[s\](.+?)\[\/s\]/is' - => '$1', - '/\[u\](.+?)\[\/u\]/is' - => '$1', - '/\[url\](.+?)\[\/url]/is' - => '$1', - '/\[url=(\w+:\/\/[^\]]+)\](.+?)\[\/url]/is' - => '$2', - '/\[quote\](.+?)\[\/quote\]/is' - => '
    $1
    ', - '/\[code\](.+?)\[\/code\]/is' - => '$1', - '/\[([^[]+)\|([^[]+)\]/is' - => '$1' - ); - $text = preg_replace( - array_keys($replace), - array_values($replace), - $text - ); - - return $text; - } - - public static function formatText($text) - { - $text = preg_replace_callback( - '/(.*?)<\/code_html>/is', - create_function( - '$matches', - 'return htmlspecialchars($matches[1]);' - ), - $text - ); - $text = preg_replace_callback( - '/(.*?)<\/code_php>/is', - create_function( - '$matches', - 'return highlight_string("", true);' - ), - $text - ); - $text = preg_replace('/
    /is', '', $text); - - $text = preg_replace( - '#(^|\s)([a-z]+://([^\s\w/]?[\w/])*)(\s|$)#im', - '\\1\\2\\4', - $text - ); - $text = preg_replace( - '#(^|\s)wp:?([a-z]{2}|):([\w]+)#im', - '\\1\\3', - $text - ); - $text = str_replace( - 'http://.wikipedia.org/wiki/', - 'http://www.wikipedia.org/wiki/', - $text - ); - $text = str_replace('\wp:', 'wp:', $text); - $text = str_replace('\http:', 'http:', $text); - $text = MyTool::formatBBCode($text); - $text = nl2br($text); - - return $text; - } - - public static function getUrl() - { - $https = (!empty($_SERVER['HTTPS']) - && (strtolower($_SERVER['HTTPS']) == 'on')) - || (isset($_SERVER["SERVER_PORT"]) - && $_SERVER["SERVER_PORT"] == '443'); // HTTPS detection. - $serverport = (!isset($_SERVER["SERVER_PORT"]) - || $_SERVER["SERVER_PORT"] == '80' - || ($https && $_SERVER["SERVER_PORT"] == '443') - ? '' - : ':' . $_SERVER["SERVER_PORT"]); - - $scriptname = str_replace('/index.php', '/', $_SERVER["SCRIPT_NAME"]); - - if (!isset($_SERVER["SERVER_NAME"])) { - return $scriptname; - } - - return 'http' . ($https ? 's' : '') . '://' - . $_SERVER["SERVER_NAME"] . $serverport . $scriptname; - } - - public static function rrmdir($dir) - { - if (is_dir($dir) && ($d = @opendir($dir))) { - while (($file = @readdir($d)) !== false) { - if ( $file == '.' || $file == '..' ) { - continue; - } else { - unlink($dir . '/' . $file); - } - } - } - } - - public static function humanBytes($bytes) - { - $siPrefix = array( 'bytes', 'KB', 'MB', 'GB', 'TB', 'EB', 'ZB', 'YB' ); - $base = 1024; - $class = min((int) log($bytes, $base), count($siPrefix) - 1); - $val = sprintf('%1.2f', $bytes / pow($base, $class)); - - return $val . ' ' . $siPrefix[$class]; - } - - public static function returnBytes($val) - { - $val = trim($val); - $last = strtolower($val[strlen($val)-1]); - switch($last) - { - case 'g': $val *= 1024; - case 'm': $val *= 1024; - case 'k': $val *= 1024; - } - - return $val; - } - - public static function getMaxFileSize() - { - $sizePostMax = MyTool::returnBytes(ini_get('post_max_size')); - $sizeUploadMax = MyTool::returnBytes(ini_get('upload_max_filesize')); - - // Return the smaller of two: - return min($sizePostMax, $sizeUploadMax); - } - - public static function smallHash($text) - { - $t = rtrim(base64_encode(hash('crc32', $text, true)), '='); - // Get rid of characters which need encoding in URLs. - $t = str_replace('+', '-', $t); - $t = str_replace('/', '_', $t); - $t = str_replace('=', '@', $t); - - return $t; - } - - public static function renderJson($data) - { - header('Cache-Control: no-cache, must-revalidate'); - header('Expires: Sat, 26 Jul 1997 05:00:00 GMT'); - header('Content-type: application/json; charset=UTF-8'); - - echo json_encode($data); - exit(); - } - - public static function grabToLocal($url, $file, $force = false) - { - if ((!file_exists($file) || $force) && in_array('curl', get_loaded_extensions())){ - $ch = curl_init ($url); - curl_setopt($ch, CURLOPT_HEADER, false); - curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); - curl_setopt($ch, CURLOPT_BINARYTRANSFER, true); - $raw = curl_exec($ch); - if (curl_getinfo($ch, CURLINFO_HTTP_CODE) == 200) { - $fp = fopen($file, 'x'); - fwrite($fp, $raw); - fclose($fp); - } - curl_close ($ch); - } - } - - public static function redirect($rurl = '') - { - if ($rurl === '') { - // if (!empty($_SERVER['HTTP_REFERER']) && strcmp(parse_url($_SERVER['HTTP_REFERER'],PHP_URL_HOST),$_SERVER['SERVER_NAME'])==0) - $rurl = (empty($_SERVER['HTTP_REFERER'])?'?':$_SERVER['HTTP_REFERER']); - if (isset($_POST['returnurl'])) { - $rurl = $_POST['returnurl']; - } - } - - // prevent loop - if (empty($rurl) || parse_url($rurl, PHP_URL_QUERY) === $_SERVER['QUERY_STRING']) { - $rurl = MyTool::getUrl(); - } - - if (substr($rurl, 0, 1) !== '?') { - $ref = MyTool::getUrl(); - if (substr($rurl, 0, strlen($ref)) !== $ref) { - $rurl = $ref; - } - } - header('Location: '.$rurl); - exit(); - } - - public static function silence_errors($num, $str) - { -// No-op - } -} \ No newline at end of file diff --git a/inc/config.php b/inc/config.php deleted file mode 100644 index bd9287fe..00000000 --- a/inc/config.php +++ /dev/null @@ -1,66 +0,0 @@ - - * @copyright 2013 - * @license http://www.wtfpl.net/ see COPYING file - */ - -define ('POCHE_VERSION', '0.2.1'); - -if (!is_dir('db/')) { - @mkdir('db/',0705); -} - -define ('MODE_DEMO', FALSE); -define ('ABS_PATH', 'assets/'); -define ('CONVERT_LINKS_FOOTNOTES', TRUE); -define ('REVERT_FORCED_PARAGRAPH_ELEMENTS',FALSE); -define ('DOWNLOAD_PICTURES', TRUE); -define ('SALT', '464v54gLLw928uz4zUBqkRJeiPY68zCX'); -$storage_type = 'sqlite'; # sqlite or file - -include 'functions.php'; -require_once 'Readability.php'; -require_once 'Encoding.php'; -require_once 'rain.tpl.class.php'; -require_once 'MyTool.class.php'; -require_once 'Session.class.php'; -require_once 'store/store.class.php'; -require_once 'store/sqlite.class.php'; -require_once 'store/file.class.php'; -require_once 'class.messages.php'; - -Session::init(); - -$store = new $storage_type(); -# initialisation de RainTPL -raintpl::$tpl_dir = './tpl/'; -raintpl::$cache_dir = './cache/'; -raintpl::$base_url = get_poche_url(); -raintpl::configure('path_replace', false); -raintpl::configure('debug', false); -$tpl = new raintpl(); - -if(!$store->isInstalled()) -{ - logm('poche still not installed'); - $tpl->draw('install'); - if (isset($_GET['install'])) { - if (($_POST['password'] == $_POST['password_repeat']) - && $_POST['password'] != "" && $_POST['login'] != "") { - $store->install($_POST['login'], encode_string($_POST['password'] . $_POST['login'])); - Session::logout(); - MyTool::redirect(); - } - } - exit(); -} - -$_SESSION['login'] = (isset ($_SESSION['login'])) ? $_SESSION['login'] : $store->getLogin(); -$_SESSION['pass'] = (isset ($_SESSION['pass'])) ? $_SESSION['pass'] : $store->getPassword(); - -$msg = new Messages(); -$tpl->assign('msg', $msg); \ No newline at end of file diff --git a/inc/functions.php b/inc/functions.php deleted file mode 100644 index 73e591c5..00000000 --- a/inc/functions.php +++ /dev/null @@ -1,398 +0,0 @@ - - * @copyright 2013 - * @license http://www.wtfpl.net/ see COPYING file - */ - -/** - * Permet de générer l'URL de poche pour le bookmarklet - */ -function get_poche_url() -{ - $protocol = "http"; - if(isset($_SERVER['HTTPS'])) { - if($_SERVER['HTTPS'] != "off" && $_SERVER['HTTPS'] != "") { - $protocol = "https"; - } - } - - return $protocol . "://" . $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI']; -} - -function encode_string($string) -{ - return sha1($string . SALT); -} - -// function define to retrieve url content -function get_external_file($url) -{ - $timeout = 15; - // spoofing FireFox 18.0 - $useragent="Mozilla/5.0 (Windows NT 5.1; rv:18.0) Gecko/20100101 Firefox/18.0"; - - if (in_array ('curl', get_loaded_extensions())) { - // Fetch feed from URL - $curl = curl_init(); - curl_setopt($curl, CURLOPT_URL, $url); - curl_setopt($curl, CURLOPT_TIMEOUT, $timeout); - curl_setopt($curl, CURLOPT_FOLLOWLOCATION, true); - curl_setopt($curl, CURLOPT_RETURNTRANSFER, true); - curl_setopt($curl, CURLOPT_HEADER, false); - - // FOR SSL do not verified certificate - curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, FALSE); - curl_setopt($curl, CURLOPT_AUTOREFERER, TRUE ); - - // FeedBurner requires a proper USER-AGENT... - curl_setopt($curl, CURL_HTTP_VERSION_1_1, true); - curl_setopt($curl, CURLOPT_ENCODING, "gzip, deflate"); - curl_setopt($curl, CURLOPT_USERAGENT, $useragent); - - $data = curl_exec($curl); - - $httpcode = curl_getinfo($curl, CURLINFO_HTTP_CODE); - - $httpcodeOK = isset($httpcode) and ($httpcode == 200 or $httpcode == 301); - - curl_close($curl); - } else { - - // create http context and add timeout and user-agent - $context = stream_context_create(array( - 'http'=>array('timeout' => $timeout, - 'header'=> "User-Agent: ".$useragent, /*spoot Mozilla Firefox*/ - 'follow_location' => true), - // FOR SSL do not verified certificate - 'ssl' => array('verify_peer' => false, - 'allow_self_signed' => true) - ) - ); - - // only download page lesser than 4MB - $data = @file_get_contents($url, false, $context, -1, 4000000); // We download at most 4 MB from source. - - if(isset($http_response_header) and isset($http_response_header[0])) { - $httpcodeOK = isset($http_response_header) and isset($http_response_header[0]) and ((strpos($http_response_header[0], '200 OK') !== FALSE) or (strpos($http_response_header[0], '301 Moved Permanently') !== FALSE)); - } - } - - // if response is not empty and response is OK - if (isset($data) and isset($httpcodeOK) and $httpcodeOK ) { - - // take charset of page and get it - preg_match('##Usi', $data, $meta); - - // if meta tag is found - if (!empty($meta[0])) { - // retrieve encoding in $enc - preg_match('#charset="?(.*)"#si', $meta[0], $enc); - - // if charset is found set it otherwise, set it to utf-8 - $html_charset = (!empty($enc[1])) ? strtolower($enc[1]) : 'utf-8'; - - } else { - $html_charset = 'utf-8'; - $enc[1] = ''; - } - - // replace charset of url to charset of page - $data = str_replace('charset='.$enc[1], 'charset='.$html_charset, $data); - - return $data; - } - else { - return FALSE; - } -} - -/** - * Préparation de l'URL avec récupération du contenu avant insertion en base - */ -function prepare_url($url) -{ - $parametres = array(); - $url = html_entity_decode(trim($url)); - - // We remove the annoying parameters added by FeedBurner and GoogleFeedProxy (?utm_source=...) - // from shaarli, by sebsauvage - $i=strpos($url,'&utm_source='); if ($i!==false) $url=substr($url,0,$i); - $i=strpos($url,'?utm_source='); if ($i!==false) $url=substr($url,0,$i); - $i=strpos($url,'#xtor=RSS-'); if ($i!==false) $url=substr($url,0,$i); - - $title = $url; - $html = Encoding::toUTF8(get_external_file($url,15)); - // If get_external_file if not able to retrieve HTTPS content try the same URL with HTTP protocol - if (!preg_match('!^https?://!i', $url) && (!isset($html) || strlen($html) <= 0)) { - $url = 'http://' . $url; - $html = Encoding::toUTF8(get_external_file($url,15)); - } - - if (function_exists('tidy_parse_string')) { - $tidy = tidy_parse_string($html, array(), 'UTF8'); - $tidy->cleanRepair(); - $html = $tidy->value; - } - - if (isset($html) and strlen($html) > 0) - { - $r = new Readability($html, $url); - - $r->convertLinksToFootnotes = CONVERT_LINKS_FOOTNOTES; - $r->revertForcedParagraphElements = REVERT_FORCED_PARAGRAPH_ELEMENTS; - - if($r->init()) - { - $content = $r->articleContent->innerHTML; - $parametres['title'] = $r->articleTitle->innerHTML; - $parametres['content'] = $content; - return $parametres; - } - } - - return FALSE; -} - -/** - * On modifie les URLS des images dans le corps de l'article - */ -function filtre_picture($content, $url, $id) -{ - $matches = array(); - preg_match_all('#<\s*(img)[^>]+src="([^"]*)"[^>]*>#Si', $content, $matches, PREG_SET_ORDER); - foreach($matches as $i => $link) - { - $link[1] = trim($link[1]); - if (!preg_match('#^(([a-z]+://)|(\#))#', $link[1]) ) - { - $absolute_path = get_absolute_link($link[2],$url); - $filename = basename(parse_url($absolute_path, PHP_URL_PATH)); - $directory = create_assets_directory($id); - $fullpath = $directory . '/' . $filename; - download_pictures($absolute_path, $fullpath); - $content = str_replace($matches[$i][2], $fullpath, $content); - } - - } - - return $content; -} - -/** - * Retourne le lien absolu - */ -function get_absolute_link($relative_link, $url) -{ - /* return if already absolute URL */ - if (parse_url($relative_link, PHP_URL_SCHEME) != '') return $relative_link; - - /* queries and anchors */ - if ($relative_link[0]=='#' || $relative_link[0]=='?') return $url . $relative_link; - - /* parse base URL and convert to local variables: - $scheme, $host, $path */ - extract(parse_url($url)); - - /* remove non-directory element from path */ - $path = preg_replace('#/[^/]*$#', '', $path); - - /* destroy path if relative url points to root */ - if ($relative_link[0] == '/') $path = ''; - - /* dirty absolute URL */ - $abs = $host . $path . '/' . $relative_link; - - /* replace '//' or '/./' or '/foo/../' with '/' */ - $re = array('#(/\.?/)#', '#/(?!\.\.)[^/]+/\.\./#'); - for($n=1; $n>0; $abs=preg_replace($re, '/', $abs, -1, $n)) {} - - /* absolute URL is ready! */ - return $scheme.'://'.$abs; -} - -/** - * Téléchargement des images - */ - -function download_pictures($absolute_path, $fullpath) -{ - $rawdata = get_external_file($absolute_path); - - if(file_exists($fullpath)) { - unlink($fullpath); - } - $fp = fopen($fullpath, 'x'); - fwrite($fp, $rawdata); - fclose($fp); -} - -/** - * Crée un répertoire de médias pour l'article - */ -function create_assets_directory($id) -{ - $assets_path = ABS_PATH; - if(!is_dir($assets_path)) { - mkdir($assets_path, 0705); - } - - $article_directory = $assets_path . $id; - if(!is_dir($article_directory)) { - mkdir($article_directory, 0705); - } - - return $article_directory; -} - -/** - * Suppression du répertoire d'images - */ -function remove_directory($directory) -{ - if(is_dir($directory)) { - $files = array_diff(scandir($directory), array('.','..')); - foreach ($files as $file) { - (is_dir("$directory/$file")) ? remove_directory("$directory/$file") : unlink("$directory/$file"); - } - return rmdir($directory); - } -} - -function display_view($view, $id = 0, $full_head = 'yes') -{ - global $tpl, $store, $msg; - - switch ($view) - { - case 'export': - $entries = $store->retrieveAll(); - $tpl->assign('export', myTool::renderJson($entries)); - $tpl->draw('export'); - logm('export view'); - break; - case 'config': - $tpl->assign('load_all_js', 0); - $tpl->draw('head'); - $tpl->draw('home'); - $tpl->draw('config'); - $tpl->draw('js'); - $tpl->draw('footer'); - logm('config view'); - break; - case 'view': - $entry = $store->retrieveOneById($id); - - if ($entry != NULL) { - $tpl->assign('id', $entry['id']); - $tpl->assign('url', $entry['url']); - $tpl->assign('title', $entry['title']); - $content = $entry['content']; - if (function_exists('tidy_parse_string')) { - $tidy = tidy_parse_string($content, array('indent'=>true, 'show-body-only' => true), 'UTF8'); - $tidy->cleanRepair(); - $content = $tidy->value; - } - $tpl->assign('content', $content); - $tpl->assign('is_fav', $entry['is_fav']); - $tpl->assign('is_read', $entry['is_read']); - $tpl->assign('load_all_js', 0); - $tpl->draw('view'); - } - else { - logm('error in view call : entry is NULL'); - } - - logm('view link #' . $id); - break; - default: # home view - $entries = $store->getEntriesByView($view); - - $tpl->assign('entries', $entries); - - if ($full_head == 'yes') { - $tpl->assign('load_all_js', 1); - $tpl->draw('head'); - $tpl->draw('home'); - } - - $tpl->draw('entries'); - - if ($full_head == 'yes') { - $tpl->draw('js'); - $tpl->draw('footer'); - } - break; - } -} - -/** - * Appel d'une action (mark as fav, archive, delete) - */ -function action_to_do($action, $url, $id = 0) -{ - global $store, $msg; - - switch ($action) - { - case 'add': - if ($url == '') - continue; - - if (MyTool::isUrl($url)) { - if($parametres_url = prepare_url($url)) { - if ($store->add($url, $parametres_url['title'], $parametres_url['content'])) { - $last_id = $store->getLastId(); - if (DOWNLOAD_PICTURES) { - $content = filtre_picture($parametres_url['content'], $url, $last_id); - } - $msg->add('s', 'the link has been added successfully'); - } - else { - $msg->add('e', 'error during insertion : the link wasn\'t added'); - } - } - else { - $msg->add('e', 'error during url preparation : the link wasn\'t added'); - logm('error during url preparation'); - } - } - else { - $msg->add('e', 'error during url preparation : the link is not valid'); - logm($url . ' is not a valid url'); - } - - logm('add link ' . $url); - break; - case 'delete': - if ($store->deleteById($id)) { - remove_directory(ABS_PATH . $id); - $msg->add('s', 'the link has been deleted successfully'); - logm('delete link #' . $id); - } - else { - $msg->add('e', 'the link wasn\'t deleted'); - logm('error : can\'t delete link #' . $id); - } - break; - case 'toggle_fav' : - $store->favoriteById($id); - logm('mark as favorite link #' . $id); - break; - case 'toggle_archive' : - $store->archiveById($id); - logm('archive link #' . $id); - break; - default: - break; - } -} - -function logm($message) -{ - $t = strval(date('Y/m/d_H:i:s')).' - '.$_SERVER["REMOTE_ADDR"].' - '.strval($message)."\n"; - file_put_contents('./log.txt',$t,FILE_APPEND); -} diff --git a/inc/poche/Database.class.php b/inc/poche/Database.class.php new file mode 100644 index 00000000..cd5a9a31 --- /dev/null +++ b/inc/poche/Database.class.php @@ -0,0 +1,216 @@ + + * @copyright 2013 + * @license http://www.wtfpl.net/ see COPYING file + */ + +class Database { + var $handle; + + function __construct() + { + switch (STORAGE) { + case 'sqlite': + $db_path = 'sqlite:' . STORAGE_SQLITE; + $this->handle = new PDO($db_path); + break; + case 'mysql': + $db_path = 'mysql:host=' . STORAGE_SERVER . ';dbname=' . STORAGE_DB; + $this->handle = new PDO($db_path, STORAGE_USER, STORAGE_PASSWORD); + break; + case 'postgres': + $db_path = 'pgsql:host=' . STORAGE_SERVER . ';dbname=' . STORAGE_DB; + $this->handle = new PDO($db_path, STORAGE_USER, STORAGE_PASSWORD); + break; + } + + $this->handle->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); + Tools::logm('storage type ' . STORAGE); + } + + private function getHandle() { + return $this->handle; + } + + public function isInstalled() { + $sql = "SELECT username FROM users"; + $query = $this->executeQuery($sql, array()); + $hasAdmin = count($query->fetchAll()); + + if ($hasAdmin == 0) + return FALSE; + + return TRUE; + } + + public function install($login, $password) { + $sql = 'INSERT INTO users ( username, password, name, email) VALUES (?, ?, ?, ?)'; + $params = array($login, $password, $login, ' '); + $query = $this->executeQuery($sql, $params); + + $sequence = ''; + if (STORAGE == 'postgres') { + $sequence = 'users_id_seq'; + } + + $id_user = intval($this->getLastId($sequence)); + + $sql = 'INSERT INTO users_config ( user_id, name, value ) VALUES (?, ?, ?)'; + $params = array($id_user, 'pager', '10'); + $query = $this->executeQuery($sql, $params); + + $sql = 'INSERT INTO users_config ( user_id, name, value ) VALUES (?, ?, ?)'; + $params = array($id_user, 'language', 'en_EN.UTF8'); + $query = $this->executeQuery($sql, $params); + + return TRUE; + } + + private function getConfigUser($id) { + $sql = "SELECT * FROM users_config WHERE user_id = ?"; + $query = $this->executeQuery($sql, array($id)); + $result = $query->fetchAll(); + $user_config = array(); + + foreach ($result as $key => $value) { + $user_config[$value['name']] = $value['value']; + } + + return $user_config; + } + + public function login($username, $password) { + $sql = "SELECT * FROM users WHERE username=? AND password=?"; + $query = $this->executeQuery($sql, array($username, $password)); + $login = $query->fetchAll(); + + $user = array(); + if (isset($login[0])) { + $user['id'] = $login[0]['id']; + $user['username'] = $login[0]['username']; + $user['password'] = $login[0]['password']; + $user['name'] = $login[0]['name']; + $user['email'] = $login[0]['email']; + $user['config'] = $this->getConfigUser($login[0]['id']); + } + + return $user; + } + + public function updatePassword($id, $password) + { + $sql_update = "UPDATE users SET password=? WHERE id=?"; + $params_update = array($password, $id); + $query = $this->executeQuery($sql_update, $params_update); + } + + private function executeQuery($sql, $params) { + try + { + $query = $this->getHandle()->prepare($sql); + $query->execute($params); + return $query; + } + catch (Exception $e) + { + Tools::logm('execute query error : '.$e->getMessage()); + return FALSE; + } + } + + public function retrieveAll($user_id) { + $sql = "SELECT * FROM entries WHERE user_id=? ORDER BY id"; + $query = $this->executeQuery($sql, array($user_id)); + $entries = $query->fetchAll(); + + return $entries; + } + + public function retrieveOneById($id, $user_id) { + $entry = NULL; + $sql = "SELECT * FROM entries WHERE id=? AND user_id=?"; + $params = array(intval($id), $user_id); + $query = $this->executeQuery($sql, $params); + $entry = $query->fetchAll(); + + return $entry[0]; + } + + public function getEntriesByView($view, $user_id, $limit = '') { + switch ($_SESSION['sort']) + { + case 'ia': + $order = 'ORDER BY id'; + break; + case 'id': + $order = 'ORDER BY id DESC'; + break; + case 'ta': + $order = 'ORDER BY lower(title)'; + break; + case 'td': + $order = 'ORDER BY lower(title) DESC'; + break; + default: + $order = 'ORDER BY id'; + break; + } + + switch ($view) + { + case 'archive': + $sql = "SELECT * FROM entries WHERE user_id=? AND is_read=? " . $order; + $params = array($user_id, 1); + break; + case 'fav' : + $sql = "SELECT * FROM entries WHERE user_id=? AND is_fav=? " . $order; + $params = array($user_id, 1); + break; + default: + $sql = "SELECT * FROM entries WHERE user_id=? AND is_read=? " . $order; + $params = array($user_id, 0); + break; + } + + $sql .= ' ' . $limit; + + $query = $this->executeQuery($sql, $params); + $entries = $query->fetchAll(); + + return $entries; + } + + public function add($url, $title, $content, $user_id) { + $sql_action = 'INSERT INTO entries ( url, title, content, user_id ) VALUES (?, ?, ?, ?)'; + $params_action = array($url, $title, $content, $user_id); + $query = $this->executeQuery($sql_action, $params_action); + return $query; + } + + public function deleteById($id, $user_id) { + $sql_action = "DELETE FROM entries WHERE id=? AND user_id=?"; + $params_action = array($id, $user_id); + $query = $this->executeQuery($sql_action, $params_action); + return $query; + } + + public function favoriteById($id, $user_id) { + $sql_action = "UPDATE entries SET is_fav=NOT is_fav WHERE id=? AND user_id=?"; + $params_action = array($id, $user_id); + $query = $this->executeQuery($sql_action, $params_action); + } + + public function archiveById($id, $user_id) { + $sql_action = "UPDATE entries SET is_read=NOT is_read WHERE id=? AND user_id=?"; + $params_action = array($id, $user_id); + $query = $this->executeQuery($sql_action, $params_action); + } + + public function getLastId($column = '') { + return $this->getHandle()->lastInsertId($column); + } +} diff --git a/inc/poche/Poche.class.php b/inc/poche/Poche.class.php new file mode 100644 index 00000000..56910bc0 --- /dev/null +++ b/inc/poche/Poche.class.php @@ -0,0 +1,485 @@ + + * @copyright 2013 + * @license http://www.wtfpl.net/ see COPYING file + */ + +class Poche +{ + public $user; + public $store; + public $tpl; + public $messages; + public $pagination; + + function __construct() + { + $this->store = new Database(); + $this->init(); + $this->messages = new Messages(); + + # installation + if(!$this->store->isInstalled()) + { + $this->install(); + } + } + + private function init() + { + if (file_exists('./install') && !DEBUG_POCHE) { + Tools::logm('folder /install exists'); + die('the folder /install exists, you have to delete it before using poche.'); + } + + Tools::initPhp(); + Session::init(); + + if (isset($_SESSION['poche_user']) && $_SESSION['poche_user'] != array()) { + $this->user = $_SESSION['poche_user']; + } + else { + # fake user, just for install & login screens + $this->user = new User(); + $this->user->setConfig($this->getDefaultConfig()); + } + + # l10n + $language = $this->user->getConfigValue('language'); + putenv('LC_ALL=' . $language); + setlocale(LC_ALL, $language); + bindtextdomain($language, LOCALE); + textdomain($language); + + # template engine + $loader = new Twig_Loader_Filesystem(TPL); + if (DEBUG_POCHE) { + $twig_params = array(); + } + else { + $twig_params = array('cache' => CACHE); + } + $this->tpl = new Twig_Environment($loader, $twig_params); + $this->tpl->addExtension(new Twig_Extensions_Extension_I18n()); + # filter to display domain name of an url + $filter = new Twig_SimpleFilter('getDomain', 'Tools::getDomain'); + $this->tpl->addFilter($filter); + + # Pagination + $this->pagination = new Paginator($this->user->getConfigValue('pager'), 'p'); + } + + private function install() + { + Tools::logm('poche still not installed'); + echo $this->tpl->render('install.twig', array( + 'token' => Session::getToken() + )); + if (isset($_GET['install'])) { + if (($_POST['password'] == $_POST['password_repeat']) + && $_POST['password'] != "" && $_POST['login'] != "") { + # let's rock, install poche baby ! + $this->store->install($_POST['login'], Tools::encodeString($_POST['password'] . $_POST['login'])); + Session::logout(); + Tools::logm('poche is now installed'); + Tools::redirect(); + } + else { + Tools::logm('error during installation'); + Tools::redirect(); + } + } + exit(); + } + + public function getDefaultConfig() + { + return array( + 'pager' => PAGINATION, + 'language' => LANG, + ); + } + + /** + * Call action (mark as fav, archive, delete, etc.) + */ + public function action($action, Url $url, $id = 0, $import = FALSE) + { + switch ($action) + { + case 'add': + if($parametres_url = $url->fetchContent()) { + if ($this->store->add($url->getUrl(), $parametres_url['title'], $parametres_url['content'], $this->user->getId())) { + Tools::logm('add link ' . $url->getUrl()); + $sequence = ''; + if (STORAGE == 'postgres') { + $sequence = 'entries_id_seq'; + } + $last_id = $this->store->getLastId($sequence); + if (DOWNLOAD_PICTURES) { + $content = filtre_picture($parametres_url['content'], $url->getUrl(), $last_id); + } + if (!$import) { + $this->messages->add('s', _('the link has been added successfully')); + } + } + else { + if (!$import) { + $this->messages->add('e', _('error during insertion : the link wasn\'t added')); + Tools::logm('error during insertion : the link wasn\'t added ' . $url->getUrl()); + } + } + } + else { + if (!$import) { + $this->messages->add('e', _('error during fetching content : the link wasn\'t added')); + Tools::logm('error during content fetch ' . $url->getUrl()); + } + } + if (!$import) { + Tools::redirect(); + } + break; + case 'delete': + $msg = 'delete link #' . $id; + if ($this->store->deleteById($id, $this->user->getId())) { + if (DOWNLOAD_PICTURES) { + remove_directory(ABS_PATH . $id); + } + $this->messages->add('s', _('the link has been deleted successfully')); + } + else { + $this->messages->add('e', _('the link wasn\'t deleted')); + $msg = 'error : can\'t delete link #' . $id; + } + Tools::logm($msg); + Tools::redirect('?'); + break; + case 'toggle_fav' : + $this->store->favoriteById($id, $this->user->getId()); + Tools::logm('mark as favorite link #' . $id); + if (!$import) { + Tools::redirect(); + } + break; + case 'toggle_archive' : + $this->store->archiveById($id, $this->user->getId()); + Tools::logm('archive link #' . $id); + if (!$import) { + Tools::redirect(); + } + break; + default: + break; + } + } + + function displayView($view, $id = 0) + { + $tpl_vars = array(); + + switch ($view) + { + case 'config': + $dev = $this->getPocheVersion('dev'); + $prod = $this->getPocheVersion('prod'); + $compare_dev = version_compare(POCHE_VERSION, $dev); + $compare_prod = version_compare(POCHE_VERSION, $prod); + $tpl_vars = array( + 'dev' => $dev, + 'prod' => $prod, + 'compare_dev' => $compare_dev, + 'compare_prod' => $compare_prod, + ); + Tools::logm('config view'); + break; + case 'view': + $entry = $this->store->retrieveOneById($id, $this->user->getId()); + if ($entry != NULL) { + Tools::logm('view link #' . $id); + $content = $entry['content']; + if (function_exists('tidy_parse_string')) { + $tidy = tidy_parse_string($content, array('indent'=>true, 'show-body-only' => true), 'UTF8'); + $tidy->cleanRepair(); + $content = $tidy->value; + } + $tpl_vars = array( + 'entry' => $entry, + 'content' => $content, + ); + } + else { + Tools::logm('error in view call : entry is NULL'); + } + break; + default: # home view + $entries = $this->store->getEntriesByView($view, $this->user->getId()); + $this->pagination->set_total(count($entries)); + $page_links = $this->pagination->page_links('?view=' . $view . '&sort=' . $_SESSION['sort'] . '&'); + $datas = $this->store->getEntriesByView($view, $this->user->getId(), $this->pagination->get_limit()); + $tpl_vars = array( + 'entries' => $datas, + 'page_links' => $page_links, + ); + Tools::logm('display ' . $view . ' view'); + break; + } + + return $tpl_vars; + } + + /** + * update the password of the current user. + * if MODE_DEMO is TRUE, the password can't be updated. + * @todo add the return value + * @todo set the new password in function header like this updatePassword($newPassword) + * @return boolean + */ + public function updatePassword() + { + if (MODE_DEMO) { + $this->messages->add('i', _('in demo mode, you can\'t update your password')); + Tools::logm('in demo mode, you can\'t do this'); + Tools::redirect('?view=config'); + } + else { + if (isset($_POST['password']) && isset($_POST['password_repeat'])) { + if ($_POST['password'] == $_POST['password_repeat'] && $_POST['password'] != "") { + $this->messages->add('s', _('your password has been updated')); + $this->store->updatePassword($this->user->getId(), Tools::encodeString($_POST['password'] . $this->user->getUsername())); + Session::logout(); + Tools::logm('password updated'); + Tools::redirect(); + } + else { + $this->messages->add('e', _('the two fields have to be filled & the password must be the same in the two fields')); + Tools::redirect('?view=config'); + } + } + } + } + + /** + * checks if login & password are correct and save the user in session. + * it redirects the user to the $referer link + * @param string $referer the url to redirect after login + * @todo add the return value + * @return boolean + */ + public function login($referer) + { + if (!empty($_POST['login']) && !empty($_POST['password'])) { + $user = $this->store->login($_POST['login'], Tools::encodeString($_POST['password'] . $_POST['login'])); + if ($user != array()) { + # Save login into Session + Session::login($user['username'], $user['password'], $_POST['login'], Tools::encodeString($_POST['password'] . $_POST['login']), array('poche_user' => new User($user))); + + $this->messages->add('s', _('welcome to your poche')); + if (!empty($_POST['longlastingsession'])) { + $_SESSION['longlastingsession'] = 31536000; + $_SESSION['expires_on'] = time() + $_SESSION['longlastingsession']; + session_set_cookie_params($_SESSION['longlastingsession']); + } else { + session_set_cookie_params(0); + } + session_regenerate_id(true); + Tools::logm('login successful'); + Tools::redirect($referer); + } + $this->messages->add('e', _('login failed: bad login or password')); + Tools::logm('login failed'); + Tools::redirect(); + } else { + $this->messages->add('e', _('login failed: you have to fill all fields')); + Tools::logm('login failed'); + Tools::redirect(); + } + } + + /** + * log out the poche user. It cleans the session. + * @todo add the return value + * @return boolean + */ + public function logout() + { + $this->user = array(); + Session::logout(); + $this->messages->add('s', _('see you soon!')); + Tools::logm('logout'); + Tools::redirect(); + } + + /** + * import from Instapaper. poche needs a ./instapaper-export.html file + * @todo add the return value + * @return boolean + */ + private function importFromInstapaper() + { + # TODO gestion des articles favs + $html = new simple_html_dom(); + $html->load_file('./instapaper-export.html'); + Tools::logm('starting import from instapaper'); + + $read = 0; + $errors = array(); + foreach($html->find('ol') as $ul) + { + foreach($ul->find('li') as $li) + { + $a = $li->find('a'); + $url = new Url(base64_encode($a[0]->href)); + $this->action('add', $url, 0, TRUE); + if ($read == '1') { + $sequence = ''; + if (STORAGE == 'postgres') { + $sequence = 'entries_id_seq'; + } + $last_id = $this->store->getLastId($sequence); + $this->action('toggle_archive', $url, $last_id, TRUE); + } + } + + # the second
      is for read links + $read = 1; + } + $this->messages->add('s', _('import from instapaper completed')); + Tools::logm('import from instapaper completed'); + Tools::redirect(); + } + + /** + * import from Pocket. poche needs a ./ril_export.html file + * @todo add the return value + * @return boolean + */ + private function importFromPocket() + { + # TODO gestion des articles favs + $html = new simple_html_dom(); + $html->load_file('./ril_export.html'); + Tools::logm('starting import from pocket'); + + $read = 0; + $errors = array(); + foreach($html->find('ul') as $ul) + { + foreach($ul->find('li') as $li) + { + $a = $li->find('a'); + $url = new Url(base64_encode($a[0]->href)); + $this->action('add', $url, 0, TRUE); + if ($read == '1') { + $sequence = ''; + if (STORAGE == 'postgres') { + $sequence = 'entries_id_seq'; + } + $last_id = $this->store->getLastId($sequence); + $this->action('toggle_archive', $url, $last_id, TRUE); + } + } + + # the second
        is for read links + $read = 1; + } + $this->messages->add('s', _('import from pocket completed')); + Tools::logm('import from pocket completed'); + Tools::redirect(); + } + + /** + * import from Readability. poche needs a ./readability file + * @todo add the return value + * @return boolean + */ + private function importFromReadability() + { + # TODO gestion des articles lus / favs + $str_data = file_get_contents("./readability"); + $data = json_decode($str_data,true); + Tools::logm('starting import from Readability'); + + foreach ($data as $key => $value) { + $url = ''; + foreach ($value as $attr => $attr_value) { + if ($attr == 'article__url') { + $url = new Url(base64_encode($attr_value)); + } + $sequence = ''; + if (STORAGE == 'postgres') { + $sequence = 'entries_id_seq'; + } + // if ($attr_value == 'favorite' && $attr_value == 'true') { + // $last_id = $this->store->getLastId($sequence); + // $this->store->favoriteById($last_id); + // $this->action('toogle_fav', $url, $last_id, TRUE); + // } + if ($attr_value == 'archive' && $attr_value == 'true') { + $last_id = $this->store->getLastId($sequence); + $this->action('toggle_archive', $url, $last_id, TRUE); + } + } + if ($url->isCorrect()) + $this->action('add', $url, 0, TRUE); + } + $this->messages->add('s', _('import from Readability completed')); + Tools::logm('import from Readability completed'); + Tools::redirect(); + } + + /** + * import datas into your poche + * @param string $from name of the service to import : pocket, instapaper or readability + * @todo add the return value + * @return boolean + */ + public function import($from) + { + if ($from == 'pocket') { + return $this->importFromPocket(); + } + else if ($from == 'readability') { + return $this->importFromReadability(); + } + else if ($from == 'instapaper') { + return $this->importFromInstapaper(); + } + } + + /** + * export poche entries in json + * @return json all poche entries + */ + public function export() + { + $entries = $this->store->retrieveAll($this->user->getId()); + echo $this->tpl->render('export.twig', array( + 'export' => Tools::renderJson($entries), + )); + Tools::logm('export view'); + } + + /** + * Checks online the latest version of poche and cache it + * @param string $which 'prod' or 'dev' + * @return string latest $which version + */ + private function getPocheVersion($which = 'prod') + { + $cache_file = CACHE . '/' . $which; + + # checks if the cached version file exists + if (file_exists($cache_file) && (filemtime($cache_file) > (time() - 86400 ))) { + $version = file_get_contents($cache_file); + } else { + $version = file_get_contents('http://static.inthepoche.com/versions/' . $which); + file_put_contents($cache_file, $version, LOCK_EX); + } + return $version; + } +} \ No newline at end of file diff --git a/inc/poche/Tools.class.php b/inc/poche/Tools.class.php new file mode 100644 index 00000000..d0e43166 --- /dev/null +++ b/inc/poche/Tools.class.php @@ -0,0 +1,226 @@ + + * @copyright 2013 + * @license http://www.wtfpl.net/ see COPYING file + */ + +class Tools +{ + public static function initPhp() + { + define('START_TIME', microtime(true)); + + if (phpversion() < 5) { + die(_('Oops, it seems you don\'t have PHP 5.')); + } + + error_reporting(E_ALL); + + function stripslashesDeep($value) { + return is_array($value) + ? array_map('stripslashesDeep', $value) + : stripslashes($value); + } + + if (get_magic_quotes_gpc()) { + $_POST = array_map('stripslashesDeep', $_POST); + $_GET = array_map('stripslashesDeep', $_GET); + $_COOKIE = array_map('stripslashesDeep', $_COOKIE); + } + + ob_start(); + register_shutdown_function('ob_end_flush'); + } + + public static function getPocheUrl() + { + $https = (!empty($_SERVER['HTTPS']) + && (strtolower($_SERVER['HTTPS']) == 'on')) + || (isset($_SERVER["SERVER_PORT"]) + && $_SERVER["SERVER_PORT"] == '443'); // HTTPS detection. + $serverport = (!isset($_SERVER["SERVER_PORT"]) + || $_SERVER["SERVER_PORT"] == '80' + || ($https && $_SERVER["SERVER_PORT"] == '443') + ? '' : ':' . $_SERVER["SERVER_PORT"]); + + $scriptname = str_replace('/index.php', '/', $_SERVER["SCRIPT_NAME"]); + + if (!isset($_SERVER["SERVER_NAME"])) { + return $scriptname; + } + + return 'http' . ($https ? 's' : '') . '://' + . $_SERVER["SERVER_NAME"] . $serverport . $scriptname; + } + + public static function redirect($url = '') + { + if ($url === '') { + $url = (empty($_SERVER['HTTP_REFERER'])?'?':$_SERVER['HTTP_REFERER']); + if (isset($_POST['returnurl'])) { + $url = $_POST['returnurl']; + } + } + + # prevent loop + if (empty($url) || parse_url($url, PHP_URL_QUERY) === $_SERVER['QUERY_STRING']) { + $url = Tools::getPocheUrl(); + } + + if (substr($url, 0, 1) !== '?') { + $ref = Tools::getPocheUrl(); + if (substr($url, 0, strlen($ref)) !== $ref) { + $url = $ref; + } + } + self::logm('redirect to ' . $url); + header('Location: '.$url); + exit(); + } + + public static function getTplFile($view) + { + $tpl_file = 'home.twig'; + switch ($view) + { + case 'install': + $tpl_file = 'install.twig'; + break; + case 'import'; + $tpl_file = 'import.twig'; + break; + case 'export': + $tpl_file = 'export.twig'; + break; + case 'config': + $tpl_file = 'config.twig'; + break; + case 'view': + $tpl_file = 'view.twig'; + break; + default: + break; + } + return $tpl_file; + } + + public static function getFile($url) + { + $timeout = 15; + $useragent = "Mozilla/5.0 (Windows NT 5.1; rv:18.0) Gecko/20100101 Firefox/18.0"; + + if (in_array ('curl', get_loaded_extensions())) { + # Fetch feed from URL + $curl = curl_init(); + curl_setopt($curl, CURLOPT_URL, $url); + curl_setopt($curl, CURLOPT_TIMEOUT, $timeout); + curl_setopt($curl, CURLOPT_FOLLOWLOCATION, true); + curl_setopt($curl, CURLOPT_RETURNTRANSFER, true); + curl_setopt($curl, CURLOPT_HEADER, false); + + # for ssl, do not verified certificate + curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, FALSE); + curl_setopt($curl, CURLOPT_AUTOREFERER, TRUE ); + + # FeedBurner requires a proper USER-AGENT... + curl_setopt($curl, CURL_HTTP_VERSION_1_1, true); + curl_setopt($curl, CURLOPT_ENCODING, "gzip, deflate"); + curl_setopt($curl, CURLOPT_USERAGENT, $useragent); + + $data = curl_exec($curl); + $httpcode = curl_getinfo($curl, CURLINFO_HTTP_CODE); + $httpcodeOK = isset($httpcode) and ($httpcode == 200 or $httpcode == 301); + curl_close($curl); + } else { + # create http context and add timeout and user-agent + $context = stream_context_create( + array( + 'http' => array( + 'timeout' => $timeout, + 'header' => "User-Agent: " . $useragent, + 'follow_location' => true + ), + 'ssl' => array( + 'verify_peer' => false, + 'allow_self_signed' => true + ) + ) + ); + + # only download page lesser than 4MB + $data = @file_get_contents($url, false, $context, -1, 4000000); + + if (isset($http_response_header) and isset($http_response_header[0])) { + $httpcodeOK = isset($http_response_header) and isset($http_response_header[0]) and ((strpos($http_response_header[0], '200 OK') !== FALSE) or (strpos($http_response_header[0], '301 Moved Permanently') !== FALSE)); + } + } + + # if response is not empty and response is OK + if (isset($data) and isset($httpcodeOK) and $httpcodeOK) { + + # take charset of page and get it + preg_match('##Usi', $data, $meta); + + # if meta tag is found + if (!empty($meta[0])) { + preg_match('#charset="?(.*)"#si', $meta[0], $encoding); + # if charset is found set it otherwise, set it to utf-8 + $html_charset = (!empty($encoding[1])) ? strtolower($encoding[1]) : 'utf-8'; + } else { + $html_charset = 'utf-8'; + $encoding[1] = ''; + } + + # replace charset of url to charset of page + $data = str_replace('charset=' . $encoding[1], 'charset=' . $html_charset, $data); + + return $data; + } + else { + return FALSE; + } + } + + public static function renderJson($data) + { + header('Cache-Control: no-cache, must-revalidate'); + header('Expires: Sat, 26 Jul 1997 05:00:00 GMT'); + header('Content-type: application/json; charset=UTF-8'); + echo json_encode($data); + exit(); + } + + public static function logm($message) + { + if (DEBUG_POCHE) { + $t = strval(date('Y/m/d_H:i:s')) . ' - ' . $_SERVER["REMOTE_ADDR"] . ' - ' . strval($message) . "\n"; + file_put_contents(CACHE . '/log.txt', $t, FILE_APPEND); + error_log('DEBUG POCHE : ' . $message); + } + } + + public static function encodeString($string) + { + return sha1($string . SALT); + } + + public static function checkVar($var, $default = '') + { + return ((isset ($_REQUEST["$var"])) ? htmlentities($_REQUEST["$var"]) : $default); + } + + public static function getDomain($url) + { + $pieces = parse_url($url); + $domain = isset($pieces['host']) ? $pieces['host'] : ''; + if (preg_match('/(?P[a-z0-9][a-z0-9\-]{1,63}\.[a-z\.]{2,6})$/i', $domain, $regs)) { + return $regs['domain']; + } + + return FALSE; + } +} \ No newline at end of file diff --git a/inc/poche/Url.class.php b/inc/poche/Url.class.php new file mode 100644 index 00000000..f4a8f99e --- /dev/null +++ b/inc/poche/Url.class.php @@ -0,0 +1,94 @@ + + * @copyright 2013 + * @license http://www.wtfpl.net/ see COPYING file + */ + +class Url +{ + public $url; + + function __construct($url) + { + $this->url = base64_decode($url); + } + + public function getUrl() { + return $this->url; + } + + public function setUrl($url) { + $this->url = $url; + } + + public function isCorrect() + { + $pattern = '|^http(s)?://[a-z0-9-]+(.[a-z0-9-]+)*(:[0-9]+)?(/.*)?$|i'; + + return preg_match($pattern, $this->url); + } + + public function clean() + { + $url = html_entity_decode(trim($this->url)); + + $stuff = strpos($url,'&utm_source='); + if ($stuff !== FALSE) + $url = substr($url, 0, $stuff); + $stuff = strpos($url,'?utm_source='); + if ($stuff !== FALSE) + $url = substr($url, 0, $stuff); + $stuff = strpos($url,'#xtor=RSS-'); + if ($stuff !== FALSE) + $url = substr($url, 0, $stuff); + + $this->url = $url; + } + + public function fetchContent() + { + if ($this->isCorrect()) { + $this->clean(); + $html = Encoding::toUTF8(Tools::getFile($this->getUrl())); + + # if Tools::getFile() if not able to retrieve HTTPS content, try the same URL with HTTP protocol + if (!preg_match('!^https?://!i', $this->getUrl()) && (!isset($html) || strlen($html) <= 0)) { + $this->setUrl('http://' . $this->getUrl()); + $html = Encoding::toUTF8(Tools::getFile($this->getUrl())); + } + + if (function_exists('tidy_parse_string')) { + $tidy = tidy_parse_string($html, array(), 'UTF8'); + $tidy->cleanRepair(); + $html = $tidy->value; + } + + $parameters = array(); + if (isset($html) and strlen($html) > 0) + { + $readability = new Readability($html, $this->getUrl()); + $readability->convertLinksToFootnotes = CONVERT_LINKS_FOOTNOTES; + $readability->revertForcedParagraphElements = REVERT_FORCED_PARAGRAPH_ELEMENTS; + + if($readability->init()) + { + $content = $readability->articleContent->innerHTML; + $parameters['title'] = $readability->articleTitle->innerHTML; + $parameters['content'] = $content; + + return $parameters; + } + } + } + else { + #$msg->add('e', _('error during url preparation : the link is not valid')); + Tools::logm($this->getUrl() . ' is not a valid url'); + } + + return FALSE; + } +} \ No newline at end of file diff --git a/inc/poche/User.class.php b/inc/poche/User.class.php new file mode 100644 index 00000000..6dac7839 --- /dev/null +++ b/inc/poche/User.class.php @@ -0,0 +1,50 @@ + + * @copyright 2013 + * @license http://www.wtfpl.net/ see COPYING file + */ + +class User +{ + public $id; + public $username; + public $name; + public $password; + public $email; + public $config; + + function __construct($user = array()) + { + if ($user != array()) { + $this->id = $user['id']; + $this->username = $user['username']; + $this->name = $user['name']; + $this->password = $user['password']; + $this->email = $user['email']; + $this->config = $user['config']; + } + } + + public function getId() + { + return $this->id; + } + + public function getUsername() + { + return $this->username; + } + + public function setConfig($config) + { + $this->config = $config; + } + + public function getConfigValue($name) { + return (isset($this->config[$name])) ? $this->config[$name] : FALSE; + } +} \ No newline at end of file diff --git a/inc/poche/config.inc.php b/inc/poche/config.inc.php new file mode 100644 index 00000000..0958600f --- /dev/null +++ b/inc/poche/config.inc.php @@ -0,0 +1,61 @@ + + * @copyright 2013 + * @license http://www.wtfpl.net/ see COPYING file + */ + +# storage +define ('STORAGE','sqlite'); # postgres, mysql, sqlite +define ('STORAGE_SERVER', 'localhost'); # leave blank for sqlite +define ('STORAGE_DB', 'poche'); # only for postgres & mysql +define ('STORAGE_SQLITE', './db/poche.sqlite'); +define ('STORAGE_USER', 'postgres'); # leave blank for sqlite +define ('STORAGE_PASSWORD', 'postgres'); # leave blank for sqlite + +define ('POCHE_VERSION', '1.0-beta1'); +define ('MODE_DEMO', FALSE); +define ('DEBUG_POCHE', TRUE); +define ('CONVERT_LINKS_FOOTNOTES', FALSE); +define ('REVERT_FORCED_PARAGRAPH_ELEMENTS', FALSE); +define ('DOWNLOAD_PICTURES', FALSE); +define ('SHARE_TWITTER', TRUE); +define ('SHARE_MAIL', TRUE); +define ('SALT', '464v54gLLw928uz4zUBqkRJeiPY68zCX'); +define ('ABS_PATH', 'assets/'); +define ('TPL', './tpl'); +define ('LOCALE', './locale'); +define ('CACHE', './cache'); +define ('LANG', 'en_EN.UTF8'); +define ('PAGINATION', '10'); +define ('THEME', 'light'); + +# /!\ Be careful if you change the lines below /!\ +require_once './inc/poche/User.class.php'; +require_once './inc/poche/Tools.class.php'; +require_once './inc/poche/Url.class.php'; +require_once './inc/3rdparty/class.messages.php'; +require_once './inc/poche/Poche.class.php'; +require_once './inc/3rdparty/Readability.php'; +require_once './inc/3rdparty/Encoding.php'; +require_once './inc/poche/Database.class.php'; +require_once './vendor/autoload.php'; +require_once './inc/3rdparty/simple_html_dom.php'; +require_once './inc/3rdparty/paginator.php'; +require_once './inc/3rdparty/Session.class.php'; + +if (DOWNLOAD_PICTURES) { + require_once './inc/poche/pochePictures.php'; +} + +$poche = new Poche(); +#XSRF protection with token +// if (!empty($_POST)) { +// if (!Session::isToken($_POST['token'])) { +// die(_('Wrong token')); +// } +// unset($_SESSION['tokens']); +// } \ No newline at end of file diff --git a/inc/poche/pochePictures.php b/inc/poche/pochePictures.php new file mode 100644 index 00000000..4e4a0b08 --- /dev/null +++ b/inc/poche/pochePictures.php @@ -0,0 +1,110 @@ + + * @copyright 2013 + * @license http://www.wtfpl.net/ see COPYING file + */ + +/** + * On modifie les URLS des images dans le corps de l'article + */ +function filtre_picture($content, $url, $id) +{ + $matches = array(); + preg_match_all('#<\s*(img)[^>]+src="([^"]*)"[^>]*>#Si', $content, $matches, PREG_SET_ORDER); + foreach($matches as $i => $link) { + $link[1] = trim($link[1]); + if (!preg_match('#^(([a-z]+://)|(\#))#', $link[1])) { + $absolute_path = get_absolute_link($link[2],$url); + $filename = basename(parse_url($absolute_path, PHP_URL_PATH)); + $directory = create_assets_directory($id); + $fullpath = $directory . '/' . $filename; + download_pictures($absolute_path, $fullpath); + $content = str_replace($matches[$i][2], $fullpath, $content); + } + + } + + return $content; +} + +/** + * Retourne le lien absolu + */ +function get_absolute_link($relative_link, $url) { + /* return if already absolute URL */ + if (parse_url($relative_link, PHP_URL_SCHEME) != '') return $relative_link; + + /* queries and anchors */ + if ($relative_link[0]=='#' || $relative_link[0]=='?') return $url . $relative_link; + + /* parse base URL and convert to local variables: + $scheme, $host, $path */ + extract(parse_url($url)); + + /* remove non-directory element from path */ + $path = preg_replace('#/[^/]*$#', '', $path); + + /* destroy path if relative url points to root */ + if ($relative_link[0] == '/') $path = ''; + + /* dirty absolute URL */ + $abs = $host . $path . '/' . $relative_link; + + /* replace '//' or '/./' or '/foo/../' with '/' */ + $re = array('#(/\.?/)#', '#/(?!\.\.)[^/]+/\.\./#'); + for($n=1; $n>0; $abs=preg_replace($re, '/', $abs, -1, $n)) {} + + /* absolute URL is ready! */ + return $scheme.'://'.$abs; +} + +/** + * Téléchargement des images + */ +function download_pictures($absolute_path, $fullpath) +{ + $rawdata = Tools::getFile($absolute_path); + + if(file_exists($fullpath)) { + unlink($fullpath); + } + $fp = fopen($fullpath, 'x'); + fwrite($fp, $rawdata); + fclose($fp); +} + +/** + * Crée un répertoire de médias pour l'article + */ +function create_assets_directory($id) +{ + $assets_path = ABS_PATH; + if(!is_dir($assets_path)) { + mkdir($assets_path, 0705); + } + + $article_directory = $assets_path . $id; + if(!is_dir($article_directory)) { + mkdir($article_directory, 0705); + } + + return $article_directory; +} + +/** + * Suppression du répertoire d'images + */ +function remove_directory($directory) +{ + if(is_dir($directory)) { + $files = array_diff(scandir($directory), array('.','..')); + foreach ($files as $file) { + (is_dir("$directory/$file")) ? remove_directory("$directory/$file") : unlink("$directory/$file"); + } + return rmdir($directory); + } +} \ No newline at end of file diff --git a/inc/rain.tpl.class.php b/inc/rain.tpl.class.php deleted file mode 100644 index 6522c798..00000000 --- a/inc/rain.tpl.class.php +++ /dev/null @@ -1,1043 +0,0 @@ -), stylesheet (), script ( \ No newline at end of file diff --git a/tpl/_footer.twig b/tpl/_footer.twig new file mode 100644 index 00000000..6891756a --- /dev/null +++ b/tpl/_footer.twig @@ -0,0 +1,4 @@ +
        +

        {% trans "powered by" %} poche

        + {% if constant('DEBUG_POCHE') == 1 %}

        {% trans "debug mode is on so cache is off." %} {% trans "your poche version:" %}{{constant('POCHE_VERSION')}}. {% trans "storage:" %} {{constant('STORAGE')}}

        {% endif %} +
        \ No newline at end of file diff --git a/tpl/_head.twig b/tpl/_head.twig new file mode 100644 index 00000000..f25f0471 --- /dev/null +++ b/tpl/_head.twig @@ -0,0 +1,9 @@ + + + + + + + + + \ No newline at end of file diff --git a/tpl/_menu.twig b/tpl/_menu.twig new file mode 100644 index 00000000..699d6a0c --- /dev/null +++ b/tpl/_menu.twig @@ -0,0 +1,7 @@ + \ No newline at end of file diff --git a/tpl/_messages.twig b/tpl/_messages.twig new file mode 100644 index 00000000..679aa098 --- /dev/null +++ b/tpl/_messages.twig @@ -0,0 +1 @@ + {{ messages | raw }} \ No newline at end of file diff --git a/tpl/_top.twig b/tpl/_top.twig new file mode 100644 index 00000000..ae01cc3f --- /dev/null +++ b/tpl/_top.twig @@ -0,0 +1,3 @@ +
        +

        logo poche

        +
        \ No newline at end of file diff --git a/tpl/config.html b/tpl/config.html deleted file mode 100644 index 1100d455..00000000 --- a/tpl/config.html +++ /dev/null @@ -1,27 +0,0 @@ -
        -

        Bookmarklet

        -

        Thanks to the bookmarklet, you will be able to easily add a link to your poche. If you don't know how use a bookmarklet, have a look here.

        -

        Drag & drop this link to your bookmarks bar and have fun with poche.

        -

        poche it !

        - -

        Password

        -
        -
        -
        - - -
        -
        - - -
        -
        - -
        -
        - - -
        -

        Export

        -

        Click here to export your poche datas.

        -
        \ No newline at end of file diff --git a/tpl/config.twig b/tpl/config.twig new file mode 100644 index 00000000..95d5d8cf --- /dev/null +++ b/tpl/config.twig @@ -0,0 +1,57 @@ +{% extends "layout.twig" %} + +{% block title %}{% trans "config" %}{% endblock %} +{% block menu %} + +{% endblock %} +{% block content %} +

        {% trans "Bookmarklet" %}

        +

        {% trans "Thanks to the bookmarklet, you will be able to easily add a link to your poche." %} {% trans "Have a look to this documentation:" %} inthepoche.com.

        +

        {% trans "Drag & drop this link to your bookmarks bar and have fun with poche." %}

        +

        {% trans "poche it!" %}

        + +

        {% trans "Updating poche" %}

        +

        +

        + +

        {% trans "Change your password" %}

        +
        +
        +
        + + +
        +
        + + +
        +
        + +
        +
        + + +
        + +

        {% trans "Import" %}

        +

        {% trans "Please execute the import script locally, it can take a very long time." %}

        +

        {% trans "More infos in the official doc:" %} inthepoche.com

        +

        + +

        {% trans "Export your poche datas" %}

        +

        {% trans "Click here" %} {% trans "to export your poche datas." %}

        +{% endblock %} \ No newline at end of file diff --git a/css/knacss.css b/tpl/css/knacss.css similarity index 100% rename from css/knacss.css rename to tpl/css/knacss.css diff --git a/tpl/css/messages.css b/tpl/css/messages.css new file mode 100755 index 00000000..9222bb88 --- /dev/null +++ b/tpl/css/messages.css @@ -0,0 +1,13 @@ +.messages { width: 400px; -moz-border-radius: 4px; border-radius: 4px; display: block; padding: 10px 0; margin: 10px auto 10px; clear: both; } +.messages a.closeMessage { margin: -14px -8px 0 0; display:none; width: 16px; height: 16px; float: right; background: url(../img/messages/close.png) no-repeat; } +/*.messages:hover a.closeMessage { visibility:visible; }*/ +.messages p { margin: 3px 0 3px 10px !important; padding: 0 10px 0 23px !important; font-size: 14px; line-height: 16px; } +.messages.error { border: 1px solid #C42608; color: #c00 !important; background: #FFF0EF; } +.messages.error p { background: url(../img/messages/cross.png ) no-repeat 0px 50%; color:#c00 !important; } +.messages.success {background: #E0FBCC; border: 1px solid #6DC70C; } +.messages.success p { background: url(../img/messages/tick.png) no-repeat 0px 50%; color: #2B6301 !important; } +.messages.warning { background: #FFFCD3; border: 1px solid #EBCD41; color: #000; } +.messages.warning p { background: url(../img/messages/warning.png ) no-repeat 0px 50%; color: #5F4E01; } +.messages.information, .messages.info { background: #DFEBFB; border: 1px solid #82AEE7; } +.messages.information p, .messages.info p { background: url(../img/messages/help.png ) no-repeat 0px 50%; color: #064393; } +.messages.information a { text-decoration: underline; } \ No newline at end of file diff --git a/tpl/css/style-light.css b/tpl/css/style-light.css new file mode 100644 index 00000000..9ea7955a --- /dev/null +++ b/tpl/css/style-light.css @@ -0,0 +1,53 @@ + +a.back span { + background: url('../img/light/left.png') no-repeat; +} + +a.top span { + background: url('../img/light/top.png') no-repeat; +} + + +a.fav span { + background: url('../img/light/star-on.png') no-repeat; +} + +a.fav span:hover { + background: url('../img/light/star-off.png') no-repeat; +} + +a.fav-off span { + background: url('../img/light/star-off.png') no-repeat; +} + +a.fav-off span:hover { + background: url('../img/light/star-on.png') no-repeat; +} + +a.archive span { + background: url('../img/light/checkmark-on.png') no-repeat; +} + +a.archive span:hover { + background: url('../img/light/checkmark-off.png') no-repeat; +} + +a.archive-off span { + background: url('../img/light/checkmark-off.png') no-repeat; +} + +a.archive-off span:hover { + background: url('../img/light/checkmark-on.png') no-repeat; +} + +a.twitter span { + background: url('../img/light/twitter.png') no-repeat; +} + +a.email span { + background: url('../img/light/envelop.png') no-repeat; +} + +a.delete span { + background: url('../img/light/remove.png') no-repeat; +} \ No newline at end of file diff --git a/tpl/css/style.css b/tpl/css/style.css new file mode 100644 index 00000000..d23c1896 --- /dev/null +++ b/tpl/css/style.css @@ -0,0 +1,244 @@ +body { + font-size: 16px; + font-family: 'Roboto', sans-serif; + margin: 10px; +} + +header { + text-align: center; +} + +header h1 { + font-size: 1.3em; +} + +.bouton { + border-radius: 2px; +} + +#main { + margin: 0 auto; +} + +#main ul#links { + padding: 0; + list-style-type: none; + text-align: center; + font-size: 0.9em; +} + +#main ul#links li { + display: inline; +} + +#main ul#links li a.current { + -webkit-border-radius: 2px; + border-radius: 2px; +} + +#main ul#sort { + padding: 0; + list-style-type: none; + text-align: center; + opacity: 0.5; +} + +#main ul#sort li { + display: inline; + font-size: 0.9em; +} + +#main ul#sort img:hover { + cursor: pointer; +} + + +#links a{ + text-decoration: none; + padding: 5px 10px; +} +#links a:hover{ + -webkit-border-radius: 2px; + border-radius: 2px; +} + +/*** ***/ +/*** LINKS DISPLAY ***/ + +#main a.tool { + text-decoration: none; + cursor: pointer; +} + +#main #content { + margin-top: 20px; +} + +#main #content h2 { + font-size: 1.3em; + text-decoration: none; +} + +#main #content .entrie { + border-bottom: 1px dashed #222222; +} + +#main .entrie ul.tools { + list-style-type: none; +} + +#main .entrie ul.tools li { + /*display: inline;*/ +} + +.tools { + float: right; + text-align: right; + opacity: 0.5; +} + +.tools p { + font-size: 0.8em;} + +/* +.tools ul { + padding: 0; margin: 0; + list-style-type: none; +} + +.tools ul li { + line-height: 20px; +} + +.tools a.tool { + cursor: pointer; +}*/ + +#main .entrie .tools a.tool span, #article .tools a.tool span { + display: inline-block; + width: 16px; + height: 16px; +} + +#main .entrie .url { + font-size: 13px; +} + + +/*** ***/ +/*** ARTICLE PAGE ***/ + +#article { + margin: 0 auto; +} +#article header { + text-align: left; +} + +#article header a { + text-decoration: none; +} + +.vieworiginal a, .vieworiginal a:hover, .vieworiginal a:visited { + text-decoration: none; + color: #888888; +} + +.backhome { + display: inline; +} + +#article .tools { + position: relative; + display: inline; + top: 0px; + right: 0px; + width: 100%; +} + +#article .tools ul li{ + display: inline; +} + + +/*** GENERAL ***/ +body { + color: #000; +} + +a, a:hover, a:visited { + color: #000; +} + +.bouton { + background-color: #000; + color: #fff; + border: none; +} +.bouton:hover { + background-color: #222222; + color: #F1F1F1; +} + +#main ul#links li a.current { + background-color: #000; + color: #fff; +} + +#links a:hover{ + background-color: #040707; + color: #F1F1F1; +} + + +/*** ***/ +/*** ARTICLE PAGE ***/ + +#article header, #article article { + border-bottom: 1px solid #222222; +} + + +/* Pagination */ +.pagination { + clear: both; + padding-bottom: 20px; + padding-top: 10px; + text-align: right; +} +.pagination a { + border: 1px solid #D5D5D5; + color: #333; + font-size: 11px; + font-weight: bold; + height: 25px; + padding: 4px 8px; + text-decoration: none; + margin:2px; +} +.pagination a:hover, .pagination a:active { + background:#efefef; +} +.pagination span.current { + background-color: #ccc; + border: 1px solid #D5D5D5; + color: #000; + font-size: 11px; + font-weight: bold; + height: 25px; + padding: 4px 8px; + text-decoration: none; + margin:2px; +} +.pagination span.disabled { + border: 1px solid #EEEEEE; + color: #DDDDDD; + margin:2px; + padding: 4px 8px; + font-size: 11px; + font-weight: bold; +} + +footer { + clear: both; +} \ No newline at end of file diff --git a/tpl/entries.html b/tpl/entries.html deleted file mode 100644 index 8526a3c9..00000000 --- a/tpl/entries.html +++ /dev/null @@ -1,18 +0,0 @@ -
        - {loop="entries"} -
        - -

        - {$value.title} -

        -
        -
          -
        • -
        • -
        • -
        -
        -
        -
        - {/loop} -
        \ No newline at end of file diff --git a/tpl/export.html b/tpl/export.html deleted file mode 100644 index d22d05fc..00000000 --- a/tpl/export.html +++ /dev/null @@ -1 +0,0 @@ -export {$export} \ No newline at end of file diff --git a/tpl/export.twig b/tpl/export.twig new file mode 100644 index 00000000..4adb9540 --- /dev/null +++ b/tpl/export.twig @@ -0,0 +1 @@ +{{ export }} \ No newline at end of file diff --git a/tpl/footer.html b/tpl/footer.html deleted file mode 100644 index b8bd755c..00000000 --- a/tpl/footer.html +++ /dev/null @@ -1,7 +0,0 @@ - - - - - \ No newline at end of file diff --git a/tpl/head.html b/tpl/head.html deleted file mode 100644 index e95f6100..00000000 --- a/tpl/head.html +++ /dev/null @@ -1,22 +0,0 @@ - - - - - - - - - - - {$title} - - - - - - - - - - - \ No newline at end of file diff --git a/tpl/home.html b/tpl/home.html deleted file mode 100644 index 90e247f7..00000000 --- a/tpl/home.html +++ /dev/null @@ -1,19 +0,0 @@ - -
        -

        logo pochepoche

        -
        -
        - - {if condition="isset($entries)"} -
          -
        • by date
        • -
        • by title
        • -
        - {/if} - {include="messages"} \ No newline at end of file diff --git a/tpl/home.twig b/tpl/home.twig new file mode 100644 index 00000000..a6da641f --- /dev/null +++ b/tpl/home.twig @@ -0,0 +1,29 @@ +{% extends "layout.twig" %} +{% block title %}{% trans "home" %}{% endblock %} +{% block menu %} +{% include '_menu.twig' %} +{% endblock %} +{% block precontent %} +
          +
        • {% trans {% trans "by date" %} {% trans
        • +
        • {% trans {% trans "by title" %} {% trans
        • +
        +{% endblock %} +{% block content %} + {{ page_links | raw }} + {% for entry in entries %} +
        +

        {{ entry.title|e }}

        +
          +
        • +
        • +
        • +
        • + +
        +

        {{ entry.content|striptags|slice(0, 300) }}...

        +

        {{ entry.url | e | getDomain }}

        +
        + {% endfor %} + {{ page_links | raw }} +{% endblock %} \ No newline at end of file diff --git a/img/apple-touch-icon-144x144-precomposed.png b/tpl/img/apple-touch-icon-144x144-precomposed.png similarity index 100% rename from img/apple-touch-icon-144x144-precomposed.png rename to tpl/img/apple-touch-icon-144x144-precomposed.png diff --git a/img/apple-touch-icon-72x72-precomposed.png b/tpl/img/apple-touch-icon-72x72-precomposed.png similarity index 100% rename from img/apple-touch-icon-72x72-precomposed.png rename to tpl/img/apple-touch-icon-72x72-precomposed.png diff --git a/img/apple-touch-icon.png b/tpl/img/apple-touch-icon.png similarity index 100% rename from img/apple-touch-icon.png rename to tpl/img/apple-touch-icon.png diff --git a/img/favicon.ico b/tpl/img/favicon.ico similarity index 100% rename from img/favicon.ico rename to tpl/img/favicon.ico diff --git a/img/light/checkmark-off.png b/tpl/img/light/checkmark-off.png similarity index 100% rename from img/light/checkmark-off.png rename to tpl/img/light/checkmark-off.png diff --git a/img/light/checkmark-on.png b/tpl/img/light/checkmark-on.png similarity index 100% rename from img/light/checkmark-on.png rename to tpl/img/light/checkmark-on.png diff --git a/img/down.png b/tpl/img/light/down.png similarity index 100% rename from img/down.png rename to tpl/img/light/down.png diff --git a/tpl/img/light/envelop.png b/tpl/img/light/envelop.png new file mode 100755 index 00000000..6be1c886 Binary files /dev/null and b/tpl/img/light/envelop.png differ diff --git a/tpl/img/light/left.png b/tpl/img/light/left.png new file mode 100755 index 00000000..a0a53631 Binary files /dev/null and b/tpl/img/light/left.png differ diff --git a/img/light/remove.png b/tpl/img/light/remove.png similarity index 100% rename from img/light/remove.png rename to tpl/img/light/remove.png diff --git a/img/light/star-off.png b/tpl/img/light/star-off.png similarity index 100% rename from img/light/star-off.png rename to tpl/img/light/star-off.png diff --git a/img/light/star-on.png b/tpl/img/light/star-on.png similarity index 100% rename from img/light/star-on.png rename to tpl/img/light/star-on.png diff --git a/img/up.png b/tpl/img/light/top.png old mode 100644 new mode 100755 similarity index 100% rename from img/up.png rename to tpl/img/light/top.png diff --git a/tpl/img/light/twitter.png b/tpl/img/light/twitter.png new file mode 100755 index 00000000..cfcfe419 Binary files /dev/null and b/tpl/img/light/twitter.png differ diff --git a/tpl/img/logo.png b/tpl/img/logo.png new file mode 100644 index 00000000..549b8466 Binary files /dev/null and b/tpl/img/logo.png differ diff --git a/img/messages/close.png b/tpl/img/messages/close.png old mode 100644 new mode 100755 similarity index 100% rename from img/messages/close.png rename to tpl/img/messages/close.png diff --git a/img/messages/cross.png b/tpl/img/messages/cross.png old mode 100644 new mode 100755 similarity index 100% rename from img/messages/cross.png rename to tpl/img/messages/cross.png diff --git a/img/messages/help.png b/tpl/img/messages/help.png old mode 100644 new mode 100755 similarity index 100% rename from img/messages/help.png rename to tpl/img/messages/help.png diff --git a/img/messages/tick.png b/tpl/img/messages/tick.png old mode 100644 new mode 100755 similarity index 100% rename from img/messages/tick.png rename to tpl/img/messages/tick.png diff --git a/img/messages/warning.png b/tpl/img/messages/warning.png old mode 100644 new mode 100755 similarity index 100% rename from img/messages/warning.png rename to tpl/img/messages/warning.png diff --git a/tpl/install.html b/tpl/install.html deleted file mode 100644 index d11a7810..00000000 --- a/tpl/install.html +++ /dev/null @@ -1,30 +0,0 @@ -{include="head"} - -
        -

        logo pochepoche

        -
        -
        -
        -
        -

        install your poche

        -
        - - -
        -
        - - -
        -
        - - -
        -
        - -
        -
        - - -
        - -{include="footer"} diff --git a/tpl/install.twig b/tpl/install.twig new file mode 100644 index 00000000..8bcede0d --- /dev/null +++ b/tpl/install.twig @@ -0,0 +1,28 @@ +{% extends "layout.twig" %} +{% block title %}{% trans "installation" %}{% endblock %} +{% block content %} +
        +
        +

        {% trans "install your poche" %}

        +

        + {% trans "poche is still not installed. Please fill the below form to install it. Don't hesitate to read the documentation on poche website." %} +

        +
        + + +
        +
        + + +
        +
        + + +
        +
        + +
        +
        + +
        +{% endblock %} \ No newline at end of file diff --git a/tpl/js.html b/tpl/js.html deleted file mode 100644 index a02212b0..00000000 --- a/tpl/js.html +++ /dev/null @@ -1,22 +0,0 @@ - - - - {if="$load_all_js == '1'"} - - - {/if} \ No newline at end of file diff --git a/tpl/layout.twig b/tpl/layout.twig new file mode 100644 index 00000000..c4bbe779 --- /dev/null +++ b/tpl/layout.twig @@ -0,0 +1,29 @@ + + + + + + + + + + + {% block title %}{% endblock %} - poche + {% include '_head.twig' %} + {% include '_bookmarklet.twig' %} + + + {% include '_top.twig' %} +
        + {% block menu %}{% endblock %} + {% block precontent %}{% endblock %} + {% block messages %} + {% include '_messages.twig' %} + {% endblock %} +
        + {% block content %}{% endblock %} +
        +
        + {% include '_footer.twig' %} + + \ No newline at end of file diff --git a/tpl/login.html b/tpl/login.html deleted file mode 100644 index 69c17a55..00000000 --- a/tpl/login.html +++ /dev/null @@ -1,33 +0,0 @@ -{include="head"} - -
        -

        logo pochepoche

        -
        -
        -
        -
        -

        login to your poche

        -
        - - -
        -
        - - -
        -
        - -
        - - (Do not check on public computers) -
        -
        -
        - -
        -
        - - -
        - -{include="footer"} diff --git a/tpl/login.twig b/tpl/login.twig new file mode 100644 index 00000000..0ae130bc --- /dev/null +++ b/tpl/login.twig @@ -0,0 +1,32 @@ +{% extends "layout.twig" %} + +{% block title %}{% trans "login to your poche" %}{% endblock %} +{% block content %} +
        +
        +

        {% trans "login to your poche" %}

        + {% if constant('MODE_DEMO') == 1 %}

        {% trans "you are in demo mode, some features may be disabled." %}

        {% endif %} +
        + + +
        + +
        + + +
        +
        + +
        + + {% trans "(Do not check on public computers)" %} +
        +
        +
        + +
        +
        + + +
        +{% endblock %} \ No newline at end of file diff --git a/tpl/messages.html b/tpl/messages.html deleted file mode 100644 index 87af259b..00000000 --- a/tpl/messages.html +++ /dev/null @@ -1 +0,0 @@ -
        display(); ?>
        \ No newline at end of file diff --git a/tpl/view.html b/tpl/view.html deleted file mode 100644 index af94df2e..00000000 --- a/tpl/view.html +++ /dev/null @@ -1,53 +0,0 @@ - - - - - - - - - - - {$title} - - - - - - - - - - - - -
        -
        - -
        -
        - -
        -
        -

        {$title}

        - -
        - {include="messages"} -
        -
        - {$content} -
        -
        - -
        - -
        - - {include="js"} - {include="footer"} \ No newline at end of file diff --git a/tpl/view.twig b/tpl/view.twig new file mode 100644 index 00000000..e999afa1 --- /dev/null +++ b/tpl/view.twig @@ -0,0 +1,40 @@ +{% extends "layout.twig" %} +{% block title %}{% trans "home" %}{% endblock %} +{% block content %} +
        +
        +
          +
        • +
        • + +
        • +
        • + {% if constant('SHARE_TWITTER') == 1 %}
        • {% endif %} + {% if constant('SHARE_MAIL') == 1 %}
        • {% endif %} + +
        +
        +
        +

        {{ entry.title|e }}

        + +
        + +
        +
          +
        • +
        • +
        • + +
        • +
        • + {% if constant('SHARE_TWITTER') == 1 %}
        • {% endif %} + {% if constant('SHARE_MAIL') == 1 %}
        • {% endif %} + +
        +

        {% trans "this article appears wrong?" %} {% trans "create an issue" %} {% trans "or" %} {% trans "contact us by mail" %}

        +
        +
        +{% endblock %} \ No newline at end of file