Merge pull request #109 from inthepoche/dev

merge dev into master
This commit is contained in:
Nicolas Lœuillet 2013-08-08 09:36:10 -07:00
commit 9a8b4ff4ed
101 changed files with 3733 additions and 3281 deletions

5
.gitignore vendored Normal file
View File

@ -0,0 +1,5 @@
vendor
composer.phar
db/poche.sqlite
output
phpdoc*

15
.travis.yml Normal file
View File

@ -0,0 +1,15 @@
language: php
php:
- 5.4
branches:
only:
- dev
before_script:
- composer install
notifications:
email:
- nicolas.loeuillet@gmail.com

12
CREDITS
View File

@ -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
Contributors : https://github.com/inthepoche/poche/graphs/contributors

53
INSTALL.md Normal file
View File

@ -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 !

View File

@ -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 <nicolas@loeuillet.org>
Copyright © 2010-2013 Nicolas Lœuillet <nicolas.loeuillet@gmail.com>
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.

11
TODO.md Normal file
View File

@ -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

7
composer.json Normal file
View File

@ -0,0 +1,7 @@
{
"require": {
"twig/twig": "1.*",
"twig/extensions": "1.0.*",
"umpirsky/twig-gettext-extractor": "1.1.*"
}
}

744
composer.lock generated Normal file
View File

@ -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": [
]
}

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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; }

Binary file not shown.

Before

Width:  |  Height:  |  Size: 267 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 221 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 223 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 786 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 265 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 330 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 277 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 225 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 911 B

View File

@ -1,50 +0,0 @@
<?php
/**
* poche, a read it later open source system
*
* @category poche
* @author Nicolas Lœuillet <support@inthepoche.com>
* @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. <br /><a href="import.php?start">Bye bye Pocket, let\'s go !</a>';
}
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 <ul>
# Le premier concerne les éléments non lus
# Le second concerne les éléments archivés
$read = 1;
}
echo 'Import from Pocket completed. <a href="index.php">Welcome to #poche !</a>';
logm('import from pocket completed');
}

View File

@ -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('<div><p>Para 1</p><p>Para 2</p></div>');
* $elem = $doc->getElementsByTagName('div')->item(0);
*
*
* // print innerHTML
* echo $elem->innerHTML; // prints '<p>Para 1</p><p>Para 2</p>'
* echo "\n\n";
*
*
* // set innerHTML
* $elem->innerHTML = '<a href="http://fivefilters.org">FiveFilters.org</a>';
* echo $elem->innerHTML; // prints '<a href="http://fivefilters.org">FiveFilters.org</a>'
* 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 <htmlfragment> 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 <html><body> tags which we don't really want to keep.
// Note: despite the warning, if loadHTML succeeds it will return true.
$result = @$f->loadHTML('<htmlfragment>'.$value.'</htmlfragment>');
@ -86,7 +86,7 @@ class JSLikeHTMLElement extends DOMElement
* @code
* $string = $div->innerHTML;
* @endcode
*/
*/
public function __get($name)
{
if ($name == 'innerHTML') {

View File

@ -1,5 +1,5 @@
<?php
/**
/**
* Arc90's Readability ported to PHP for FiveFilters.org
* Based on readability.js version 1.7.1 (without multi-page support)
* Updated to allow HTML5 parsing with html5lib
@ -13,34 +13,34 @@
* License: Apache License, Version 2.0
* Requires: PHP5
* Date: 2012-09-19
*
*
* Differences between the PHP port and the original
* ------------------------------------------------------
* Arc90's Readability is designed to run in the browser. It works on the DOM
* tree (the parsed HTML) after the page's CSS styles have been applied and
* Javascript code executed. This PHP port does not run inside a browser.
* We use PHP's ability to parse HTML to build our DOM tree, but we cannot
* rely on CSS or Javascript support. As such, the results will not always
* match Arc90's Readability. (For example, if a web page contains CSS style
* rules or Javascript code which hide certain HTML elements from display,
* Arc90's Readability will dismiss those from consideration but our PHP port,
* Arc90's Readability is designed to run in the browser. It works on the DOM
* tree (the parsed HTML) after the page's CSS styles have been applied and
* Javascript code executed. This PHP port does not run inside a browser.
* We use PHP's ability to parse HTML to build our DOM tree, but we cannot
* rely on CSS or Javascript support. As such, the results will not always
* match Arc90's Readability. (For example, if a web page contains CSS style
* rules or Javascript code which hide certain HTML elements from display,
* Arc90's Readability will dismiss those from consideration but our PHP port,
* unable to understand CSS or Javascript, will not know any better.)
*
* Another significant difference is that the aim of Arc90's Readability is
* to re-present the main content block of a given web page so users can
* read it more easily in their browsers. Correct identification, clean up,
* and separation of the content block is only a part of this process.
* This PHP port is only concerned with this part, it does not include code
* that relates to presentation in the browser - Arc90 already do
* that extremely well, and for PDF output there's FiveFilters.org's
*
* Another significant difference is that the aim of Arc90's Readability is
* to re-present the main content block of a given web page so users can
* read it more easily in their browsers. Correct identification, clean up,
* and separation of the content block is only a part of this process.
* This PHP port is only concerned with this part, it does not include code
* that relates to presentation in the browser - Arc90 already do
* that extremely well, and for PDF output there's FiveFilters.org's
* PDF Newspaper: http://fivefilters.org/pdf-newspaper/.
*
* Finally, this class contains methods that might be useful for developers
* working on HTML document fragments. So without deviating too much from
* the original code (which I don't want to do because it makes debugging
* and updating more difficult), I've tried to make it a little more
* developer friendly. You should be able to use the methods here on
* existing DOMElement objects without passing an entire HTML document to
*
* Finally, this class contains methods that might be useful for developers
* working on HTML document fragments. So without deviating too much from
* the original code (which I don't want to do because it makes debugging
* and updating more difficult), I've tried to make it a little more
* developer friendly. You should be able to use the methods here on
* existing DOMElement objects without passing an entire HTML document to
* be parsed.
*/
@ -48,7 +48,7 @@
require_once(dirname(__FILE__).'/JSLikeHTMLElement.php');
// Alternative usage (for testing only!)
// uncomment the lines below and call Readability.php in your browser
// uncomment the lines below and call Readability.php in your browser
// passing it the URL of the page you'd like content from, e.g.:
// Readability.php?url=http://medialens.org/alerts/09/090615_the_guardian_climate.php
@ -75,11 +75,11 @@ class Readability
public $url = null; // optional - URL where HTML was retrieved
public $debug = false;
public $lightClean = true; // preserves more content (experimental) added 2012-09-19
protected $body = null; //
protected $body = null; //
protected $bodyCache = null; // Cache the body HTML in case we need to re-use it later
protected $flags = 7; // 1 | 2 | 4; // Start with all flags set.
protected $success = false; // indicates whether we were able to extract or not
/**
* All of the regular expressions in use within readability.
* Defined up here so we don't instantiate them repeatedly in loops.
@ -97,19 +97,19 @@ class Readability
'killBreaks' => '/(<br\s*\/?>(\s|&nbsp;?)*){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 = '<p>Sorry, Readability was unable to parse this page for content.</p>';
$articleContent->innerHTML = '<p>Sorry, Readability was unable to parse this page for content.</p>';
}
$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 = '<h3>References</h3>';
$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 = '<small><sup>[' . $linkCount . ']</sup></small>';
$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 . '<small> (' . $linkDomain . ')</small>';
$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 <p> 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('/<br[^>]*>\s*<p/i', '<p', $articleContent->innerHTML);
//articleContent.innerHTML = articleContent.innerHTML.replace(/<br[^>]*>\s*<p/gi, '<p');
//articleContent.innerHTML = articleContent.innerHTML.replace(/<br[^>]*>\s*<p/gi, '<p');
}
catch (Exception $e) {
$this->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 <input> 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 <input> 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;
}

View File

@ -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.

460
inc/class.messages.php → inc/3rdparty/class.messages.php vendored Normal file → Executable file
View File

@ -1,231 +1,231 @@
<?php
//--------------------------------------------------------------------------------------------------
// Session-Based Flash Messages v1.0
// Copyright 2012 Mike Everhart (http://mikeeverhart.net)
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
//------------------------------------------------------------------------------
// Description:
//------------------------------------------------------------------------------
//
// Stores messages in Session data to be easily retrieved later on.
// This class includes four different types of messages:
// - Success
// - Error
// - Warning
// - Information
//
// See README for basic usage instructions, or see samples/index.php for more advanced samples
//
//--------------------------------------------------------------------------------------------------
// Changelog
//--------------------------------------------------------------------------------------------------
//
// 2011-05-15 - v1.0 - Initial Version
//
//--------------------------------------------------------------------------------------------------
class Messages {
//-----------------------------------------------------------------------------------------------
// Class Variables
//-----------------------------------------------------------------------------------------------
var $msgId;
var $msgTypes = array( 'help', 'info', 'warning', 'success', 'error' );
var $msgClass = 'messages';
var $msgWrapper = "<div class='%s %s'><a href='#' class='closeMessage'></a>\n%s</div>\n";
var $msgBefore = '<p>';
var $msgAfter = "</p>\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
<?php
//--------------------------------------------------------------------------------------------------
// Session-Based Flash Messages v1.0
// Copyright 2012 Mike Everhart (http://mikeeverhart.net)
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
//------------------------------------------------------------------------------
// Description:
//------------------------------------------------------------------------------
//
// Stores messages in Session data to be easily retrieved later on.
// This class includes four different types of messages:
// - Success
// - Error
// - Warning
// - Information
//
// See README for basic usage instructions, or see samples/index.php for more advanced samples
//
//--------------------------------------------------------------------------------------------------
// Changelog
//--------------------------------------------------------------------------------------------------
//
// 2011-05-15 - v1.0 - Initial Version
//
//--------------------------------------------------------------------------------------------------
class Messages {
//-----------------------------------------------------------------------------------------------
// Class Variables
//-----------------------------------------------------------------------------------------------
var $msgId;
var $msgTypes = array( 'help', 'info', 'warning', 'success', 'error' );
var $msgClass = 'messages';
var $msgWrapper = "<div class='%s %s'><a href='#' class='closeMessage'>X</a>\n%s</div>\n";
var $msgBefore = '<p>';
var $msgAfter = "</p>\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
?>

202
inc/3rdparty/paginator.php vendored Normal file
View File

@ -0,0 +1,202 @@
<?php
/*
* PHP Pagination Class
*
* @author David Carr - dave@daveismyname.com - http://www.daveismyname.com
* @version 1.0
* @date October 20, 2013
*/
class Paginator{
/**
* set the number of items per page.
*
* @var numeric
*/
private $_perPage;
/**
* set get parameter for fetching the page number
*
* @var string
*/
private $_instance;
/**
* sets the page number.
*
* @var numeric
*/
private $_page;
/**
* set the limit for the data source
*
* @var string
*/
private $_limit;
/**
* set the total number of records/items.
*
* @var numeric
*/
private $_totalRows = 0;
/**
* __construct
*
* pass values when class is istantiated
*
* @param numeric $_perPage sets the number of iteems per page
* @param numeric $_instance sets the instance for the GET parameter
*/
public function __construct($perPage,$instance){
$this->_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 .= "<div class='pagination'>";
if ($this->_page > 1)
$pagination.= "<a href='".$path."$this->_instance=$prev"."$ext'>« previous</a>";
else
$pagination.= "<span class='disabled'>« previous</span>";
if ($lastpage < 7 + ($adjacents * 2))
{
for ($counter = 1; $counter <= $lastpage; $counter++)
{
if ($counter == $this->_page)
$pagination.= "<span class='current'>$counter</span>";
else
$pagination.= "<a href='".$path."$this->_instance=$counter"."$ext'>$counter</a>";
}
}
elseif($lastpage > 5 + ($adjacents * 2))
{
if($this->_page < 1 + ($adjacents * 2))
{
for ($counter = 1; $counter < 4 + ($adjacents * 2); $counter++)
{
if ($counter == $this->_page)
$pagination.= "<span class='current'>$counter</span>";
else
$pagination.= "<a href='".$path."$this->_instance=$counter"."$ext'>$counter</a>";
}
$pagination.= "...";
$pagination.= "<a href='".$path."$this->_instance=$lpm1"."$ext'>$lpm1</a>";
$pagination.= "<a href='".$path."$this->_instance=$lastpage"."$ext'>$lastpage</a>";
}
elseif($lastpage - ($adjacents * 2) > $this->_page && $this->_page > ($adjacents * 2))
{
$pagination.= "<a href='".$path."$this->_instance=1"."$ext'>1</a>";
$pagination.= "<a href='".$path."$this->_instance=2"."$ext'>2</a>";
$pagination.= "...";
for ($counter = $this->_page - $adjacents; $counter <= $this->_page + $adjacents; $counter++)
{
if ($counter == $this->_page)
$pagination.= "<span class='current'>$counter</span>";
else
$pagination.= "<a href='".$path."$this->_instance=$counter"."$ext'>$counter</a>";
}
$pagination.= "..";
$pagination.= "<a href='".$path."$this->_instance=$lpm1"."$ext'>$lpm1</a>";
$pagination.= "<a href='".$path."$this->_instance=$lastpage"."$ext'>$lastpage</a>";
}
else
{
$pagination.= "<a href='".$path."$this->_instance=1"."$ext'>1</a>";
$pagination.= "<a href='".$path."$this->_instance=2"."$ext'>2</a>";
$pagination.= "..";
for ($counter = $lastpage - (2 + ($adjacents * 2)); $counter <= $lastpage; $counter++)
{
if ($counter == $this->_page)
$pagination.= "<span class='current'>$counter</span>";
else
$pagination.= "<a href='".$path."$this->_instance=$counter"."$ext'>$counter</a>";
}
}
}
if ($this->_page < $counter - 1)
$pagination.= "<a href='".$path."$this->_instance=$next"."$ext'>next »</a>";
else
$pagination.= "<span class='disabled'>next »</span>";
$pagination.= "</div>\n";
}
return $pagination;
}
}

View File

@ -1,265 +0,0 @@
<?php
/**
* poche, a read it later open source system
*
* @category poche
* @author Nicolas Lœuillet <support@inthepoche.com>
* @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'
=> '<strong>$1</strong>',
'/\[i\](.+?)\[\/i\]/is'
=> '<em>$1</em>',
'/\[s\](.+?)\[\/s\]/is'
=> '<del>$1</del>',
'/\[u\](.+?)\[\/u\]/is'
=> '<span style="text-decoration: underline;">$1</span>',
'/\[url\](.+?)\[\/url]/is'
=> '<a href="$1">$1</a>',
'/\[url=(\w+:\/\/[^\]]+)\](.+?)\[\/url]/is'
=> '<a href="$1">$2</a>',
'/\[quote\](.+?)\[\/quote\]/is'
=> '<blockquote>$1</blockquote>',
'/\[code\](.+?)\[\/code\]/is'
=> '<code>$1</code>',
'/\[([^[]+)\|([^[]+)\]/is'
=> '<a href="$2">$1</a>'
);
$text = preg_replace(
array_keys($replace),
array_values($replace),
$text
);
return $text;
}
public static function formatText($text)
{
$text = preg_replace_callback(
'/<code_html>(.*?)<\/code_html>/is',
create_function(
'$matches',
'return htmlspecialchars($matches[1]);'
),
$text
);
$text = preg_replace_callback(
'/<code_php>(.*?)<\/code_php>/is',
create_function(
'$matches',
'return highlight_string("<?php $matches[1] ?>", true);'
),
$text
);
$text = preg_replace('/<br \/>/is', '', $text);
$text = preg_replace(
'#(^|\s)([a-z]+://([^\s\w/]?[\w/])*)(\s|$)#im',
'\\1<a href="\\2">\\2</a>\\4',
$text
);
$text = preg_replace(
'#(^|\s)wp:?([a-z]{2}|):([\w]+)#im',
'\\1<a href="http://\\2.wikipedia.org/wiki/\\3">\\3</a>',
$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
}
}

View File

@ -1,66 +0,0 @@
<?php
/**
* poche, a read it later open source system
*
* @category poche
* @author Nicolas Lœuillet <nicolas@loeuillet.org>
* @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);

View File

@ -1,398 +0,0 @@
<?php
/**
* poche, a read it later open source system
*
* @category poche
* @author Nicolas Lœuillet <support@inthepoche.com>
* @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('#<meta .*charset=.*>#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);
}

View File

@ -0,0 +1,216 @@
<?php
/**
* poche, a read it later open source system
*
* @category poche
* @author Nicolas Lœuillet <support@inthepoche.com>
* @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);
}
}

485
inc/poche/Poche.class.php Normal file
View File

@ -0,0 +1,485 @@
<?php
/**
* poche, a read it later open source system
*
* @category poche
* @author Nicolas Lœuillet <support@inthepoche.com>
* @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 <ol> 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 <ul> 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;
}
}

226
inc/poche/Tools.class.php Normal file
View File

@ -0,0 +1,226 @@
<?php
/**
* poche, a read it later open source system
*
* @category poche
* @author Nicolas Lœuillet <support@inthepoche.com>
* @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('#<meta .*charset=.*>#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<domain>[a-z0-9][a-z0-9\-]{1,63}\.[a-z\.]{2,6})$/i', $domain, $regs)) {
return $regs['domain'];
}
return FALSE;
}
}

94
inc/poche/Url.class.php Normal file
View File

@ -0,0 +1,94 @@
<?php
/**
* poche, a read it later open source system
*
* @category poche
* @author Nicolas Lœuillet <support@inthepoche.com>
* @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;
}
}

50
inc/poche/User.class.php Normal file
View File

@ -0,0 +1,50 @@
<?php
/**
* poche, a read it later open source system
*
* @category poche
* @author Nicolas Lœuillet <support@inthepoche.com>
* @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;
}
}

61
inc/poche/config.inc.php Normal file
View File

@ -0,0 +1,61 @@
<?php
/**
* poche, a read it later open source system
*
* @category poche
* @author Nicolas Lœuillet <nicolas@loeuillet.org>
* @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']);
// }

110
inc/poche/pochePictures.php Normal file
View File

@ -0,0 +1,110 @@
<?php
/**
* poche, a read it later open source system
*
* @category poche
* @author Nicolas Lœuillet <support@inthepoche.com>
* @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);
}
}

File diff suppressed because it is too large Load Diff

View File

@ -1,51 +0,0 @@
<?php
/**
* poche, a read it later open source system
*
* @category poche
* @author Nicolas Lœuillet <support@inthepoche.com>
* @copyright 2013
* @license http://www.wtfpl.net/ see COPYING file
*/
class File extends Store {
function __construct() {
}
public function add() {
}
public function retrieveOneById($id) {
}
public function retrieveOneByURL($url) {
}
public function deleteById($id) {
}
public function favoriteById($id) {
}
public function archiveById($id) {
}
public function getEntriesByView($view) {
}
public function getLastId() {
}
public function updateContentById($id) {
}
}

View File

@ -1,202 +0,0 @@
<?php
/**
* poche, a read it later open source system
*
* @category poche
* @author Nicolas Lœuillet <support@inthepoche.com>
* @copyright 2013
* @license http://www.wtfpl.net/ see COPYING file
*/
class Sqlite extends Store {
public static $db_path = 'sqlite:./db/poche.sqlite';
var $handle;
function __construct() {
parent::__construct();
$this->handle = new PDO(self::$db_path);
$this->handle->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
}
private function getHandle() {
return $this->handle;
}
public function isInstalled() {
$sql = "SELECT name FROM sqlite_sequence WHERE name=?";
$query = $this->executeQuery($sql, array('config'));
$hasConfig = $query->fetchAll();
if (count($hasConfig) == 0)
return FALSE;
if (!$this->getLogin() || !$this->getPassword())
return FALSE;
return TRUE;
}
public function install($login, $password) {
$this->getHandle()->exec('CREATE TABLE IF NOT EXISTS "config" ("id" INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL UNIQUE , "name" VARCHAR UNIQUE, "value" BLOB)');
$this->handle->exec('CREATE TABLE IF NOT EXISTS "entries" ("id" INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL UNIQUE , "title" VARCHAR, "url" VARCHAR UNIQUE , "is_read" INTEGER DEFAULT 0, "is_fav" INTEGER DEFAULT 0, "content" BLOB)');
if (!$this->getLogin()) {
$sql_login = 'INSERT INTO config ( name, value ) VALUES (?, ?)';
$params_login = array('login', $login);
$query = $this->executeQuery($sql_login, $params_login);
}
if (!$this->getPassword()) {
$sql_pass = 'INSERT INTO config ( name, value ) VALUES (?, ?)';
$params_pass = array('password', $password);
$query = $this->executeQuery($sql_pass, $params_pass);
}
return TRUE;
}
public function getLogin() {
$sql = "SELECT value FROM config WHERE name=?";
$query = $this->executeQuery($sql, array('login'));
$login = $query->fetchAll();
return isset($login[0]['value']) ? $login[0]['value'] : FALSE;
}
public function getPassword() {
$sql = "SELECT value FROM config WHERE name=?";
$query = $this->executeQuery($sql, array('password'));
$pass = $query->fetchAll();
return isset($pass[0]['value']) ? $pass[0]['value'] : FALSE;
}
public function updatePassword($password)
{
$sql_update = "UPDATE config SET value=? WHERE name='password'";
$params_update = array($password);
$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)
{
logm('execute query error : '.$e->getMessage());
}
}
public function retrieveAll() {
$sql = "SELECT * FROM entries ORDER BY id";
$query = $this->executeQuery($sql, array());
$entries = $query->fetchAll();
return $entries;
}
public function retrieveOneById($id) {
parent::__construct();
$entry = NULL;
$sql = "SELECT * FROM entries WHERE id=?";
$params = array(intval($id));
$query = $this->executeQuery($sql, $params);
$entry = $query->fetchAll();
return $entry[0];
}
public function getEntriesByView($view) {
parent::__construct();
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 is_read=? " . $order;
$params = array(-1);
break;
case 'fav' :
$sql = "SELECT * FROM entries WHERE is_fav=? " . $order;
$params = array(-1);
break;
default:
$sql = "SELECT * FROM entries WHERE is_read=? " . $order;
$params = array(0);
break;
}
$query = $this->executeQuery($sql, $params);
$entries = $query->fetchAll();
return $entries;
}
public function add($url, $title, $content) {
parent::__construct();
$sql_action = 'INSERT INTO entries ( url, title, content ) VALUES (?, ?, ?)';
$params_action = array($url, $title, $content);
$query = $this->executeQuery($sql_action, $params_action);
return $query;
}
public function deleteById($id) {
parent::__construct();
$sql_action = "DELETE FROM entries WHERE id=?";
$params_action = array($id);
$query = $this->executeQuery($sql_action, $params_action);
return $query;
}
public function favoriteById($id) {
parent::__construct();
$sql_action = "UPDATE entries SET is_fav=~is_fav WHERE id=?";
$params_action = array($id);
$query = $this->executeQuery($sql_action, $params_action);
}
public function archiveById($id) {
parent::__construct();
$sql_action = "UPDATE entries SET is_read=~is_read WHERE id=?";
$params_action = array($id);
$query = $this->executeQuery($sql_action, $params_action);
}
public function getLastId() {
parent::__construct();
return $this->getHandle()->lastInsertId();
}
public function updateContentById($id) {
parent::__construct();
$sql_update = "UPDATE entries SET content=? WHERE id=?";
$params_update = array($content, $id);
$query = $this->executeQuery($sql_update, $params_update);
}
}

View File

@ -1,63 +0,0 @@
<?php
/**
* poche, a read it later open source system
*
* @category poche
* @author Nicolas Lœuillet <support@inthepoche.com>
* @copyright 2013
* @license http://www.wtfpl.net/ see COPYING file
*/
class Store {
function __construct() {
}
public function getLogin() {
}
public function getPassword() {
}
public function add() {
}
public function retrieveAll() {
}
public function retrieveOneById($id) {
}
public function retrieveOneByURL($url) {
}
public function deleteById($id) {
}
public function favoriteById($id) {
}
public function archiveById($id) {
}
public function getEntriesByView($view) {
}
public function getLastId() {
}
public function updateContentById($id) {
}
}

108
index.php
View File

@ -8,83 +8,57 @@
* @license http://www.wtfpl.net/ see COPYING file
*/
include dirname(__FILE__).'/inc/config.php';
include dirname(__FILE__).'/inc/poche/config.inc.php';
myTool::initPhp();
# XSRF protection with token
if (!empty($_POST)) {
if (!Session::isToken($_POST['token'])) {
die('Wrong token.');
}
unset($_SESSION['tokens']);
}
$ref = empty($_SERVER['HTTP_REFERER']) ? '' : $_SERVER['HTTP_REFERER'];
# Parse GET & REFERER vars
$referer = empty($_SERVER['HTTP_REFERER']) ? '' : $_SERVER['HTTP_REFERER'];
$view = Tools::checkVar('view', 'home');
$action = Tools::checkVar('action');
$id = Tools::checkVar('id');
$_SESSION['sort'] = Tools::checkVar('sort', 'id');
$url = new Url((isset ($_GET['url'])) ? $_GET['url'] : '');
# poche actions
if (isset($_GET['login'])) {
// Login
if (!empty($_POST['login']) && !empty($_POST['password'])) {
if (Session::login($_SESSION['login'], $_SESSION['pass'], $_POST['login'], encode_string($_POST['password'] . $_POST['login']))) {
logm('login successful');
$msg->add('s', 'welcome in 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); // when browser closes
}
session_regenerate_id(true);
MyTool::redirect($ref);
}
logm('login failed');
die("Login failed !");
} else {
logm('login failed');
}
# hello you
$poche->login($referer);
}
elseif (isset($_GET['logout'])) {
logm('logout');
Session::logout();
MyTool::redirect();
# see you soon !
$poche->logout();
}
elseif (isset($_GET['config'])) {
if (isset($_POST['password']) && isset($_POST['password_repeat'])) {
if ($_POST['password'] == $_POST['password_repeat'] && $_POST['password'] != "") {
logm('password updated');
if (!DEMO) {
$store->updatePassword(encode_string($_POST['password'] . $_SESSION['login']));
$msg->add('s', 'your password has been updated');
}
else {
$msg->add('i', 'in demo mode, you can\'t update password');
}
}
else
$msg->add('e', 'your password can\'t be empty and you have to repeat it in the second field');
}
elseif (isset($_GET['config'])) {
# Update password
$poche->updatePassword();
}
elseif (isset($_GET['import'])) {
$import = $poche->import($_GET['from']);
}
elseif (isset($_GET['export'])) {
$poche->export();
}
# Traitement des paramètres et déclenchement des actions
$view = (isset ($_REQUEST['view'])) ? htmlentities($_REQUEST['view']) : 'index';
$full_head = (isset ($_REQUEST['full_head'])) ? htmlentities($_REQUEST['full_head']) : 'yes';
$action = (isset ($_REQUEST['action'])) ? htmlentities($_REQUEST['action']) : '';
$_SESSION['sort'] = (isset ($_REQUEST['sort'])) ? htmlentities($_REQUEST['sort']) : 'id';
$id = (isset ($_REQUEST['id'])) ? htmlspecialchars($_REQUEST['id']) : '';
$url = (isset ($_GET['url'])) ? $_GET['url'] : '';
$tpl->assign('isLogged', Session::isLogged());
$tpl->assign('referer', $ref);
$tpl->assign('view', $view);
$tpl->assign('poche_url', myTool::getUrl());
$tpl->assign('title', 'poche, a read it later open source system');
# vars to send to templates
$tpl_vars = array(
'referer' => $referer,
'view' => $view,
'poche_url' => Tools::getPocheUrl(),
'title' => _('poche, a read it later open source system'),
'token' => Session::getToken(),
);
if (Session::isLogged()) {
action_to_do($action, $url, $id);
display_view($view, $id, $full_head);
$poche->action($action, $url, $id);
$tpl_file = Tools::getTplFile($view);
$tpl_vars = array_merge($tpl_vars, $poche->displayView($view, $id));
}
else {
$tpl->draw('login');
$tpl_file = 'login.twig';
}
# because messages can be added in $poche->action(), we have to add this entry now (we can add it before)
$messages = $poche->messages->display('all', FALSE);
$tpl_vars = array_merge($tpl_vars, array('messages' => $messages));
# display poche
echo $poche->tpl->render($tpl_file, $tpl_vars);

34
install/mysql.sql Normal file
View File

@ -0,0 +1,34 @@
CREATE TABLE IF NOT EXISTS `config` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`name` varchar(255) NOT NULL,
`value` varchar(255) NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
CREATE TABLE IF NOT EXISTS `entries` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`title` varchar(255) NOT NULL,
`url` varchar(255) NOT NULL,
`is_read` tinyint(1) NOT NULL,
`is_fav` tinyint(1) NOT NULL,
`content` blob NOT NULL,
`user_id` int(11) NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
CREATE TABLE IF NOT EXISTS `users` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`username` varchar(255) NOT NULL,
`password` varchar(255) NOT NULL,
`name` int(255) NOT NULL,
`email` varchar(255) NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
CREATE TABLE IF NOT EXISTS `users_config` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`user_id` int(11) NOT NULL,
`name` varchar(255) NOT NULL,
`value` varchar(255) NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1;

30
install/postgres.sql Normal file
View File

@ -0,0 +1,30 @@
CREATE TABLE config (
id bigserial primary key,
name varchar(255) NOT NULL,
value varchar(255) NOT NULL
);
CREATE TABLE entries (
id bigserial primary key,
title varchar(255) NOT NULL,
url varchar(255) NOT NULL,
is_read boolean DEFAULT false,
is_fav boolean DEFAULT false,
content TEXT,
user_id integer NOT NULL
);
CREATE TABLE users (
id bigserial primary key,
username varchar(255) NOT NULL,
password varchar(255) NOT NULL,
name varchar(255) NOT NULL,
email varchar(255) NOT NULL
);
CREATE TABLE users_config (
id bigserial primary key,
user_id integer NOT NULL,
name varchar(255) NOT NULL,
value varchar(255) NOT NULL
);

View File

@ -0,0 +1,72 @@
<?php
# import script to upgrade from poche 0.3
$db_path = 'sqlite:../db/poche.sqlite';
$handle = new PDO($db_path);
$handle->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
# Requêtes à exécuter pour mettre à jour poche.sqlite en 1.x
# ajout d'un champ user_id sur la table entries
$sql = 'ALTER TABLE entries RENAME TO tempEntries;';
$query = $handle->prepare($sql);
$query->execute();
$sql = 'CREATE TABLE entries (id INTEGER PRIMARY KEY, title TEXT, url TEXT, is_read NUMERIC DEFAULT 0, is_fav NUMERIC DEFAULT 0, content BLOB, user_id NUMERIC);';
$query = $handle->prepare($sql);
$query->execute();
$sql = 'INSERT INTO entries (id, title, url, is_read, is_fav, content) SELECT id, title, url, is_read, is_fav, content FROM tempEntries;';
$query = $handle->prepare($sql);
$query->execute();
# Update tout pour mettre user_id = 1
$sql = 'UPDATE entries SET user_id = 1;';
$query = $handle->prepare($sql);
$query->execute();
# Changement des flags pour les lus / favoris
$sql = 'UPDATE entries SET is_read = 1 WHERE is_read = -1;';
$query = $handle->prepare($sql);
$query->execute();
$sql = 'UPDATE entries SET is_fav = 1 WHERE is_fav = -1;';
$query = $handle->prepare($sql);
$query->execute();
# Création de la table users
$sql = 'CREATE TABLE users (id INTEGER PRIMARY KEY, username TEXT, password TEXT, name TEXT, email TEXT);';
$query = $handle->prepare($sql);
$query->execute();
$sql = 'INSERT INTO users (username) SELECT value FROM config WHERE name = "login";';
$query = $handle->prepare($sql);
$query->execute();
$sql = "UPDATE users SET password = (SELECT value FROM config WHERE name = 'password')";
$query = $handle->prepare($sql);
$query->execute();
# Création de la table users_config
$sql = 'CREATE TABLE users_config (id INTEGER PRIMARY KEY, user_id NUMERIC, name TEXT, value TEXT);';
$query = $handle->prepare($sql);
$query->execute();
$sql = 'INSERT INTO users_config (user_id, name, value) VALUES (1, "pager", "10");';
$query = $handle->prepare($sql);
$query->execute();
$sql = 'INSERT INTO users_config (user_id, name, value) VALUES (1, "language", "en_EN.UTF8");';
$query = $handle->prepare($sql);
$query->execute();
# Suppression de la table temporaire
$sql = 'DROP TABLE tempEntries;';
$query = $handle->prepare($sql);
$query->execute();
# Vidage de la table de config
$sql = 'DELETE FROM config;';
$query = $handle->prepare($sql);
$query->execute();
echo 'welcome to poche 1.0 !';

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -1,57 +0,0 @@
function toggle_favorite(element, id) {
$(element).toggleClass('fav-off');
$.ajax ({
url: "index.php?action=toggle_fav",
data:{id:id}
});
}
function toggle_archive(element, id, view_article) {
$(element).toggleClass('archive-off');
$.ajax ({
url: "index.php?action=toggle_archive",
data:{id:id}
});
var obj = $('#entry-'+id);
// on vient de la vue de l'article, donc pas de gestion de grille
if (view_article != 1) {
$('#content').masonry('remove',obj);
$('#content').masonry('reloadItems');
$('#content').masonry('reload');
}
}
function sort_links(view, sort) {
$.get('index.php', { view: view, sort: sort, full_head: 'no' }, function(data) {
$('#content').html(data);
});
}
// ---------- Swith light or dark view
function setActiveStyleSheet(title) {
var i, a, main;
for(i=0; (a = document.getElementsByTagName("link")[i]); i++) {
if(a.getAttribute("rel").indexOf("style") != -1 && a.getAttribute("title")) {
a.disabled = true;
if(a.getAttribute("title") == title) a.disabled = false;
}
}
}
$('#themeswitch').click(function() {
// we want the dark
if ($('body').hasClass('light-style')) {
setActiveStyleSheet('dark-style');
$('body').addClass('dark-style');
$('body').removeClass('light-style');
$('#themeswitch').text('light');
// we want the light
} else if ($('body').hasClass('dark-style')) {
setActiveStyleSheet('light-style');
$('body').addClass('light-style');
$('body').removeClass('dark-style');
$('#themeswitch').text('dark');
}
return false;
});

Binary file not shown.

View File

@ -0,0 +1,376 @@
msgid ""
msgstr ""
"Project-Id-Version: poche\n"
"POT-Creation-Date: 2013-08-06 08:35+0100\n"
"PO-Revision-Date: 2013-08-06 08:35+0100\n"
"Last-Translator: Nicolas Lœuillet <nicolas.loeuillet@gmail.com>\n"
"Language-Team: poche <support@inthepoche.com>\n"
"Language: Français\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"X-Generator: Poedit 1.5.4\n"
"X-Poedit-KeywordsList: _;gettext;gettext_noop\n"
"X-Poedit-Basepath: /\n"
"Plural-Forms: nplurals=2; plural=(n > 1);\n"
"X-Poedit-SourceCharset: UTF-8\n"
"X-Poedit-SearchPath-0: /var/www/poche-i18n\n"
#: /var/www/poche-i18n/index.php:43
msgid "poche, a read it later open source system"
msgstr "poche, a read it later open source system"
#: /var/www/poche-i18n/inc/poche/Poche.class.php:101
msgid "the link has been added successfully"
msgstr "le lien a été ajouté avec succès"
#: /var/www/poche-i18n/inc/poche/Poche.class.php:104
msgid "error during insertion : the link wasn't added"
msgstr "erreur durant l'insertion : le lien n'a pas été ajouté"
#: /var/www/poche-i18n/inc/poche/Poche.class.php:109
msgid "error during fetching content : the link wasn't added"
msgstr "erreur durant la récupération du contenu : le lien n'a pas été ajouté"
#: /var/www/poche-i18n/inc/poche/Poche.class.php:119
msgid "the link has been deleted successfully"
msgstr "le lien a été supprimé avec succès"
#: /var/www/poche-i18n/inc/poche/Poche.class.php:123
msgid "the link wasn't deleted"
msgstr "le lien n'a pas été supprimé"
#: /var/www/poche-i18n/inc/poche/Tools.class.php:18
msgid "Oops, it seems you don't have PHP 5."
msgstr "Oups, il semblerait que PHP 5 ne soit pas installé. "
#: /var/www/poche-i18n/cache/c9/b0/845a8dc93165e6c00b6b43068799.php:32
#: /var/www/poche-i18n/cache/c9/b0/845a8dc93165e6c00b6b43068799.php:70
#: /var/www/poche-i18n/cache/76/a4/e7c21f2e0ba29104fc654cd8ba41.php:50
msgid "config"
msgstr "config"
#: /var/www/poche-i18n/cache/c9/b0/845a8dc93165e6c00b6b43068799.php:46
#: /var/www/poche-i18n/cache/7a/1e/68e6b4aec1301ae024cc85232e7c.php:31
#: /var/www/poche-i18n/cache/76/a4/e7c21f2e0ba29104fc654cd8ba41.php:26
#: /var/www/poche-i18n/cache/dd/a8/530129765655dcde0a70a31a78b6.php:34
msgid "home"
msgstr "accueil"
#: /var/www/poche-i18n/cache/c9/b0/845a8dc93165e6c00b6b43068799.php:54
#: /var/www/poche-i18n/cache/76/a4/e7c21f2e0ba29104fc654cd8ba41.php:34
msgid "favorites"
msgstr "favoris"
#: /var/www/poche-i18n/cache/c9/b0/845a8dc93165e6c00b6b43068799.php:62
#: /var/www/poche-i18n/cache/76/a4/e7c21f2e0ba29104fc654cd8ba41.php:42
msgid "archive"
msgstr "archives"
#: /var/www/poche-i18n/cache/c9/b0/845a8dc93165e6c00b6b43068799.php:74
#: /var/www/poche-i18n/cache/c9/b0/845a8dc93165e6c00b6b43068799.php:76
#: /var/www/poche-i18n/cache/76/a4/e7c21f2e0ba29104fc654cd8ba41.php:54
#: /var/www/poche-i18n/cache/76/a4/e7c21f2e0ba29104fc654cd8ba41.php:56
msgid "logout"
msgstr "déconnexion"
#: /var/www/poche-i18n/cache/c9/b0/845a8dc93165e6c00b6b43068799.php:87
msgid "Bookmarklet"
msgstr "Bookmarklet"
#: /var/www/poche-i18n/cache/c9/b0/845a8dc93165e6c00b6b43068799.php:91
msgid ""
"Thanks to the bookmarklet, you will be able to easily add a link to your "
"poche."
msgstr ""
"Grâce au bookmarklet, vous pouvez ajouter facilement un lien dans votre "
"poche."
#: /var/www/poche-i18n/cache/c9/b0/845a8dc93165e6c00b6b43068799.php:93
msgid "Have a look to this documentation:"
msgstr "Jetez un œil à la documentation :"
#: /var/www/poche-i18n/cache/c9/b0/845a8dc93165e6c00b6b43068799.php:97
msgid "Drag & drop this link to your bookmarks bar and have fun with poche."
msgstr ""
"Glissez / déposez ce lien dans votre barre de favoris de votre navigateur et "
"prenez du bon temps avec poche."
#: /var/www/poche-i18n/cache/c9/b0/845a8dc93165e6c00b6b43068799.php:103
msgid "poche it!"
msgstr "poche-le !"
#: /var/www/poche-i18n/cache/c9/b0/845a8dc93165e6c00b6b43068799.php:108
msgid "Updating poche"
msgstr "Mettre à jour poche"
#: /var/www/poche-i18n/cache/c9/b0/845a8dc93165e6c00b6b43068799.php:113
msgid "your version"
msgstr "votre version"
#: /var/www/poche-i18n/cache/c9/b0/845a8dc93165e6c00b6b43068799.php:119
msgid "latest stable version"
msgstr "dernière version stable"
#: /var/www/poche-i18n/cache/c9/b0/845a8dc93165e6c00b6b43068799.php:125
msgid "a more recent stable version is available."
msgstr "une version stable plus récente est disponible."
#: /var/www/poche-i18n/cache/c9/b0/845a8dc93165e6c00b6b43068799.php:128
#: /var/www/poche-i18n/cache/c9/b0/845a8dc93165e6c00b6b43068799.php:142
msgid "you are up to date."
msgstr "vous êtes à jour."
#: /var/www/poche-i18n/cache/c9/b0/845a8dc93165e6c00b6b43068799.php:133
msgid "latest dev version"
msgstr "dernière version de développement"
#: /var/www/poche-i18n/cache/c9/b0/845a8dc93165e6c00b6b43068799.php:139
msgid "a more recent development version is available."
msgstr "une version de développement plus récente est disponible."
#: /var/www/poche-i18n/cache/c9/b0/845a8dc93165e6c00b6b43068799.php:150
msgid "Change your password"
msgstr "Modifier votre mot de passe"
#: /var/www/poche-i18n/cache/c9/b0/845a8dc93165e6c00b6b43068799.php:157
msgid "New password:"
msgstr "Nouveau mot de passe :"
#: /var/www/poche-i18n/cache/c9/b0/845a8dc93165e6c00b6b43068799.php:161
#: /var/www/poche-i18n/cache/c9/b0/845a8dc93165e6c00b6b43068799.php:171
#: /var/www/poche-i18n/cache/d4/28/e0d08991ec2d8a7b133505e7c651.php:60
#: /var/www/poche-i18n/cache/ae/26/05eb67771213c16bd8c9aaf2d2c4.php:68
msgid "Password"
msgstr "Mot de passe"
#: /var/www/poche-i18n/cache/c9/b0/845a8dc93165e6c00b6b43068799.php:167
msgid "Repeat your new password:"
msgstr "Répétez le nouveau mot de passe :"
#: /var/www/poche-i18n/cache/c9/b0/845a8dc93165e6c00b6b43068799.php:177
msgid "Update"
msgstr "Mettre à jour"
#: /var/www/poche-i18n/cache/c9/b0/845a8dc93165e6c00b6b43068799.php:193
msgid "Import"
msgstr "Import"
#: /var/www/poche-i18n/cache/c9/b0/845a8dc93165e6c00b6b43068799.php:197
msgid "Please execute the import script locally, it can take a very long time."
msgstr "Merci d'exécuter l'import en local, cela peut prendre du temps. "
#: /var/www/poche-i18n/cache/c9/b0/845a8dc93165e6c00b6b43068799.php:201
msgid "More infos in the official doc:"
msgstr "Plus d'infos sur la documentation officielle :"
#: /var/www/poche-i18n/cache/c9/b0/845a8dc93165e6c00b6b43068799.php:206
msgid "import from Pocket"
msgstr "l'import depuis Pocket est terminé."
#: /var/www/poche-i18n/cache/c9/b0/845a8dc93165e6c00b6b43068799.php:210
msgid "import from Readability"
msgstr "l'import depuis Readability est terminé."
#: /var/www/poche-i18n/cache/c9/b0/845a8dc93165e6c00b6b43068799.php:214
msgid "import from Instapaper"
msgstr "Import depuis Instapaper"
#: /var/www/poche-i18n/cache/c9/b0/845a8dc93165e6c00b6b43068799.php:220
msgid "Export your poche datas"
msgstr "Exporter vos données de poche"
#: /var/www/poche-i18n/cache/c9/b0/845a8dc93165e6c00b6b43068799.php:224
msgid "Click here"
msgstr "Cliquez-ici"
#: /var/www/poche-i18n/cache/c9/b0/845a8dc93165e6c00b6b43068799.php:226
msgid "to export your poche datas."
msgstr "pour exporter vos données de poche."
#: /var/www/poche-i18n/cache/7a/1e/68e6b4aec1301ae024cc85232e7c.php:46
#: /var/www/poche-i18n/cache/7a/1e/68e6b4aec1301ae024cc85232e7c.php:139
#: /var/www/poche-i18n/cache/30/97/b548692380c89d047a16cec7af79.php:22
msgid "back to home"
msgstr "retour à l'accueil"
#: /var/www/poche-i18n/cache/7a/1e/68e6b4aec1301ae024cc85232e7c.php:50
#: /var/www/poche-i18n/cache/7a/1e/68e6b4aec1301ae024cc85232e7c.php:147
#: /var/www/poche-i18n/cache/dd/a8/530129765655dcde0a70a31a78b6.php:119
msgid "toggle mark as read"
msgstr "marquer comme lu"
#: /var/www/poche-i18n/cache/7a/1e/68e6b4aec1301ae024cc85232e7c.php:60
#: /var/www/poche-i18n/cache/7a/1e/68e6b4aec1301ae024cc85232e7c.php:157
#: /var/www/poche-i18n/cache/dd/a8/530129765655dcde0a70a31a78b6.php:129
msgid "toggle favorite"
msgstr "favori"
#: /var/www/poche-i18n/cache/7a/1e/68e6b4aec1301ae024cc85232e7c.php:70
#: /var/www/poche-i18n/cache/7a/1e/68e6b4aec1301ae024cc85232e7c.php:167
#: /var/www/poche-i18n/cache/dd/a8/530129765655dcde0a70a31a78b6.php:139
msgid "delete"
msgstr "supprimer"
#: /var/www/poche-i18n/cache/7a/1e/68e6b4aec1301ae024cc85232e7c.php:82
#: /var/www/poche-i18n/cache/7a/1e/68e6b4aec1301ae024cc85232e7c.php:179
msgid "tweet"
msgstr "tweeter"
#: /var/www/poche-i18n/cache/7a/1e/68e6b4aec1301ae024cc85232e7c.php:93
#: /var/www/poche-i18n/cache/7a/1e/68e6b4aec1301ae024cc85232e7c.php:190
msgid "email"
msgstr "envoyer par email"
#: /var/www/poche-i18n/cache/7a/1e/68e6b4aec1301ae024cc85232e7c.php:109
#: /var/www/poche-i18n/cache/7a/1e/68e6b4aec1301ae024cc85232e7c.php:125
#: /var/www/poche-i18n/cache/dd/a8/530129765655dcde0a70a31a78b6.php:153
msgid "original"
msgstr "original"
#: /var/www/poche-i18n/cache/7a/1e/68e6b4aec1301ae024cc85232e7c.php:143
msgid "back to top"
msgstr "retour en haut de page"
#: /var/www/poche-i18n/cache/7a/1e/68e6b4aec1301ae024cc85232e7c.php:198
msgid "this article appears wrong?"
msgstr "cet article s'affiche mal ?"
#: /var/www/poche-i18n/cache/7a/1e/68e6b4aec1301ae024cc85232e7c.php:200
msgid "create an issue"
msgstr "créer un ticket"
#: /var/www/poche-i18n/cache/7a/1e/68e6b4aec1301ae024cc85232e7c.php:202
msgid "or"
msgstr "ou"
#: /var/www/poche-i18n/cache/7a/1e/68e6b4aec1301ae024cc85232e7c.php:206
msgid "contact us by mail"
msgstr "contactez-nous par email"
#: /var/www/poche-i18n/cache/88/8a/ee3b7080c13204391c14947a0c2c.php:22
msgid "powered by"
msgstr "propulsé par"
#: /var/www/poche-i18n/cache/d4/28/e0d08991ec2d8a7b133505e7c651.php:31
msgid "installation"
msgstr "installation"
#: /var/www/poche-i18n/cache/d4/28/e0d08991ec2d8a7b133505e7c651.php:42
msgid "install your poche"
msgstr "installez votre poche"
#: /var/www/poche-i18n/cache/d4/28/e0d08991ec2d8a7b133505e7c651.php:47
msgid ""
"poche is still not installed. Please fill the below form to install it. "
"Don't hesitate to <a href='http://inthepoche.com/?pages/Documentation'>read "
"the documentation on poche website</a>."
msgstr ""
"poche n'est pas encore installé. Merci de remplir les champs ci-dessous pour "
"l'installer. N'hésitez pas à <a href='http://inthepoche.com/?pages/"
"Documentation'>lire la documentation sur le site de poche</a>."
#: /var/www/poche-i18n/cache/d4/28/e0d08991ec2d8a7b133505e7c651.php:53
#: /var/www/poche-i18n/cache/ae/26/05eb67771213c16bd8c9aaf2d2c4.php:55
msgid "Login"
msgstr "Nom d'utilisateur"
#: /var/www/poche-i18n/cache/d4/28/e0d08991ec2d8a7b133505e7c651.php:67
msgid "Repeat your password"
msgstr "Répétez votre mot de passe"
#: /var/www/poche-i18n/cache/d4/28/e0d08991ec2d8a7b133505e7c651.php:74
msgid "Install"
msgstr "Installer"
#: /var/www/poche-i18n/cache/ae/26/05eb67771213c16bd8c9aaf2d2c4.php:31
#: /var/www/poche-i18n/cache/ae/26/05eb67771213c16bd8c9aaf2d2c4.php:42
msgid "login to your poche"
msgstr "Se connecter à votre poche"
#: /var/www/poche-i18n/cache/ae/26/05eb67771213c16bd8c9aaf2d2c4.php:48
msgid "you are in demo mode, some features may be disabled."
msgstr ""
"vous êtes en mode démo, certaines fonctionnalités sont peut-être désactivées."
#: /var/www/poche-i18n/cache/ae/26/05eb67771213c16bd8c9aaf2d2c4.php:80
msgid "Stay signed in"
msgstr "rester connecté"
#: /var/www/poche-i18n/cache/ae/26/05eb67771213c16bd8c9aaf2d2c4.php:86
msgid "(Do not check on public computers)"
msgstr "(à ne pas cocher sur un ordinateur public)"
#: /var/www/poche-i18n/cache/ae/26/05eb67771213c16bd8c9aaf2d2c4.php:93
msgid "Sign in"
msgstr ""
#: /var/www/poche-i18n/cache/dd/a8/530129765655dcde0a70a31a78b6.php:55
#: /var/www/poche-i18n/cache/dd/a8/530129765655dcde0a70a31a78b6.php:57
msgid "by date asc"
msgstr "par date asc"
#: /var/www/poche-i18n/cache/dd/a8/530129765655dcde0a70a31a78b6.php:59
msgid "by date"
msgstr "par date"
#: /var/www/poche-i18n/cache/dd/a8/530129765655dcde0a70a31a78b6.php:65
#: /var/www/poche-i18n/cache/dd/a8/530129765655dcde0a70a31a78b6.php:67
msgid "by date desc"
msgstr "par date desc"
#: /var/www/poche-i18n/cache/dd/a8/530129765655dcde0a70a31a78b6.php:75
#: /var/www/poche-i18n/cache/dd/a8/530129765655dcde0a70a31a78b6.php:77
msgid "by title asc"
msgstr "par titre asc"
#: /var/www/poche-i18n/cache/dd/a8/530129765655dcde0a70a31a78b6.php:79
msgid "by title"
msgstr "par titre"
#: /var/www/poche-i18n/cache/dd/a8/530129765655dcde0a70a31a78b6.php:85
#: /var/www/poche-i18n/cache/dd/a8/530129765655dcde0a70a31a78b6.php:87
msgid "by title desc"
msgstr "par titre desc"
#~ msgid "Please choose between Pocket & Readabilty :"
#~ msgstr "Merci de choisir entre Pocket & Readability :"
#~ msgid "Bye bye Pocket, let's go !"
#~ msgstr "Bye bye Pocket, en route !"
#~ msgid "Bye bye Readability, let's go !"
#~ msgstr "Bye bye Readability, en route !"
#~ msgid "Welcome to poche !"
#~ msgstr "Bienvenue dans poche !"
#~ msgid "Error with the import."
#~ msgstr "Erreur durant l'import."
#~ msgid "Wrong token."
#~ msgstr "Mauvais jeton."
#~ msgid "Login failed !"
#~ msgstr "Connexion échouée."
#~ msgid "your password has been updated"
#~ msgstr "Votre mot de passe a été mis à jour. "
#~ msgid "in demo mode, you can't update password"
#~ msgstr "En mode démo, le mot de passe ne peut être modifié."
#~ msgid ""
#~ "your password can't be empty and you have to repeat it in the second field"
#~ msgstr ""
#~ "Votre mot de passe ne peut être vide et vous devez le répéter dans le "
#~ "second champ."
#~ msgid "error during url preparation : the link wasn't added"
#~ msgstr "erreur durant l'insertion : le lien n'a pas été ajouté"
#~ msgid "error during url preparation : the link is not valid"
#~ msgstr "erreur durant la préparation de l'URL : le lien n'est pas valide"
#~ msgid "TEST"
#~ msgstr "NICOLAS"

0
phpunit.xml.dist Normal file
View File

3
tpl/_bookmarklet.twig Normal file
View File

@ -0,0 +1,3 @@
<script type="text/javascript">
top["bookmarklet-url@inthepoche.com"]=""+"<!DOCTYPE html>"+"<html>"+"<head>"+"<title>poche it !</title>"+'<link rel="icon" href="{{poche_url}}tpl/img/favicon.ico" />'+"</head>"+"<body>"+"<script>"+"window.onload=function(){"+"window.setTimeout(function(){"+"history.back();"+"},250);"+"};"+"</scr"+"ipt>"+"</body>"+"</html>"
</script>

4
tpl/_footer.twig Normal file
View File

@ -0,0 +1,4 @@
<footer class="w600p center mt3 smaller txtright">
<p>{% trans "powered by" %} <a href="http://inthepoche.com">poche</a></p>
{% if constant('DEBUG_POCHE') == 1 %}<p><strong>{% trans "debug mode is on so cache is off." %} {% trans "your poche version:" %}{{constant('POCHE_VERSION')}}. {% trans "storage:" %} {{constant('STORAGE')}}</strong></p>{% endif %}
</footer>

9
tpl/_head.twig Normal file
View File

@ -0,0 +1,9 @@
<link rel="shortcut icon" type="image/x-icon" href="./tpl/img/favicon.ico" />
<link rel="apple-touch-icon-precomposed" sizes="144x144" href="./tpl/img/apple-touch-icon-144x144-precomposed.png">
<link rel="apple-touch-icon-precomposed" sizes="72x72" href="./tpl/img/apple-touch-icon-72x72-precomposed.png">
<link rel="apple-touch-icon-precomposed" href="./tpl/img/apple-touch-icon-precomposed.png">
<link rel="stylesheet" href="./tpl/css/knacss.css" media="all">
<link rel="stylesheet" href="./tpl/css/style.css" media="all">
<link rel="stylesheet" href="./tpl/css/style-{{ constant('THEME') }}.css" media="all" title="{{ constant('THEME') }} theme">
<link rel="stylesheet" href="./tpl/css/messages.css" media="all">
<link href='http://fonts.googleapis.com/css?family=Roboto' rel='stylesheet' type='text/css'>

7
tpl/_menu.twig Normal file
View File

@ -0,0 +1,7 @@
<ul id="links">
<li><a href="./" {% if view == 'home' %}class="current"{% endif %}>{% trans "home" %}</a></li>
<li><a href="./?view=fav" {% if view == 'fav' %}class="current"{% endif %}>{% trans "favorites" %}</a></li>
<li><a href="./?view=archive" {% if view == 'archive' %}class="current"{% endif %}>{% trans "archive" %}</a></li>
<li><a href="./?view=config" {% if view == 'config' %}class="current"{% endif %}>{% trans "config" %}</a></li>
<li><a href="./?logout" title="{% trans "logout" %}">{% trans "logout" %}</a></li>
</ul>

1
tpl/_messages.twig Normal file
View File

@ -0,0 +1 @@
{{ messages | raw }}

3
tpl/_top.twig Normal file
View File

@ -0,0 +1,3 @@
<header class="w600p center mbm">
<h1><a href="./" title="{% trans "back to home" %}" ><img src="./tpl/img/logo.png" alt="logo poche" /></a></h1>
</header>

View File

@ -1,27 +0,0 @@
<div id="content">
<h2>Bookmarklet</h2>
<p>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, <a href="http://support.mozilla.org/en-US/kb/bookmarklets-perform-common-web-page-tasks">have a look here</a>.</p>
<p>Drag & drop this link to your bookmarks bar and have fun with poche.</p>
<p><a style="cursor: move; border: 1px dashed grey; background: white;" title="i am a bookmarklet, use me !" href="javascript:(function(){var%20url%20=%20location.href%20||%20url;window.open('{$poche_url}?action=add&url='%20+%20encodeURIComponent(url),'_self');})();">poche it !</a></p>
<h2>Password</h2>
<form method="post" action="?config" name="loginform">
<fieldset class="w500p">
<div class="row">
<label class="col w150p" for="password">New password</label>
<input class="col" type="password" id="password" name="password" placeholder="Password" tabindex="2">
</div>
<div class="row">
<label class="col w150p" for="password_repeat">Repeat your new password</label>
<input class="col" type="password" id="password_repeat" name="password_repeat" placeholder="Password" tabindex="3">
</div>
<div class="row mts txtcenter">
<button class="bouton" type="submit" tabindex="4">Update</button>
</div>
</fieldset>
<input type="hidden" name="returnurl" value="<?php echo htmlspecialchars($referer);?>">
<input type="hidden" name="token" value="<?php echo Session::getToken(); ?>">
</form>
<h2>Export</h2>
<p><a href="?view=export" target="_blank">Click here</a> to export your poche datas.</p>
</div>

57
tpl/config.twig Normal file
View File

@ -0,0 +1,57 @@
{% extends "layout.twig" %}
{% block title %}{% trans "config" %}{% endblock %}
{% block menu %}
<ul id="links">
<li><a href="./" {% if view == 'home' %}class="current"{% endif %}>{% trans "home" %}</a></li>
<li><a href="./?view=fav" {% if view == 'fav' %}class="current"{% endif %}>{% trans "favorites" %}</a></li>
<li><a href="./?view=archive" {% if view == 'archive' %}class="current"{% endif %}>{% trans "archive" %}</a></li>
<li><a href="./?view=config" {% if view == 'config' %}class="current"{% endif %}>{% trans "config" %}</a></li>
<li><a href="./?logout" title="{% trans "logout" %}">{% trans "logout" %}</a></li>
</ul>
{% endblock %}
{% block content %}
<h2>{% trans "Bookmarklet" %}</h2>
<p>{% trans "Thanks to the bookmarklet, you will be able to easily add a link to your poche." %} {% trans "Have a look to this documentation:" %} <a href="http://inthepoche.com/?pages/Documentation">inthepoche.com</a>.</p>
<p>{% trans "Drag & drop this link to your bookmarks bar and have fun with poche." %}</p>
<p class="txtcenter"><a ondragend="this.click();" style="cursor: move; border: 1px dashed grey; background: white; padding: 5px;" title="i am a bookmarklet, use me !" href="javascript:if(top['bookmarklet-url@inthepoche.com']){top['bookmarklet-url@inthepoche.com'];}else{(function(){var%20url%20=%20location.href%20||%20url;window.open('{{ poche_url }}?action=add&url='%20+%20btoa(url),'_self');})();void(0);}">{% trans "poche it!" %}</a></p>
<h2>{% trans "Updating poche" %}</h2>
<p><ul>
<li>{% trans "your version" %} : <strong>{{ constant('POCHE_VERSION') }}</strong></li>
<li>{% trans "latest stable version" %} : {{ prod }}. {% if compare_prod == -1 %}<strong><a href="http://inthepoche.com/?pages/T%C3%A9l%C3%A9charger-poche">{% trans "a more recent stable version is available." %}</a></strong>{% else %}{% trans "you are up to date." %}{% endif %}</li>
<li>{% trans "latest dev version" %} : {{ dev }}. {% if compare_dev == -1 %}<strong><a href="http://inthepoche.com/?pages/T%C3%A9l%C3%A9charger-poche">{% trans "a more recent development version is available." %}</a></strong>{% else %}{% trans "you are up to date." %}{% endif %}</li>
</ul>
</p>
<h2>{% trans "Change your password" %}</h2>
<form method="post" action="?config" name="loginform">
<fieldset class="w500p">
<div class="row">
<label class="col w150p" for="password">{% trans "New password:" %}</label>
<input class="col" type="password" id="password" name="password" placeholder="{% trans "Password" %}" tabindex="2">
</div>
<div class="row">
<label class="col w150p" for="password_repeat">{% trans "Repeat your new password:" %}</label>
<input class="col" type="password" id="password_repeat" name="password_repeat" placeholder="{% trans "Password" %}" tabindex="3">
</div>
<div class="row mts txtcenter">
<button class="bouton" type="submit" tabindex="4">{% trans "Update" %}</button>
</div>
</fieldset>
<input type="hidden" name="returnurl" value="{{ referer }}">
<input type="hidden" name="token" value="{{ token }}">
</form>
<h2>{% trans "Import" %}</h2>
<p>{% trans "Please execute the import script locally, it can take a very long time." %}</p>
<p>{% trans "More infos in the official doc:" %} <a href="http://inthepoche.com/?pages/Documentation">inthepoche.com</a></p>
<p><ul>
<li><a href="./?import&from=pocket">{% trans "import from Pocket" %}</a> (you must have a "ril_export.html" file on your server)</li>
<li><a href="./?import&from=readability">{% trans "import from Readability" %}</a> (you must have a "readability" file on your server)</li>
<li><a href="./?import&from=instapaper">{% trans "import from Instapaper" %}</a> (you must have a "instapaper-export.html" file on your server)</li>
</ul></p>
<h2>{% trans "Export your poche datas" %}</h2>
<p><a href="./?export" target="_blank">{% trans "Click here" %}</a> {% trans "to export your poche datas." %}</p>
{% endblock %}

13
tpl/css/messages.css Executable file
View File

@ -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; }

53
tpl/css/style-light.css Normal file
View File

@ -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;
}

244
tpl/css/style.css Normal file
View File

@ -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;
}

View File

@ -1,18 +0,0 @@
<div id="content">
{loop="entries"}
<div id="entry-{$value.id}" class="entrie mb2">
<span class="content">
<h2 class="h6-like">
<a href="index.php?&view=view&id={$value.id}">{$value.title}</a>
</h2>
<div class="tools">
<ul>
<li><a title="toggle mark as read" class="tool archive {if="$value.is_read == '0'"}archive-off{/if}" onclick="toggle_archive(this, {$value.id})"><span></span></a></li>
<li><a title="toggle favorite" class="tool fav {if="$value.is_fav == '0'"}fav-off{/if}" onclick="toggle_favorite(this, {$value.id})"><span></span></a></li>
<li><form method="post" onsubmit="return confirm('Are you sure?')" style="display: inline;"><input type="hidden" name="token" id="token" value="<?php echo Session::getToken(); ?>" /><input type="hidden" id="action" name="action" value="delete" /><input type="hidden" id="view" name="view" value="{$view}" /><input type="hidden" id="id" name="id" value="{$value.id}" /><input type="submit" class="delete" title="toggle delete" /></form></li>
</ul>
</div>
</span>
</div>
{/loop}
</div>

View File

@ -1 +0,0 @@
export {$export}

1
tpl/export.twig Normal file
View File

@ -0,0 +1 @@
{{ export }}

View File

@ -1,7 +0,0 @@
</div>
<footer class="mr2 mt3 smaller">
<p>powered by <a href="http://inthepoche.com">poche</a><br />follow us on <a href="https://twitter.com/getpoche" title="follow us on twitter">twitter</a></p>
</footer>
</body>
</html>

View File

@ -1,22 +0,0 @@
<!DOCTYPE html>
<!--[if lte IE 6]> <html class="no-js ie6 ie67 ie678" lang="en"> <![endif]-->
<!--[if lte IE 7]> <html class="no-js ie7 ie67 ie678" lang="en"> <![endif]-->
<!--[if IE 8]> <html class="no-js ie8 ie678" lang="en"> <![endif]-->
<!--[if gt IE 8]><!--> <html class="no-js" lang="en"> <!--<![endif]-->
<html>
<head>
<meta name="viewport" content="width=device-width, minimum-scale=1.0, maximum-scale=1.0">
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=10">
<title>{$title}</title>
<link rel="shortcut icon" type="image/x-icon" href="./img/favicon.ico" />
<link rel="apple-touch-icon-precomposed" sizes="144x144" href="./img/apple-touch-icon-144x144-precomposed.png">
<link rel="apple-touch-icon-precomposed" sizes="72x72" href="./img/apple-touch-icon-72x72-precomposed.png">
<link rel="apple-touch-icon-precomposed" href="./img/apple-touch-icon-precomposed.png">
<link rel="stylesheet" href="./css/knacss.css" media="all">
<link rel="stylesheet" href="./css/style.css" media="all">
<!-- Light Theme -->
<link rel="stylesheet" href="./css/style-light.css" media="all" title="light-style">
<!-- Dark Theme -->
<link rel="alternate stylesheet" href="./css/style-dark.css" media="all" title="dark-style">
</head>

View File

@ -1,19 +0,0 @@
<body class="light-style">
<header>
<h1><a href="index.php"><img src="./img/logo.png" alt="logo poche" /></a>poche</h1>
</header>
<div id="main">
<ul id="links">
<li><a href="index.php" {if="$view == 'index'"}class="current"{/if}>home</a></li>
<li><a href="?view=fav" {if="$view == 'fav'"}class="current"{/if}>favorites</a></li>
<li><a href="?view=archive" {if="$view == 'archive'"}class="current"{/if}>archive</a></li>
<li><a href="?view=config" {if="$view == 'config'"}class="current"{/if}>config</a></li>
<li><a href="?logout" title="Logout">logout</a></li>
</ul>
{if condition="isset($entries)"}
<ul id="sort">
<li><img src="img/up.png" onclick="sort_links('{$view}', 'ia');" title="by date asc" /> by date <img src="img/down.png" onclick="sort_links('{$view}', 'id');" title="by date desc" /></li>
<li><img src="img/up.png" onclick="sort_links('{$view}', 'ta');" title="by title asc" /> by title <img src="img/down.png" onclick="sort_links('{$view}', 'td');" title="by title desc" /></li>
</ul>
{/if}
{include="messages"}

29
tpl/home.twig Normal file
View File

@ -0,0 +1,29 @@
{% extends "layout.twig" %}
{% block title %}{% trans "home" %}{% endblock %}
{% block menu %}
{% include '_menu.twig' %}
{% endblock %}
{% block precontent %}
<ul id="sort">
<li><a href="./?sort=ia&view={{ view }}"><img src="./tpl/img/{{ constant('THEME') }}/top.png" alt="{% trans "by date asc" %}" title="{% trans "by date asc" %}" /></a> {% trans "by date" %} <a href="./?sort=id&view={{ view }}"><img src="./tpl/img/{{ constant('THEME') }}/down.png" alt="{% trans "by date desc" %}" title="{% trans "by date desc" %}" /></a></li>
<li><a href="./?sort=ta&view={{ view }}"><img src="./tpl/img/{{ constant('THEME') }}/top.png" alt="{% trans "by title asc" %}" title="{% trans "by title asc" %}" /></a> {% trans "by title" %} <a href="./?sort=td&view={{ view }}"><img src="./tpl/img/{{ constant('THEME') }}/down.png" alt="{% trans "by title desc" %}" title="{% trans "by title desc" %}" /></a></li>
</ul>
{% endblock %}
{% block content %}
{{ page_links | raw }}
{% for entry in entries %}
<div id="entry-{{ entry.id|e }}" class="entrie">
<h2><a href="index.php?view=view&id={{ entry.id|e }}">{{ entry.title|e }}</a></h2>
<ul class="tools">
<li>
<a title="{% trans "toggle mark as read" %}" class="tool archive {% if entry.is_read == 0 %}archive-off{% endif %}" href="./?action=toggle_archive&id={{ entry.id|e }}"><span></span></a></li>
<li><a title="{% trans "toggle favorite" %}" class="tool fav {% if entry.is_fav == 0 %}fav-off{% endif %}" href="./?action=toggle_fav&id={{ entry.id|e }}"><span></span></a></li>
<li><a title="{% trans "delete" %}" class="tool delete" href="./?action=delete&id={{ entry.id|e }}"><span></span></a></li>
</li>
</ul>
<p>{{ entry.content|striptags|slice(0, 300) }}...</p>
<p class="vieworiginal txtright small"><a href="{{ entry.url|e }}" target="_blank" title="{% trans "original" %} : {{ entry.title|e }}">{{ entry.url | e | getDomain }}</a></p>
</div>
{% endfor %}
{{ page_links | raw }}
{% endblock %}

View File

Before

Width:  |  Height:  |  Size: 7.2 KiB

After

Width:  |  Height:  |  Size: 7.2 KiB

View File

Before

Width:  |  Height:  |  Size: 6.0 KiB

After

Width:  |  Height:  |  Size: 6.0 KiB

View File

Before

Width:  |  Height:  |  Size: 5.7 KiB

After

Width:  |  Height:  |  Size: 5.7 KiB

View File

Before

Width:  |  Height:  |  Size: 346 B

After

Width:  |  Height:  |  Size: 346 B

View File

Before

Width:  |  Height:  |  Size: 277 B

After

Width:  |  Height:  |  Size: 277 B

View File

Before

Width:  |  Height:  |  Size: 235 B

After

Width:  |  Height:  |  Size: 235 B

View File

Before

Width:  |  Height:  |  Size: 216 B

After

Width:  |  Height:  |  Size: 216 B

BIN
tpl/img/light/envelop.png Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 285 B

BIN
tpl/img/light/left.png Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 196 B

View File

Before

Width:  |  Height:  |  Size: 252 B

After

Width:  |  Height:  |  Size: 252 B

View File

Before

Width:  |  Height:  |  Size: 314 B

After

Width:  |  Height:  |  Size: 314 B

View File

Before

Width:  |  Height:  |  Size: 281 B

After

Width:  |  Height:  |  Size: 281 B

0
img/up.png → tpl/img/light/top.png Normal file → Executable file
View File

Before

Width:  |  Height:  |  Size: 212 B

After

Width:  |  Height:  |  Size: 212 B

BIN
tpl/img/light/twitter.png Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 297 B

BIN
tpl/img/logo.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 454 B

0
img/messages/close.png → tpl/img/messages/close.png Normal file → Executable file
View File

Before

Width:  |  Height:  |  Size: 662 B

After

Width:  |  Height:  |  Size: 662 B

0
img/messages/cross.png → tpl/img/messages/cross.png Normal file → Executable file
View File

Before

Width:  |  Height:  |  Size: 655 B

After

Width:  |  Height:  |  Size: 655 B

0
img/messages/help.png → tpl/img/messages/help.png Normal file → Executable file
View File

Before

Width:  |  Height:  |  Size: 786 B

After

Width:  |  Height:  |  Size: 786 B

0
img/messages/tick.png → tpl/img/messages/tick.png Normal file → Executable file
View File

Before

Width:  |  Height:  |  Size: 537 B

After

Width:  |  Height:  |  Size: 537 B

View File

Before

Width:  |  Height:  |  Size: 666 B

After

Width:  |  Height:  |  Size: 666 B

View File

@ -1,30 +0,0 @@
{include="head"}
<body class="light-style">
<header>
<h1><a href="index.php"><img src="./img/logo.png" alt="logo poche" /></a>poche</h1>
</header>
<div id="main">
<form method="post" action="?install" name="loginform">
<fieldset class="w500p center">
<h2 class="mbs txtcenter">install your poche</h2>
<div class="row">
<label class="col w150p" for="login">Login</label>
<input class="col" type="text" id="login" name="login" placeholder="Login" tabindex="1" autofocus />
</div>
<div class="row">
<label class="col w150p" for="password">Password</label>
<input class="col" type="password" id="password" name="password" placeholder="Password" tabindex="2">
</div>
<div class="row">
<label class="col w150p" for="password_repeat">Repeat your password</label>
<input class="col" type="password" id="password_repeat" name="password_repeat" placeholder="Password" tabindex="3">
</div>
<div class="row mts txtcenter">
<button class="bouton" type="submit" tabindex="4">Install</button>
</div>
</fieldset>
<input type="hidden" name="returnurl" value="<?php echo htmlspecialchars($referer);?>">
<input type="hidden" name="token" value="<?php echo Session::getToken(); ?>">
</form>
{include="footer"}

28
tpl/install.twig Normal file
View File

@ -0,0 +1,28 @@
{% extends "layout.twig" %}
{% block title %}{% trans "installation" %}{% endblock %}
{% block content %}
<form method="post" action="?install" name="loginform">
<fieldset class="w500p center">
<h2 class="mbs txtcenter">{% trans "install your poche" %}</h2>
<p>
{% trans "poche is still not installed. Please fill the below form to install it. Don't hesitate to <a href='http://inthepoche.com/?pages/Documentation'>read the documentation on poche website</a>." %}
</p>
<div class="row">
<label class="col w150p" for="login">{% trans "Login" %}</label>
<input class="col" type="text" id="login" name="login" placeholder="Login" tabindex="1" autofocus />
</div>
<div class="row">
<label class="col w150p" for="password">{% trans "Password" %}</label>
<input class="col" type="password" id="password" name="password" placeholder="Password" tabindex="2">
</div>
<div class="row">
<label class="col w150p" for="password_repeat">{% trans "Repeat your password" %}</label>
<input class="col" type="password" id="password_repeat" name="password_repeat" placeholder="Password" tabindex="3">
</div>
<div class="row mts txtcenter">
<button class="bouton" type="submit" tabindex="4">{% trans "Install" %}</button>
</div>
</fieldset>
<input type="hidden" name="token" value="{{ token }}">
</form>
{% endblock %}

View File

@ -1,22 +0,0 @@
<script type="text/javascript" src="js/jquery-1.9.1.min.js"></script>
<script type="text/javascript" src="js/poche.js"></script>
{if="$load_all_js == '1'"}
<script type="text/javascript" src="js/jquery.masonry.min.js"></script>
<script type="text/javascript">
$( window ).load( function()
{
var columns = 3,
setColumns = function() { columns = $( window ).width() > 640 ? 3 : $( window ).width() > 320 ? 2 : 1; };
setColumns();
$( window ).resize( setColumns );
$( '#content' ).masonry(
{
itemSelector: '.entrie',
columnWidth: function( containerWidth ) { return containerWidth / columns; }
});
});
</script>
{/if}

29
tpl/layout.twig Normal file
View File

@ -0,0 +1,29 @@
<!DOCTYPE html>
<!--[if lte IE 6]> <html class="no-js ie6 ie67 ie678" lang="en"> <![endif]-->
<!--[if lte IE 7]> <html class="no-js ie7 ie67 ie678" lang="en"> <![endif]-->
<!--[if IE 8]> <html class="no-js ie8 ie678" lang="en"> <![endif]-->
<!--[if gt IE 8]><!--> <html class="no-js" lang="en"> <!--<![endif]-->
<html>
<head>
<meta name="viewport" content="width=device-width, minimum-scale=1.0, maximum-scale=1.0">
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=10">
<title>{% block title %}{% endblock %} - poche</title>
{% include '_head.twig' %}
{% include '_bookmarklet.twig' %}
</head>
<body>
{% include '_top.twig' %}
<div id="main">
{% block menu %}{% endblock %}
{% block precontent %}{% endblock %}
{% block messages %}
{% include '_messages.twig' %}
{% endblock %}
<div id="content" class="w600p center">
{% block content %}{% endblock %}
</div>
</div>
{% include '_footer.twig' %}
</body>
</html>

View File

@ -1,33 +0,0 @@
{include="head"}
<body class="light-style">
<header>
<h1><a href="index.php"><img src="./img/logo.png" alt="logo poche" /></a>poche</h1>
</header>
<div id="main">
<form method="post" action="?login" name="loginform">
<fieldset class="w500p center">
<h2 class="mbs txtcenter">login to your poche</h2>
<div class="row">
<label class="col w150p" for="login">Login</label>
<input class="col" type="text" id="login" name="login" placeholder="Login" tabindex="1" autofocus />
</div>
<div class="row">
<label class="col w150p" for="password">Password</label>
<input class="col" type="password" id="password" name="password" placeholder="Password" tabindex="2">
</div>
<div class="row">
<label class="col w150p">Stay signed in</label>
<div class="col">
<input type="checkbox" name="longlastingsession" tabindex="3">
<small class="inbl">(Do not check on public computers)</small>
</div>
</div>
<div class="row mts txtcenter">
<button class="bouton" type="submit" tabindex="4">Sign in</button>
</div>
</fieldset>
<input type="hidden" name="returnurl" value="<?php echo htmlspecialchars($referer);?>">
<input type="hidden" name="token" value="<?php echo Session::getToken(); ?>">
</form>
{include="footer"}

32
tpl/login.twig Normal file
View File

@ -0,0 +1,32 @@
{% extends "layout.twig" %}
{% block title %}{% trans "login to your poche" %}{% endblock %}
{% block content %}
<form method="post" action="?login" name="loginform">
<fieldset class="w500p center">
<h2 class="mbs txtcenter">{% trans "login to your poche" %}</h2>
{% if constant('MODE_DEMO') == 1 %}<p>{% trans "you are in demo mode, some features may be disabled." %}</p>{% endif %}
<div class="row">
<label class="col w150p" for="login">{% trans "Login" %}</label>
<input class="col" type="text" id="login" name="login" placeholder="Login" tabindex="1" autofocus {% if constant('MODE_DEMO') == 1 %}value="poche"{% endif %} />
</div>
<div class="row">
<label class="col w150p" for="password">{% trans "Password" %}</label>
<input class="col" type="password" id="password" name="password" placeholder="Password" tabindex="2" {% if constant('MODE_DEMO') == 1 %}value="poche"{% endif %} />
</div>
<div class="row">
<label class="col w150p" for="longlastingsession">{% trans "Stay signed in" %}</label>
<div class="col">
<input type="checkbox" id="longlastingsession" name="longlastingsession" tabindex="3">
<small class="inbl">{% trans "(Do not check on public computers)" %}</small>
</div>
</div>
<div class="row mts txtcenter">
<button class="bouton" type="submit" tabindex="4">{% trans "Login" %}</button>
</div>
</fieldset>
<input type="hidden" name="returnurl" value="{{ referer }}">
<input type="hidden" name="token" value="{{ token }}">
</form>
{% endblock %}

View File

@ -1 +0,0 @@
<div id="messages"><?php echo $msg->display(); ?></div>

View File

@ -1,53 +0,0 @@
<!DOCTYPE html>
<!--[if lte IE 6]> <html class="no-js ie6 ie67 ie678" lang="en"> <![endif]-->
<!--[if lte IE 7]> <html class="no-js ie7 ie67 ie678" lang="en"> <![endif]-->
<!--[if IE 8]> <html class="no-js ie8 ie678" lang="en"> <![endif]-->
<!--[if gt IE 8]><!--> <html class="no-js" lang="en"> <!--<![endif]-->
<html>
<head>
<meta name="viewport" content="width=device-width, minimum-scale=1.0, maximum-scale=1.0">
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=10">
<title>{$title}</title>
<link rel="shortcut icon" type="image/x-icon" href="./img/favicon.ico" />
<link rel="apple-touch-icon-precomposed" sizes="144x144" href="./img/apple-touch-icon-144x144-precomposed.png">
<link rel="apple-touch-icon-precomposed" sizes="72x72" href="./img/apple-touch-icon-72x72-precomposed.png">
<link rel="apple-touch-icon-precomposed" href="./img/apple-touch-icon-precomposed.png">
<link rel="stylesheet" href="./css/knacss.css" media="all">
<link rel="stylesheet" href="./css/style.css" media="all">
<!-- Light Theme -->
<link rel="stylesheet" href="./css/style-light.css" media="all" title="light-style">
<!-- Dark Theme -->
<link rel="alternate stylesheet" href="./css/style-dark.css" media="all" title="dark-style">
</head>
<body class="article light-style">
<div id="article" class="w600p">
<div class="backhome">
<a href="index.php" title="back to home">&larr;</a>
</div>
<div class="tools">
<ul>
<li><a title="toggle mark as read" class="tool archive {if="$is_read == '0'"}archive-off{/if}" onclick="toggle_archive(this, {$id})"><span></span></a></li>
<li><a href="#" id="themeswitch">dark</a></li>
<li><a title="toggle favorite" class="tool fav {if="$is_fav == '0'"}fav-off{/if}" onclick="toggle_favorite(this, {$id})"><span></span></a></li>
<li><form method="post" onsubmit="return confirm('Are you sure?')" style="display: inline;" action="index.php"><input type="hidden" name="token" id="token" value="<?php echo Session::getToken(); ?>" /><input type="hidden" id="view" name="view" value="index" /><input type="hidden" id="action" name="action" value="delete" /><input type="hidden" id="id" name="id" value="{$id}" /><input type="submit" class="delete" title="toggle delete" /></form></li>
<li><a href="?logout" title="Logout">logout</a></li>
</ul>
</div>
<header class="mbm">
<h1><a href="{$url}">{$title}</a></h1>
<div class="vieworiginal txtright small"><a href="{$url}" target="_blank" title="original : {$title}">view original</a></div>
</header>
{include="messages"}
<article>
<div id="readityourselfcontent">
{$content}
</div>
</article>
<div class="vieworiginal txtright small"><a href="{$url}" target="_blank" title="original : {$title}">view original</a></div>
<div class="backhome">
<a href="index.php" title="back to home">&larr;</a>
</div>
{include="js"}
{include="footer"}

Some files were not shown because too many files have changed in this diff Show More