1
0
mirror of https://github.com/moparisthebest/wallabag synced 2024-11-10 11:25:04 -05:00

Merge pull request #104 from inthepoche/twig

Twig version on dev branch
This commit is contained in:
Nicolas Lœuillet 2013-08-07 10:41:26 -07:00
commit 01c0e050ad
98 changed files with 3496 additions and 3324 deletions

3
.gitignore vendored Normal file
View File

@ -0,0 +1,3 @@
vendor
composer.phar
db/poche.sqlite

11
CREDITS
View File

@ -1,17 +1,14 @@
poche is based on :
* ReadItYourself http://www.memiks.fr/readityourself/
* PHP Readability http://www.keyvan.net/2010/08/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
* RainTPL http://www.raintpl.com/
* 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 twig version](https://github.com/inthepoche/poche/archive/twig.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,11 @@ location ~ /(db) {
}
```
## Import from Pocket
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.
## Usage
See the documentation on our website : [inthepoche.com](http://inthepoche.com).
## 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.

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, .support 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, .support 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,223 +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, .support a{
text-decoration: none;
padding: 5px 10px;
}
#links a:hover, .backhome a:hover, .support a:hover{
-webkit-border-radius: 2px;
border-radius: 2px;
}
.support {
font-size: 14px;
}
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;
}
#main .entrie .url {
font-size: 13px;
}
/*** ***/
/*** 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,73 +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 /><br />' . _('Please choose between Pocket & Readabilty :') . '<br /><a href="import.php?start=pocket">' . _('Bye bye Pocket, let\'s go !') . '</a><br /><a href="import.php?start=readability">' . _('Bye bye Readability, let\'s go !') . '</a>';
}
else {
if ($_GET['start'] == 'pocket') {
$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');
}
else if ($_GET['start'] == 'readability') {
$str_data = file_get_contents("readability");
$data = json_decode($str_data,true);
foreach ($data as $key => $value) {
$url = '';
foreach ($value as $key2 => $value2) {
if ($key2 == 'article__url') {
$url = $value2;
}
}
if ($url != '')
action_to_do('add', $url);
}
echo _('Import from Readability completed.') . '<a href="index.php">' . _('Welcome to poche !') . '</a>';
logm('import from Readability completed');
}
else {
echo _('Error with the import.') . '<a href="index.php">' . _('Back to poche'). '</a>';
logm('error with the import');
}
}

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.

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

@ -44,7 +44,7 @@ class Messages {
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 $msgWrapper = "<div class='%s %s'><a href='#' class='closeMessage'>X</a>\n%s</div>\n";
var $msgBefore = '<p>';
var $msgAfter = "</p>\n";

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(_('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 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,73 +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.3');
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');
define ('LANG', 'fr_FR.UTF8');
putenv("LC_ALL=".LANG);
setlocale(LC_ALL, LANG);
bindtextdomain(LANG, "./locale");
textdomain(LANG);
$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,400 +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;
$url = base64_decode($url);
error_log(print_r($url, TRUE));
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);
}
}

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

@ -0,0 +1,429 @@
<?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()
{
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;
}
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');
}
}
}
}
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();
}
}
public function logout()
{
$this->user = array();
Session::logout();
$this->messages->add('s', _('see you soon!'));
Tools::logm('logout');
Tools::redirect();
}
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();
}
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();
}
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();
}
public function import($from)
{
if ($from == 'pocket') {
$this->importFromPocket();
}
else if ($from == 'readability') {
$this->importFromReadability();
}
else if ($from == 'instapaper') {
$this->importFromInstapaper();
}
}
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');
}
private function getPocheVersion($which = 'prod')
{
$cache_file = CACHE . '/' . $which;
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-beta');
define ('MODE_DEMO', FALSE);
define ('DEBUG_POCHE', FALSE);
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) {
}
}

101
index.php
View File

@ -8,85 +8,54 @@
* @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'];
$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'] : '');
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 (!MODE_DEMO) {
$store->updatePassword(encode_string($_POST['password'] . $_SESSION['login']));
$msg->add('s', _('your password has been updated'));
# Update password
$poche->updatePassword();
}
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['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('demo', MODE_DEMO);
$tpl->assign('title', _('poche, a read it later open source system'));
$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));
# Aaaaaaand action !
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,64 @@
<?php
$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();
# 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;
});

View File

@ -1,8 +1,8 @@
msgid ""
msgstr ""
"Project-Id-Version: poche\n"
"POT-Creation-Date: 2013-08-02 10:26+0100\n"
"PO-Revision-Date: 2013-08-02 10:26+0100\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"
@ -16,96 +16,361 @@ msgstr ""
"X-Poedit-SourceCharset: UTF-8\n"
"X-Poedit-SearchPath-0: /var/www/poche-i18n\n"
#: /var/www/poche-i18n/import.php:17
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/import.php:17
msgid "Please choose between Pocket & Readabilty :"
msgstr "Merci de choisir entre Pocket & Readability :"
#: /var/www/poche-i18n/import.php:17
msgid "Bye bye Pocket, let's go !"
msgstr "Bye bye Pocket, en route !"
#: /var/www/poche-i18n/import.php:17
msgid "Bye bye Readability, let's go !"
msgstr "Bye bye Readability, en route !"
#: /var/www/poche-i18n/import.php:48
msgid "Import from Pocket completed."
msgstr "L'import depuis Poche est terminé."
#: /var/www/poche-i18n/import.php:48 /var/www/poche-i18n/import.php:66
msgid "Welcome to poche !"
msgstr "Bienvenue dans poche !"
#: /var/www/poche-i18n/import.php:66
msgid "Import from Readability completed."
msgstr "L'import depuis Readability est terminé."
#: /var/www/poche-i18n/import.php:70
msgid "Error with the import."
msgstr "Erreur durant l'import."
#: /var/www/poche-i18n/import.php:70
msgid "Back to poche"
msgstr "Retour à poche"
#: /var/www/poche-i18n/index.php:18
msgid "Wrong token."
msgstr "Mauvais jeton."
#: /var/www/poche-i18n/index.php:43
msgid "Login failed !"
msgstr "Connexion échouée."
#: /var/www/poche-i18n/index.php:59
msgid "your password has been updated"
msgstr "Votre mot de passe a été mis à jour. "
#: /var/www/poche-i18n/index.php:62
msgid "in demo mode, you can't update password"
msgstr "En mode démo, le mot de passe ne peut être modifié."
#: /var/www/poche-i18n/index.php:66
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."
#: /var/www/poche-i18n/index.php:83
msgid "poche, a read it later open source system"
msgstr "poche, a read it later open source system"
#: /var/www/poche-i18n/inc/MyTool.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/inc/functions.php:352
#: /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/functions.php:355
#: /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/functions.php:359
msgid "error during url preparation : 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/functions.php:364
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"
#: /var/www/poche-i18n/inc/functions.php:373
#: /var/www/poche-i18n/inc/poche/Poche.class.php:119
msgid "the link has been deleted successfully"
msgstr "le lien a été ajouté avec succès"
msgstr "le lien a été supprimé avec succès"
#: /var/www/poche-i18n/inc/functions.php:377
#: /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"

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 ondragend="this.click();" style="cursor: move; border: 1px dashed grey; background: white;" 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);}">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,21 +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>
<div class="url">{$value.url}</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,42 +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">
<script>
top["bookmarklet-url@inthepoche.com"] = ''
+'<!DOCTYPE html>'
+'<html>'
+'<head>'
+'<title>poche it !</title>'
+'<link rel="icon" href="{$poche_url}img/favicon.ico" />'
+'</head>'
+'<body>'
+'<script>'
+'window.onload=function(){'
+'window.setTimeout(function(){'
+'history.back();'
+'},250);'
+'};'
+'</scr'+'ipt>'
+'</body>'
+'</html>'
;
</script>
</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 {if="$demo == '1'"}value="poche"{/if} />
</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" {if="$demo == '1'"}value="poche"{/if} />
</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,58 +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>
<a href="#" title="back to top">&uarr;</a>
</div>
<div class="support">
this article appears wrong? <a href="https://github.com/inthepoche/poche/issues/new">create an issue</a> or <a href="mailto:support@inthepoche.com">contact us by mail</a>
</div>
{include="js"}
{include="footer"}

40
tpl/view.twig Normal file
View File

@ -0,0 +1,40 @@
{% extends "layout.twig" %}
{% block title %}{% trans "home" %}{% endblock %}
{% block content %}
<div id="article">
<div class="tools">
<ul class="tools">
<li>
<li><a href="{{ referer }}" title="{% trans "back to home" %}" class="tool back"><span></span></a></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>
{% if constant('SHARE_TWITTER') == 1 %}<li><a href="https://twitter.com/home?status={{entry.title}}%20{{ entry.url|e }}%20via%20@getpoche" target="_blank" class="tool twitter" title="{% trans "tweet" %}"><span></span></a></li>{% endif %}
{% if constant('SHARE_MAIL') == 1 %}<li><a href="mailto:?subject={{ entry.title|e }}&body={{ entry.url|e }} via @getpoche" class="tool email" title="{% trans "email" %}"><span></span></a></li>{% endif %}
</li>
</ul>
</div>
<header class="mbm">
<h1>{{ entry.title|e }}</h1>
<div class="vieworiginal txtright small"><a href="{{ entry.url|e }}" target="_blank" title="{% trans "original" %} : {{ entry.title|e }}">{{ entry.url | e | getDomain }}</a></div>
</header>
<article>
{{ content | raw }}
<div class="vieworiginal txtright small"><a href="{{ entry.url|e }}" target="_blank" title="{% trans "original" %} : {{ entry.title|e }}">{{ entry.url | e | getDomain }}</a></div>
</article>
<div class="tools">
<ul class="tools">
<li>
<li><a href="{{ referer }}" title="{% trans "back to home" %}" class="tool back"><span></span></a></li>
<li><a href="#" title="{% trans "back to top" %}" class="tool top"><span></span></a></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>
{% if constant('SHARE_TWITTER') == 1 %}<li><a href="https://twitter.com/home?status={{entry.title}}%20{{ entry.url|e }}%20via%20@getpoche" target="_blank" class="tool twitter" title="{% trans "tweet" %}"><span></span></a></li>{% endif %}
{% if constant('SHARE_MAIL') == 1 %}<li><a href="mailto:?subject={{ entry.title|e }}&body={{ entry.url|e }} via @getpoche" class="tool email" title="{% trans "email" %}"><span></span></a></li>{% endif %}
</li>
</ul>
<p>{% trans "this article appears wrong?" %} <a href="https://github.com/inthepoche/poche/issues/new">{% trans "create an issue" %}</a> {% trans "or" %} <a href="mailto:support@inthepoche.com?subject=Wrong display in poche&body={{ entry.url|e }}">{% trans "contact us by mail" %}</a></p>
</div>
</div>
{% endblock %}