first commit
This commit is contained in:
commit
e3cf8fe802
1
.gitignore
vendored
Normal file
1
.gitignore
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
src/data/
|
55
README.markdown
Normal file
55
README.markdown
Normal file
@ -0,0 +1,55 @@
|
|||||||
|
Miniflux - Minimalist Feed Reader
|
||||||
|
=================================
|
||||||
|
|
||||||
|
Miniflux is a minimalist web-based news reader.
|
||||||
|
|
||||||
|
|
||||||
|
Features
|
||||||
|
--------
|
||||||
|
|
||||||
|
- Host anywhere (shared hosting, vps or localhost)
|
||||||
|
- Easy setup => copy and paste and you are done!
|
||||||
|
- CSS optimized for readability
|
||||||
|
- Keep an history of read items
|
||||||
|
- Remove Feedburner Ads and analytics trackers
|
||||||
|
- Import/Export OPML feeds
|
||||||
|
- Feeds update by a cronjob or with the user interface in one click
|
||||||
|
- Protected by a login/password (only one possible user)
|
||||||
|
- Use secure headers (only external images are allowed)
|
||||||
|
|
||||||
|
Todo
|
||||||
|
----
|
||||||
|
|
||||||
|
- Remove older items from the database
|
||||||
|
- Mobile CSS
|
||||||
|
- Improve feeds update to use Ajax calls
|
||||||
|
|
||||||
|
Requirements
|
||||||
|
------------
|
||||||
|
|
||||||
|
- PHP >= 5.3
|
||||||
|
- XML extensions (SimpleXML, DOM...)
|
||||||
|
- Sqlite
|
||||||
|
|
||||||
|
Dependencies
|
||||||
|
------------
|
||||||
|
|
||||||
|
- [PicoFeed](https://github.com/fguillot/picoFeed)
|
||||||
|
- [PicoFarad](https://github.com/fguillot/picoFarad)
|
||||||
|
- [PicoTools](https://github.com/fguillot/picoTools)
|
||||||
|
- [PicoDb](https://github.com/fguillot/picoDb)
|
||||||
|
- [SimpleValidator](https://github.com/fguillot/simpleValidator)
|
||||||
|
|
||||||
|
Screenshots
|
||||||
|
-----------
|
||||||
|
|
||||||
|
![items](https://github.com/fguillot/miniflux/screenshots/items.png)
|
||||||
|
|
||||||
|
![item](https://github.com/fguillot/miniflux/screenshots/item.png)
|
||||||
|
|
||||||
|
![feeds](https://github.com/fguillot/miniflux/screenshots/feeds.png)
|
||||||
|
|
||||||
|
Installation
|
||||||
|
------------
|
||||||
|
|
||||||
|
In progress...
|
BIN
screenshots/feeds.png
Normal file
BIN
screenshots/feeds.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 75 KiB |
BIN
screenshots/item.png
Normal file
BIN
screenshots/item.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 154 KiB |
BIN
screenshots/items.png
Normal file
BIN
screenshots/items.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 55 KiB |
348
src/assets/css/app.css
Normal file
348
src/assets/css/app.css
Normal file
@ -0,0 +1,348 @@
|
|||||||
|
li,
|
||||||
|
ul,
|
||||||
|
table,
|
||||||
|
tr,
|
||||||
|
td,
|
||||||
|
th,
|
||||||
|
p,
|
||||||
|
blockquote,
|
||||||
|
body {
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
font-size: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
body {
|
||||||
|
margin: 0 auto;
|
||||||
|
max-width: 750px;
|
||||||
|
color: #333;
|
||||||
|
font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
|
||||||
|
}
|
||||||
|
|
||||||
|
a {
|
||||||
|
color: #3366CC;
|
||||||
|
}
|
||||||
|
|
||||||
|
a:focus {
|
||||||
|
outline: 0;
|
||||||
|
color: red;
|
||||||
|
text-decoration: none;
|
||||||
|
padding: 3px;
|
||||||
|
border: 1px dotted #aaa;
|
||||||
|
}
|
||||||
|
|
||||||
|
a:hover {
|
||||||
|
color: #333;
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
h1, h2, h3 {
|
||||||
|
font-weight: normal;
|
||||||
|
color: #333;
|
||||||
|
}
|
||||||
|
|
||||||
|
h2 {
|
||||||
|
font-size: 1.6em;
|
||||||
|
}
|
||||||
|
|
||||||
|
h3 {
|
||||||
|
font-size: 1.2em;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* forms */
|
||||||
|
form {
|
||||||
|
padding-top: 5px;
|
||||||
|
padding-bottom: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
label {
|
||||||
|
cursor: pointer;
|
||||||
|
display: block;
|
||||||
|
float: left;
|
||||||
|
width: 10em;
|
||||||
|
}
|
||||||
|
|
||||||
|
input[type="email"],
|
||||||
|
input[type="tel"],
|
||||||
|
input[type="password"],
|
||||||
|
input[type="text"] {
|
||||||
|
border: 1px solid #ccc;
|
||||||
|
padding: 3px;
|
||||||
|
line-height: 15px;
|
||||||
|
width: 250px;
|
||||||
|
font-size: 99%;
|
||||||
|
margin-bottom: 15px;
|
||||||
|
}
|
||||||
|
|
||||||
|
input[type="email"]:focus,
|
||||||
|
input[type="tel"]:focus,
|
||||||
|
input[type="password"]:focus,
|
||||||
|
input[type="text"]:focus,
|
||||||
|
textarea:focus {
|
||||||
|
color: #000;
|
||||||
|
border-color: rgba(82, 168, 236, 0.8);
|
||||||
|
outline: 0;
|
||||||
|
box-shadow: 0 0 8px rgba(82, 168, 236, 0.6);
|
||||||
|
}
|
||||||
|
|
||||||
|
textarea {
|
||||||
|
border: 1px solid #ccc;
|
||||||
|
padding: 3px;
|
||||||
|
width: 400px;
|
||||||
|
height: 200px;
|
||||||
|
font-size: 99%;
|
||||||
|
}
|
||||||
|
|
||||||
|
select {
|
||||||
|
margin-bottom: 15px;
|
||||||
|
}
|
||||||
|
|
||||||
|
::-webkit-input-placeholder {
|
||||||
|
color: #bbb;
|
||||||
|
padding-top: 2px;
|
||||||
|
}
|
||||||
|
|
||||||
|
::-ms-input-placeholder {
|
||||||
|
color: #bbb;
|
||||||
|
padding-top: 2px;
|
||||||
|
}
|
||||||
|
|
||||||
|
:-moz-placeholder {
|
||||||
|
color: #bbb;
|
||||||
|
padding-top: 2px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-actions {
|
||||||
|
margin-top: 40px;
|
||||||
|
}
|
||||||
|
|
||||||
|
input.form-error,
|
||||||
|
textarea.form-error {
|
||||||
|
border: 2px solid #b94a48;
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-errors {
|
||||||
|
color: #b94a48;
|
||||||
|
margin-left: 10em;
|
||||||
|
list-style-type: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* alerts */
|
||||||
|
.alert {
|
||||||
|
padding: 8px 35px 8px 14px;
|
||||||
|
margin-bottom: 20px;
|
||||||
|
color: #c09853;
|
||||||
|
background-color: #fcf8e3;
|
||||||
|
border: 1px solid #fbeed5;
|
||||||
|
border-radius: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.alert-success {
|
||||||
|
color: #468847;
|
||||||
|
background-color: #dff0d8;
|
||||||
|
border-color: #d6e9c6;
|
||||||
|
}
|
||||||
|
|
||||||
|
.alert-error {
|
||||||
|
color: #b94a48;
|
||||||
|
background-color: #f2dede;
|
||||||
|
border-color: #eed3d7;
|
||||||
|
}
|
||||||
|
|
||||||
|
.alert-info {
|
||||||
|
color: #3a87ad;
|
||||||
|
background-color: #d9edf7;
|
||||||
|
border-color: #bce8f1;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* buttons */
|
||||||
|
.btn {
|
||||||
|
display: block;
|
||||||
|
color: #333;
|
||||||
|
border: 1px solid #ccc;
|
||||||
|
background: #efefef;
|
||||||
|
padding: 5px;
|
||||||
|
padding-left: 15px;
|
||||||
|
padding-right: 15px;
|
||||||
|
font-size: 90%;
|
||||||
|
cursor: pointer;
|
||||||
|
border-radius: 2px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-blue {
|
||||||
|
border-color: #3079ed;
|
||||||
|
background: #4d90fe;
|
||||||
|
color: #fff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-blue:hover,
|
||||||
|
.btn-blue:focus {
|
||||||
|
border-color: #2f5bb7;
|
||||||
|
background: #357ae8;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* header */
|
||||||
|
header {
|
||||||
|
margin-bottom: 50px;
|
||||||
|
margin-top: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
header ul {
|
||||||
|
text-align: right;
|
||||||
|
font-size: 90%;
|
||||||
|
}
|
||||||
|
|
||||||
|
header li {
|
||||||
|
display: inline;
|
||||||
|
padding-left: 30px;
|
||||||
|
}
|
||||||
|
|
||||||
|
header a {
|
||||||
|
color: #777;
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
nav .active a {
|
||||||
|
color: #333;
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
|
.logo {
|
||||||
|
color: #000;
|
||||||
|
letter-spacing: 1px;
|
||||||
|
float: left;
|
||||||
|
}
|
||||||
|
|
||||||
|
.logo span {
|
||||||
|
color: #339966;
|
||||||
|
}
|
||||||
|
|
||||||
|
.page-header {
|
||||||
|
margin-bottom: 30px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.page-header h2 {
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
font-size: 130%;
|
||||||
|
border-bottom: 1px dotted #ccc;
|
||||||
|
}
|
||||||
|
|
||||||
|
.page-header ul {
|
||||||
|
text-align: right;
|
||||||
|
margin-top: 2px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.page-header li {
|
||||||
|
display: inline;
|
||||||
|
padding-left: 10px;
|
||||||
|
padding-right: 10px;
|
||||||
|
border-right: 1px dotted #ccc;
|
||||||
|
}
|
||||||
|
|
||||||
|
.page-header li:last-child {
|
||||||
|
border: none;
|
||||||
|
padding-right: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* items listing */
|
||||||
|
.items article {
|
||||||
|
margin-bottom: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.items h2 {
|
||||||
|
font-size: 100%;
|
||||||
|
font-weight: bold;
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
padding-bottom: 2px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.items a {
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.items a:hover,
|
||||||
|
.items :focus {
|
||||||
|
text-decoration: underline;
|
||||||
|
}
|
||||||
|
|
||||||
|
.items p {
|
||||||
|
color: #aaa;
|
||||||
|
font-size: 70%;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* item */
|
||||||
|
.item {
|
||||||
|
font-size: 110%;
|
||||||
|
color: #444;
|
||||||
|
padding-bottom: 50px;
|
||||||
|
text-rendering: optimizeLegibility;
|
||||||
|
-webkit-font-smoothing: antialiased;
|
||||||
|
}
|
||||||
|
|
||||||
|
.item pre,
|
||||||
|
.item ul,
|
||||||
|
.item p {
|
||||||
|
margin-top: 15px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.item p {
|
||||||
|
margin-bottom: 20px;
|
||||||
|
overflow: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.item ul {
|
||||||
|
margin-left: 25px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.item li {
|
||||||
|
margin-top: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.item pre {
|
||||||
|
border: 1px solid #ccc;
|
||||||
|
border-radius: 10px;
|
||||||
|
background: #f0f0f0;
|
||||||
|
padding: 20px;
|
||||||
|
overflow: auto;
|
||||||
|
color: brown;
|
||||||
|
}
|
||||||
|
|
||||||
|
.item img {
|
||||||
|
display: block;
|
||||||
|
margin-top: 15px;
|
||||||
|
margin-bottom: 15px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.item code {
|
||||||
|
color: brown;
|
||||||
|
}
|
||||||
|
|
||||||
|
.infos {
|
||||||
|
padding-bottom: 30px;
|
||||||
|
color: #ccc;
|
||||||
|
}
|
||||||
|
|
||||||
|
.item h1 {
|
||||||
|
}
|
||||||
|
|
||||||
|
.item h1 a {
|
||||||
|
font-size: 150%;
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
blockquote {
|
||||||
|
border-left: 4px solid #ddd;
|
||||||
|
padding-left: 25px;
|
||||||
|
margin-left: 20px;
|
||||||
|
margin-top: 20px;
|
||||||
|
margin-bottom: 20px;
|
||||||
|
color: #666;
|
||||||
|
line-height: 22px;
|
||||||
|
}
|
26
src/common.php
Normal file
26
src/common.php
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
require 'vendor/PicoTools/Dependency_Injection.php';
|
||||||
|
require 'vendor/PicoDb/Database.php';
|
||||||
|
require 'vendor/PicoDb/Table.php';
|
||||||
|
require 'vendor/PicoTools/Crypto.php';
|
||||||
|
require 'schema.php';
|
||||||
|
require 'model.php';
|
||||||
|
|
||||||
|
|
||||||
|
PicoTools\container('db', function() {
|
||||||
|
|
||||||
|
$db = new PicoDb\Database(array(
|
||||||
|
'driver' => 'sqlite',
|
||||||
|
'filename' => 'data/db.sqlite'
|
||||||
|
));
|
||||||
|
|
||||||
|
if ($db->schema()->check(1)) {
|
||||||
|
|
||||||
|
return $db;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
|
||||||
|
die('Unable to migrate database schema.');
|
||||||
|
}
|
||||||
|
});
|
5
src/cronjob.php
Normal file
5
src/cronjob.php
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
require 'common.php';
|
||||||
|
|
||||||
|
Model\update_feeds();
|
259
src/index.php
Normal file
259
src/index.php
Normal file
@ -0,0 +1,259 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
require 'common.php';
|
||||||
|
require 'vendor/PicoTools/Template.php';
|
||||||
|
require 'vendor/PicoTools/Helper.php';
|
||||||
|
require 'vendor/PicoFarad/Response.php';
|
||||||
|
require 'vendor/PicoFarad/Request.php';
|
||||||
|
require 'vendor/PicoFarad/Session.php';
|
||||||
|
require 'vendor/PicoFarad/Router.php';
|
||||||
|
|
||||||
|
use PicoFarad\Router;
|
||||||
|
use PicoFarad\Response;
|
||||||
|
use PicoFarad\Request;
|
||||||
|
use PicoFarad\Session;
|
||||||
|
use PicoTools\Template;
|
||||||
|
|
||||||
|
|
||||||
|
Session\open(dirname($_SERVER['PHP_SELF']));
|
||||||
|
|
||||||
|
|
||||||
|
Router\before(function($action) {
|
||||||
|
|
||||||
|
if ($action !== 'login' && ! isset($_SESSION['user'])) {
|
||||||
|
|
||||||
|
PicoFarad\Response\redirect('?action=login');
|
||||||
|
}
|
||||||
|
|
||||||
|
Response\csp(array(
|
||||||
|
'img-src' => '*'
|
||||||
|
));
|
||||||
|
|
||||||
|
Response\xframe();
|
||||||
|
Response\xss();
|
||||||
|
Response\nosniff();
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
Router\get_action('logout', function() {
|
||||||
|
|
||||||
|
Session\close();
|
||||||
|
|
||||||
|
Response\redirect('?action=login');
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
Router\get_action('login', function() {
|
||||||
|
|
||||||
|
if (isset($_SESSION['user'])) {
|
||||||
|
|
||||||
|
Response\redirect('./index.php');
|
||||||
|
}
|
||||||
|
|
||||||
|
Response\html(Template\load('login', array(
|
||||||
|
'errors' => array(),
|
||||||
|
'values' => array()
|
||||||
|
)));
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
Router\post_action('login', function() {
|
||||||
|
|
||||||
|
$values = Request\values();
|
||||||
|
list($valid, $errors) = Model\validate_login($values);
|
||||||
|
|
||||||
|
if ($valid) {
|
||||||
|
|
||||||
|
Response\redirect('?action=default');
|
||||||
|
}
|
||||||
|
|
||||||
|
Response\html(Template\load('login', array(
|
||||||
|
'errors' => $errors,
|
||||||
|
'values' => $values
|
||||||
|
)));
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
Router\get_action('read', function() {
|
||||||
|
|
||||||
|
$id = Request\param('id');
|
||||||
|
|
||||||
|
Model\set_item_read($id);
|
||||||
|
|
||||||
|
Response\html(Template\layout('read_item', array(
|
||||||
|
'item' => Model\get_item($id)
|
||||||
|
)));
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
Router\get_action('history', function() {
|
||||||
|
|
||||||
|
Response\html(Template\layout('read_items', array(
|
||||||
|
'items' => Model\get_read_items(),
|
||||||
|
'menu' => 'history'
|
||||||
|
)));
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
Router\get_action('remove', function() {
|
||||||
|
|
||||||
|
$id = Request\int_param('feed_id');
|
||||||
|
|
||||||
|
if ($id) {
|
||||||
|
|
||||||
|
Model\remove_feed($id);
|
||||||
|
}
|
||||||
|
|
||||||
|
Response\redirect('?action=feeds');
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
Router\get_action('refresh', function() {
|
||||||
|
|
||||||
|
$id = Request\int_param('feed_id');
|
||||||
|
|
||||||
|
if ($id) {
|
||||||
|
|
||||||
|
Model\update_feed($id);
|
||||||
|
}
|
||||||
|
|
||||||
|
Response\redirect('?action=unread');
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
Router\get_action('flush-unread', function() {
|
||||||
|
|
||||||
|
Model\flush_unread();
|
||||||
|
Response\redirect('?action=unread');
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
Router\get_action('flush-history', function() {
|
||||||
|
|
||||||
|
Model\flush_read();
|
||||||
|
Response\redirect('?action=history');
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
Router\get_action('refresh-all', function() {
|
||||||
|
|
||||||
|
Model\update_feeds();
|
||||||
|
Session\flash('Your subscriptions are updated');
|
||||||
|
Response\redirect('?action=unread');
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
Router\get_action('feeds', function() {
|
||||||
|
|
||||||
|
Response\html(Template\layout('feeds', array(
|
||||||
|
'feeds' => Model\get_feeds(),
|
||||||
|
'menu' => 'feeds'
|
||||||
|
)));
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
Router\get_action('add', function() {
|
||||||
|
|
||||||
|
Response\html(Template\layout('add', array(
|
||||||
|
'values' => array(),
|
||||||
|
'errors' => array(),
|
||||||
|
'menu' => 'feeds'
|
||||||
|
)));
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
Router\post_action('add', function() {
|
||||||
|
|
||||||
|
if (Model\import_feed($_POST['url'])) {
|
||||||
|
|
||||||
|
Session\flash('Subscription added successfully.');
|
||||||
|
Response\redirect('?action=feeds');
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
|
||||||
|
Session\flash_error('Unable to find a subscription.');
|
||||||
|
}
|
||||||
|
|
||||||
|
Response\html(Template\layout('add', array(
|
||||||
|
'values' => array('url' => $_POST['url']),
|
||||||
|
'errors' => array('url' => 'Unable to find a news feed.'),
|
||||||
|
'menu' => 'feeds'
|
||||||
|
)));
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
Router\get_action('export', function() {
|
||||||
|
|
||||||
|
Response\force_download('feeds.opml');
|
||||||
|
Response\xml(Model\export_feeds());
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
Router\get_action('import', function() {
|
||||||
|
|
||||||
|
Response\html(Template\layout('import', array(
|
||||||
|
'errors' => array(),
|
||||||
|
'menu' => 'feeds'
|
||||||
|
)));
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
Router\post_action('import', function() {
|
||||||
|
|
||||||
|
if (Model\import_feeds(Request\file_content('file'))) {
|
||||||
|
|
||||||
|
Session\flash('Your feeds are imported.');
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
|
||||||
|
Session\flash_error('Unable to import your OPML file.');
|
||||||
|
}
|
||||||
|
|
||||||
|
Response\redirect('?action=feeds');
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
Router\get_action('config', function() {
|
||||||
|
|
||||||
|
Response\html(Template\layout('config', array(
|
||||||
|
'errors' => array(),
|
||||||
|
'values' => Model\get_config(),
|
||||||
|
'menu' => 'config'
|
||||||
|
)));
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
Router\post_action('config', function() {
|
||||||
|
|
||||||
|
$values = Request\values();
|
||||||
|
list($valid, $errors) = Model\validate_config_update($values);
|
||||||
|
|
||||||
|
if ($valid) {
|
||||||
|
|
||||||
|
if (Model\save_config($values)) {
|
||||||
|
|
||||||
|
Session\flash('Your preferences are updated.');
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
|
||||||
|
Session\flash_error('Unable to update your preferences.');
|
||||||
|
}
|
||||||
|
|
||||||
|
Response\redirect('?action=config');
|
||||||
|
}
|
||||||
|
|
||||||
|
Response\html(Template\layout('config', array(
|
||||||
|
'errors' => $errors,
|
||||||
|
'values' => $values,
|
||||||
|
'menu' => 'config'
|
||||||
|
)));
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
Router\notfound(function() {
|
||||||
|
|
||||||
|
Response\html(Template\layout('unread_items', array(
|
||||||
|
'items' => Model\get_unread_items(),
|
||||||
|
'menu' => 'unread'
|
||||||
|
)));
|
||||||
|
});
|
331
src/model.php
Normal file
331
src/model.php
Normal file
@ -0,0 +1,331 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Model;
|
||||||
|
|
||||||
|
require_once 'vendor/PicoFeed/Export.php';
|
||||||
|
require_once 'vendor/PicoFeed/Import.php';
|
||||||
|
require_once 'vendor/PicoFeed/Parser.php';
|
||||||
|
require_once 'vendor/PicoFeed/Reader.php';
|
||||||
|
require_once 'vendor/PicoTools/Crypto.php';
|
||||||
|
require_once 'vendor/SimpleValidator/Validator.php';
|
||||||
|
require_once 'vendor/SimpleValidator/Base.php';
|
||||||
|
require_once 'vendor/SimpleValidator/Validators/Required.php';
|
||||||
|
require_once 'vendor/SimpleValidator/Validators/Unique.php';
|
||||||
|
require_once 'vendor/SimpleValidator/Validators/MaxLength.php';
|
||||||
|
require_once 'vendor/SimpleValidator/Validators/MinLength.php';
|
||||||
|
require_once 'vendor/SimpleValidator/Validators/Integer.php';
|
||||||
|
require_once 'vendor/SimpleValidator/Validators/Equals.php';
|
||||||
|
|
||||||
|
use SimpleValidator\Validator;
|
||||||
|
use SimpleValidator\Validators;
|
||||||
|
use PicoFeed\Import;
|
||||||
|
use PicoFeed\Reader;
|
||||||
|
use PicoFeed\Export;
|
||||||
|
|
||||||
|
|
||||||
|
function export_feeds()
|
||||||
|
{
|
||||||
|
$opml = new Export(get_feeds());
|
||||||
|
return $opml->execute();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function import_feeds($content)
|
||||||
|
{
|
||||||
|
$import = new Import($content);
|
||||||
|
$feeds = $import->execute();
|
||||||
|
|
||||||
|
if ($feeds) {
|
||||||
|
|
||||||
|
$db = \PicoTools\singleton('db');
|
||||||
|
|
||||||
|
$db->startTransaction();
|
||||||
|
|
||||||
|
foreach ($feeds as $feed) {
|
||||||
|
|
||||||
|
if (! $db->table('feeds')->eq('feed_url', $feed->feed_url)->count()) {
|
||||||
|
|
||||||
|
$db->table('feeds')->save(array(
|
||||||
|
'title' => $feed->title,
|
||||||
|
'site_url' => $feed->site_url,
|
||||||
|
'feed_url' => $feed->feed_url
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$db->closeTransaction();
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function import_feed($url)
|
||||||
|
{
|
||||||
|
$reader = new Reader;
|
||||||
|
$reader->download($url);
|
||||||
|
|
||||||
|
$parser = $reader->getParser();
|
||||||
|
|
||||||
|
if ($parser !== false) {
|
||||||
|
|
||||||
|
$feed = $parser->execute();
|
||||||
|
|
||||||
|
$db = \PicoTools\singleton('db');
|
||||||
|
|
||||||
|
if (! $db->table('feeds')->eq('feed_url', $reader->getUrl())->count()) {
|
||||||
|
|
||||||
|
$rs = $db->table('feeds')->save(array(
|
||||||
|
'title' => $feed->title,
|
||||||
|
'site_url' => $feed->url,
|
||||||
|
'feed_url' => $reader->getUrl()
|
||||||
|
));
|
||||||
|
|
||||||
|
if ($rs) {
|
||||||
|
|
||||||
|
$feed_id = $db->getConnection()->getLastId();
|
||||||
|
update_items($feed_id, $feed->items);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function get_feeds()
|
||||||
|
{
|
||||||
|
return \PicoTools\singleton('db')
|
||||||
|
->table('feeds')
|
||||||
|
->asc('title')
|
||||||
|
->findAll();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function get_feed($feed_id)
|
||||||
|
{
|
||||||
|
return \PicoTools\singleton('db')
|
||||||
|
->table('feeds')
|
||||||
|
->eq('id', $feed_id)
|
||||||
|
->findOne();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function remove_feed($feed_id)
|
||||||
|
{
|
||||||
|
$db = \PicoTools\singleton('db');
|
||||||
|
|
||||||
|
$db->table('items')->eq('feed_id', $feed_id)->remove();
|
||||||
|
|
||||||
|
return $db->table('feeds')->eq('id', $feed_id)->remove();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function get_unread_items()
|
||||||
|
{
|
||||||
|
return \PicoTools\singleton('db')
|
||||||
|
->table('items')
|
||||||
|
->columns('items.id', 'items.title', 'items.updated', 'feeds.site_url')
|
||||||
|
->join('feeds', 'id', 'feed_id')
|
||||||
|
->eq('status', 'unread')
|
||||||
|
->desc('updated')
|
||||||
|
->findAll();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function get_read_items()
|
||||||
|
{
|
||||||
|
return \PicoTools\singleton('db')
|
||||||
|
->table('items')
|
||||||
|
->columns('items.id', 'items.title', 'items.updated', 'feeds.site_url')
|
||||||
|
->join('feeds', 'id', 'feed_id')
|
||||||
|
->eq('status', 'read')
|
||||||
|
->desc('updated')
|
||||||
|
->findAll();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function get_item($id)
|
||||||
|
{
|
||||||
|
return \PicoTools\singleton('db')
|
||||||
|
->table('items')
|
||||||
|
->eq('id', $id)
|
||||||
|
->findOne();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function set_item_read($id)
|
||||||
|
{
|
||||||
|
\PicoTools\singleton('db')
|
||||||
|
->table('items')
|
||||||
|
->eq('id', $id)
|
||||||
|
->save(array('status' => 'read'));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function flush_unread($id)
|
||||||
|
{
|
||||||
|
\PicoTools\singleton('db')
|
||||||
|
->table('items')
|
||||||
|
->eq('status', 'unread')
|
||||||
|
->save(array('status' => 'removed'));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function flush_read($id)
|
||||||
|
{
|
||||||
|
\PicoTools\singleton('db')
|
||||||
|
->table('items')
|
||||||
|
->eq('status', 'read')
|
||||||
|
->save(array('status' => 'removed'));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function update_feeds()
|
||||||
|
{
|
||||||
|
foreach (get_feeds() as $feed) {
|
||||||
|
|
||||||
|
$reader = new Reader;
|
||||||
|
$reader->download($feed['feed_url']);
|
||||||
|
$parser = $reader->getParser();
|
||||||
|
|
||||||
|
if ($parser !== false) {
|
||||||
|
|
||||||
|
update_items($feed['id'], $parser->execute()->items);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function update_feed($feed_id)
|
||||||
|
{
|
||||||
|
$feed = get_feed($feed_id);
|
||||||
|
|
||||||
|
$reader = new Reader;
|
||||||
|
$reader->download($feed['feed_url']);
|
||||||
|
$parser = $reader->getParser();
|
||||||
|
|
||||||
|
if ($parser !== false) {
|
||||||
|
|
||||||
|
update_items($feed['id'], $parser->execute()->items);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function update_items($feed_id, array $items)
|
||||||
|
{
|
||||||
|
$db = \PicoTools\singleton('db');
|
||||||
|
|
||||||
|
$db->startTransaction();
|
||||||
|
|
||||||
|
foreach ($items as $item) {
|
||||||
|
|
||||||
|
if (! $db->table('items')->eq('id', $item->id)->count()) {
|
||||||
|
|
||||||
|
$db->table('items')->save(array(
|
||||||
|
'id' => $item->id,
|
||||||
|
'title' => $item->title,
|
||||||
|
'url' => $item->url,
|
||||||
|
'updated' => $item->updated,
|
||||||
|
'author' => $item->author,
|
||||||
|
'content' => $item->content,
|
||||||
|
'status' => 'unread',
|
||||||
|
'feed_id' => $feed_id
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$db->closeTransaction();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function get_config()
|
||||||
|
{
|
||||||
|
return \PicoTools\singleton('db')
|
||||||
|
->table('config')
|
||||||
|
->columns('username', 'history')
|
||||||
|
->findOne();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function get_user()
|
||||||
|
{
|
||||||
|
return \PicoTools\singleton('db')
|
||||||
|
->table('config')
|
||||||
|
->columns('username', 'password')
|
||||||
|
->findOne();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function validate_login(array $values)
|
||||||
|
{
|
||||||
|
$v = new Validator($values, array(
|
||||||
|
new Validators\Required('username', 'The user name is required'),
|
||||||
|
new Validators\MaxLength('username', 'The maximum length is 50 characters', 50),
|
||||||
|
new Validators\Required('password', 'The password is required')
|
||||||
|
));
|
||||||
|
|
||||||
|
$result = $v->execute();
|
||||||
|
$errors = $v->getErrors();
|
||||||
|
|
||||||
|
if ($result) {
|
||||||
|
|
||||||
|
$user = get_user();
|
||||||
|
|
||||||
|
if ($user && \PicoTools\Crypto\password_verify($values['password'], $user['password'])) {
|
||||||
|
|
||||||
|
$_SESSION['user'] = $user;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
|
||||||
|
$result = false;
|
||||||
|
$errors['login'] = 'Bad username or password';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return array(
|
||||||
|
$result,
|
||||||
|
$errors
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function validate_config_update(array $values)
|
||||||
|
{
|
||||||
|
if (! empty($values['password'])) {
|
||||||
|
|
||||||
|
$v = new Validator($values, array(
|
||||||
|
new Validators\Required('username', 'The user name is required'),
|
||||||
|
new Validators\MaxLength('username', 'The maximum length is 50 characters', 50),
|
||||||
|
new Validators\Required('password', 'The password is required'),
|
||||||
|
new Validators\MinLength('password', 'The minimum length is 6 characters', 6),
|
||||||
|
new Validators\Required('confirmation', 'The confirmation is required'),
|
||||||
|
new Validators\Equals('password', 'confirmation', 'Passwords doesn\'t match')
|
||||||
|
));
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
|
||||||
|
$v = new Validator($values, array(
|
||||||
|
new Validators\Required('username', 'The user name is required'),
|
||||||
|
new Validators\MaxLength('username', 'The maximum length is 50 characters', 50)
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
return array(
|
||||||
|
$v->execute(),
|
||||||
|
$v->getErrors()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function save_config(array $values)
|
||||||
|
{
|
||||||
|
$values['password'] = \PicoTools\Crypto\password_hash($values['password']);
|
||||||
|
unset($values['confirmation']);
|
||||||
|
|
||||||
|
return \PicoTools\singleton('db')->table('config')->update($values);
|
||||||
|
}
|
43
src/schema.php
Normal file
43
src/schema.php
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Schema;
|
||||||
|
|
||||||
|
function version_1($pdo)
|
||||||
|
{
|
||||||
|
$pdo->exec("
|
||||||
|
CREATE TABLE config (
|
||||||
|
username TEXT DEFAULT 'admin',
|
||||||
|
password TEXT,
|
||||||
|
history INTEGER DEFAULT '15'
|
||||||
|
)
|
||||||
|
");
|
||||||
|
|
||||||
|
$pdo->exec("
|
||||||
|
INSERT INTO config
|
||||||
|
(password)
|
||||||
|
VALUES ('".\PicoTools\Crypto\password_hash('admin')."')
|
||||||
|
");
|
||||||
|
|
||||||
|
$pdo->exec('
|
||||||
|
CREATE TABLE feeds (
|
||||||
|
id INTEGER PRIMARY KEY,
|
||||||
|
site_url TEXT,
|
||||||
|
feed_url TEXT UNIQUE,
|
||||||
|
title TEXT
|
||||||
|
)
|
||||||
|
');
|
||||||
|
|
||||||
|
$pdo->exec('
|
||||||
|
CREATE TABLE items (
|
||||||
|
id TEXT PRIMARY KEY,
|
||||||
|
url TEXT,
|
||||||
|
title TEXT,
|
||||||
|
author TEXT,
|
||||||
|
content TEXT,
|
||||||
|
updated TEXT,
|
||||||
|
status TEXT,
|
||||||
|
feed_id INTEGER,
|
||||||
|
FOREIGN KEY(feed_id) REFERENCES feeds(id) ON DELETE CASCADE
|
||||||
|
)
|
||||||
|
');
|
||||||
|
}
|
12
src/templates/add.php
Normal file
12
src/templates/add.php
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
<div class="page-header">
|
||||||
|
<h2>New subscription</h2>
|
||||||
|
<?php include __DIR__.'/feed_menu.php' ?>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<form method="post" action="?action=add">
|
||||||
|
<label for="url">Site or Feed URL</label>
|
||||||
|
<input type="text" name="url" id="url" placeholder="http://website/" autofocus required/>
|
||||||
|
<div class="form-actions">
|
||||||
|
<button type="submit" class="btn btn-blue">Add</button>
|
||||||
|
</div>
|
||||||
|
</form>
|
3
src/templates/app_footer.php
Normal file
3
src/templates/app_footer.php
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
</section>
|
||||||
|
</body>
|
||||||
|
</html>
|
24
src/templates/app_header.php
Normal file
24
src/templates/app_header.php
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<title>miniflux</title>
|
||||||
|
<link href="./assets/css/app.css?v1" rel="stylesheet" media="screen">
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<header>
|
||||||
|
<nav>
|
||||||
|
<a class="logo" href="?">mini<span>flux</span></a>
|
||||||
|
<ul>
|
||||||
|
<li <?= isset($menu) && $menu === 'unread' ? 'class="active"' : '' ?>><a href="?action=default">unread</a></li>
|
||||||
|
<li <?= isset($menu) && $menu === 'history' ? 'class="active"' : '' ?>><a href="?action=history">history</a></li>
|
||||||
|
<li <?= isset($menu) && $menu === 'feeds' ? 'class="active"' : '' ?>><a href="?action=feeds">subscriptions</a></li>
|
||||||
|
<li <?= isset($menu) && $menu === 'config' ? 'class="active"' : '' ?>><a href="?action=config">preferences</a></li>
|
||||||
|
<li><a href="?action=logout">logout</a></li>
|
||||||
|
</ul>
|
||||||
|
</nav>
|
||||||
|
</header>
|
||||||
|
<section class="page">
|
||||||
|
<?= Helper\flash('<div class="alert alert-success">%s</div>') ?>
|
||||||
|
<?= Helper\flash_error('<div class="alert alert-error">%s</div>') ?>
|
19
src/templates/config.php
Normal file
19
src/templates/config.php
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
<div class="page-header">
|
||||||
|
<h2>Preferences</h2>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<form method="post" action="?action=config">
|
||||||
|
|
||||||
|
<?= Helper\form_label('Username', 'username', 'control-label') ?>
|
||||||
|
<?= Helper\form_text('username', $values, $errors, array('required')) ?><br/>
|
||||||
|
|
||||||
|
<?= Helper\form_label('Password', 'password', 'control-label') ?>
|
||||||
|
<?= Helper\form_password('password', $values, $errors) ?><br/>
|
||||||
|
|
||||||
|
<?= Helper\form_label('Confirmation', 'confirmation', 'control-label') ?>
|
||||||
|
<?= Helper\form_password('confirmation', $values, $errors) ?><br/>
|
||||||
|
|
||||||
|
<div class="form-actions">
|
||||||
|
<input type="submit" value="Update" class="btn btn-blue"/>
|
||||||
|
</div>
|
||||||
|
</form>
|
7
src/templates/feed_menu.php
Normal file
7
src/templates/feed_menu.php
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
<ul>
|
||||||
|
<li><a href="?action=feeds">feeds</a></li>
|
||||||
|
<li><a href="?action=add">add</a></li>
|
||||||
|
<li><a href="?action=import">import</a></li>
|
||||||
|
<li><a href="?action=export">export</a></li>
|
||||||
|
<li><a href="?action=refresh-all">refresh all</a></li>
|
||||||
|
</ul>
|
25
src/templates/feeds.php
Normal file
25
src/templates/feeds.php
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
<div class="page-header">
|
||||||
|
<h2>Subscriptions</h2>
|
||||||
|
<?php include __DIR__.'/feed_menu.php' ?>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<?php if (empty($feeds)): ?>
|
||||||
|
|
||||||
|
<p class="alert alert-info">No subscriptions.</p>
|
||||||
|
|
||||||
|
<?php else: ?>
|
||||||
|
|
||||||
|
<section class="items">
|
||||||
|
<?php foreach ($feeds as $feed): ?>
|
||||||
|
<article>
|
||||||
|
<h2><a href="<?= $feed['site_url'] ?>" traget="_blank"><?= Helper\escape($feed['title']) ?></a></h2>
|
||||||
|
<p>
|
||||||
|
<?= Helper\escape(parse_url($feed['site_url'], PHP_URL_HOST)) ?> |
|
||||||
|
<a href="?action=remove&feed_id=<?= $feed['id'] ?>">remove</a> |
|
||||||
|
<a href="?action=refresh&feed_id=<?= $feed['id'] ?>">refresh</a>
|
||||||
|
</p>
|
||||||
|
</article>
|
||||||
|
<?php endforeach ?>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<?php endif ?>
|
12
src/templates/import.php
Normal file
12
src/templates/import.php
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
<div class="page-header">
|
||||||
|
<h2>OPML Import</h2>
|
||||||
|
<?php include __DIR__.'/feed_menu.php' ?>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<form method="post" action="?action=import" enctype="multipart/form-data">
|
||||||
|
<label for="file">OPML file</label>
|
||||||
|
<input type="file" name="file" required/>
|
||||||
|
<div class="form-actions">
|
||||||
|
<button type="submit" class="btn btn-blue">Import</button>
|
||||||
|
</div>
|
||||||
|
</form>
|
34
src/templates/login.php
Normal file
34
src/templates/login.php
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<title>miniflux</title>
|
||||||
|
<link href="./assets/css/app.css?v1" rel="stylesheet" media="screen">
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
|
||||||
|
<div class="page-header">
|
||||||
|
<h1>Login</h1>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<?php if (isset($errors['login'])): ?>
|
||||||
|
|
||||||
|
<p class="alert alert-error"><?= Helper\escape($errors['login']) ?></p>
|
||||||
|
|
||||||
|
<?php endif ?>
|
||||||
|
|
||||||
|
<form method="post" action="?action=login">
|
||||||
|
|
||||||
|
<?= Helper\form_label('Username', 'username', 'control-label') ?>
|
||||||
|
<?= Helper\form_text('username', $values, $errors, array('autofocus', 'required')) ?><br/>
|
||||||
|
|
||||||
|
<?= Helper\form_label('Password', 'password', 'control-label') ?>
|
||||||
|
<?= Helper\form_password('password', $values, $errors, array('required')) ?>
|
||||||
|
|
||||||
|
<div class="form-actions">
|
||||||
|
<input type="submit" value="Login" class="btn btn-blue"/>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</body>
|
||||||
|
</html>
|
20
src/templates/read_item.php
Normal file
20
src/templates/read_item.php
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
<?php if (empty($item)): ?>
|
||||||
|
|
||||||
|
<p class="alert alert-info">Article not found.</p>
|
||||||
|
|
||||||
|
<?php else: ?>
|
||||||
|
|
||||||
|
<article class="item">
|
||||||
|
<h1>
|
||||||
|
<a href="<?= $item['url'] ?>" target="_blank"><?= Helper\escape($item['title']) ?></a>
|
||||||
|
</h1>
|
||||||
|
|
||||||
|
<p class="infos">
|
||||||
|
<?= Helper\escape(parse_url($item['url'], PHP_URL_HOST)) ?> |
|
||||||
|
<?= date('l, j F Y H:i T', $item['updated']) ?>
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<?= $item['content'] ?>
|
||||||
|
</article>
|
||||||
|
|
||||||
|
<?php endif ?>
|
26
src/templates/read_items.php
Normal file
26
src/templates/read_items.php
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
<?php if (empty($items)): ?>
|
||||||
|
|
||||||
|
<p class="alert alert-info">No history.</p>
|
||||||
|
|
||||||
|
<?php else: ?>
|
||||||
|
|
||||||
|
<div class="page-header">
|
||||||
|
<h2>History</h2>
|
||||||
|
<ul>
|
||||||
|
<li><a href="?action=flush-history">flush</a></li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<section class="items">
|
||||||
|
<?php foreach ($items as $item): ?>
|
||||||
|
<article>
|
||||||
|
<h2><a href="?action=read&id=<?= urlencode($item['id']) ?>"><?= Helper\escape($item['title']) ?></a></h2>
|
||||||
|
<p>
|
||||||
|
<?= Helper\escape(parse_url($item['site_url'], PHP_URL_HOST)) ?> |
|
||||||
|
<?= date('l, j F Y H:i T', $item['updated']) ?>
|
||||||
|
</p>
|
||||||
|
</article>
|
||||||
|
<?php endforeach ?>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<?php endif ?>
|
25
src/templates/unread_items.php
Normal file
25
src/templates/unread_items.php
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
<?php if (empty($items)): ?>
|
||||||
|
|
||||||
|
<p class="alert alert-info">No unread items.</p>
|
||||||
|
|
||||||
|
<?php else: ?>
|
||||||
|
|
||||||
|
<div class="page-header">
|
||||||
|
<h2>Unread items</h2>
|
||||||
|
<ul>
|
||||||
|
<li><a href="?action=flush-unread">flush</a></li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<section class="items">
|
||||||
|
<?php foreach ($items as $item): ?>
|
||||||
|
<article>
|
||||||
|
<h2><a href="?action=read&id=<?= urlencode($item['id']) ?>"><?= Helper\escape($item['title']) ?></a></h2>
|
||||||
|
<p>
|
||||||
|
<?= Helper\escape(parse_url($item['site_url'], PHP_URL_HOST)) ?>
|
||||||
|
</p>
|
||||||
|
</article>
|
||||||
|
<?php endforeach ?>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<?php endif ?>
|
114
src/vendor/PicoDb/Database.php
vendored
Normal file
114
src/vendor/PicoDb/Database.php
vendored
Normal file
@ -0,0 +1,114 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace PicoDb;
|
||||||
|
|
||||||
|
class Database
|
||||||
|
{
|
||||||
|
private $logs = array();
|
||||||
|
private $pdo;
|
||||||
|
|
||||||
|
|
||||||
|
public function __construct(array $settings)
|
||||||
|
{
|
||||||
|
if (! isset($settings['driver'])) {
|
||||||
|
|
||||||
|
throw new \LogicException('You must define a database driver.');
|
||||||
|
}
|
||||||
|
|
||||||
|
switch ($settings['driver']) {
|
||||||
|
|
||||||
|
case 'sqlite':
|
||||||
|
require_once __DIR__.'/Drivers/Sqlite.php';
|
||||||
|
$this->pdo = new Sqlite($settings['filename']);
|
||||||
|
break;
|
||||||
|
/*
|
||||||
|
case 'mysql':
|
||||||
|
$this->pdo = new \PDO(
|
||||||
|
'mysql:host='.$settings['hostname'].';dbname='.$settings['dbname'],
|
||||||
|
$settings['username'],
|
||||||
|
$settings['password']
|
||||||
|
);
|
||||||
|
break;*/
|
||||||
|
|
||||||
|
default:
|
||||||
|
throw new \LogicException('This database driver is not supported.');
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->pdo->setAttribute(\PDO::ATTR_ERRMODE, \PDO::ERRMODE_EXCEPTION);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public function setLogMessage($message)
|
||||||
|
{
|
||||||
|
$this->logs[] = $message;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public function getLogMessages()
|
||||||
|
{
|
||||||
|
return implode(', ', $this->logs);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public function getConnection()
|
||||||
|
{
|
||||||
|
return $this->pdo;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public function escapeIdentifier($value)
|
||||||
|
{
|
||||||
|
return $this->pdo->escapeIdentifier($value);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public function execute($sql, array $values = array())
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
|
||||||
|
$this->setLogMessage($sql);
|
||||||
|
$this->setLogMessage(implode(', ', $values));
|
||||||
|
|
||||||
|
$rq = $this->pdo->prepare($sql);
|
||||||
|
$rq->execute($values);
|
||||||
|
|
||||||
|
return $rq;
|
||||||
|
}
|
||||||
|
catch (\PDOException $e) {
|
||||||
|
|
||||||
|
$this->setLogMessage($e->getMessage());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public function startTransaction()
|
||||||
|
{
|
||||||
|
$this->pdo->beginTransaction();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public function closeTransaction()
|
||||||
|
{
|
||||||
|
$this->pdo->commit();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public function cancelTransaction()
|
||||||
|
{
|
||||||
|
$this->pdo->rollback();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public function table($table_name)
|
||||||
|
{
|
||||||
|
return new Table($this, $table_name);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public function schema()
|
||||||
|
{
|
||||||
|
require_once __DIR__.'/Schema.php';
|
||||||
|
return new Schema($this);
|
||||||
|
}
|
||||||
|
}
|
47
src/vendor/PicoDb/Drivers/Sqlite.php
vendored
Normal file
47
src/vendor/PicoDb/Drivers/Sqlite.php
vendored
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace PicoDb;
|
||||||
|
|
||||||
|
class Sqlite extends \PDO {
|
||||||
|
|
||||||
|
|
||||||
|
public function __construct($filename)
|
||||||
|
{
|
||||||
|
parent::__construct('sqlite:'.$filename);
|
||||||
|
|
||||||
|
$this->exec('PRAGMA foreign_keys = ON');
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public function getSchemaVersion()
|
||||||
|
{
|
||||||
|
$rq = $this->prepare('PRAGMA user_version');
|
||||||
|
$rq->execute();
|
||||||
|
$result = $rq->fetch(\PDO::FETCH_ASSOC);
|
||||||
|
|
||||||
|
if (isset($result['user_version'])) {
|
||||||
|
|
||||||
|
return $result['user_version'];
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public function setSchemaVersion($version)
|
||||||
|
{
|
||||||
|
$this->exec('PRAGMA user_version='.$version);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public function getLastId()
|
||||||
|
{
|
||||||
|
return $this->lastInsertId();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public function escapeIdentifier($value)
|
||||||
|
{
|
||||||
|
return '"'.$value.'"';
|
||||||
|
}
|
||||||
|
}
|
60
src/vendor/PicoDb/Schema.php
vendored
Normal file
60
src/vendor/PicoDb/Schema.php
vendored
Normal file
@ -0,0 +1,60 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace PicoDb;
|
||||||
|
|
||||||
|
class Schema
|
||||||
|
{
|
||||||
|
protected $db = null;
|
||||||
|
|
||||||
|
|
||||||
|
public function __construct(Database $db)
|
||||||
|
{
|
||||||
|
$this->db = $db;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public function check($last_version = 1)
|
||||||
|
{
|
||||||
|
$current_version = $this->db->getConnection()->getSchemaVersion();
|
||||||
|
|
||||||
|
if ($current_version < $last_version) {
|
||||||
|
|
||||||
|
return $this->migrateTo($current_version, $last_version);
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public function migrateTo($current_version, $next_version)
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
|
||||||
|
$this->db->startTransaction();
|
||||||
|
|
||||||
|
for ($i = $current_version + 1; $i <= $next_version; $i++) {
|
||||||
|
|
||||||
|
$function_name = '\Schema\version_'.$i;
|
||||||
|
|
||||||
|
if (function_exists($function_name)) {
|
||||||
|
|
||||||
|
call_user_func($function_name, $this->db->getConnection());
|
||||||
|
$this->db->getConnection()->setSchemaVersion($i);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
|
||||||
|
throw new \LogicException('To execute a database migration, you need to create this function: "'.$function_name.'".');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->db->closeTransaction();
|
||||||
|
}
|
||||||
|
catch (\PDOException $e) {
|
||||||
|
|
||||||
|
$this->db->cancelTransaction();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
343
src/vendor/PicoDb/Table.php
vendored
Normal file
343
src/vendor/PicoDb/Table.php
vendored
Normal file
@ -0,0 +1,343 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace PicoDb;
|
||||||
|
|
||||||
|
class Table
|
||||||
|
{
|
||||||
|
private $table_name = '';
|
||||||
|
private $sql_limit = '';
|
||||||
|
private $sql_offset = '';
|
||||||
|
private $sql_order = '';
|
||||||
|
private $joins = array();
|
||||||
|
private $conditions = array();
|
||||||
|
private $or_conditions = array();
|
||||||
|
private $is_or_condition = false;
|
||||||
|
private $columns = array();
|
||||||
|
private $values = array();
|
||||||
|
|
||||||
|
private $db;
|
||||||
|
|
||||||
|
|
||||||
|
public function __construct(Database $db, $table_name)
|
||||||
|
{
|
||||||
|
$this->db = $db;
|
||||||
|
$this->table_name = $table_name;
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public function save(array $data)
|
||||||
|
{
|
||||||
|
if (! empty($this->conditions)) {
|
||||||
|
|
||||||
|
return $this->update($data);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
|
||||||
|
return $this->insert($data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public function update(array $data)
|
||||||
|
{
|
||||||
|
$columns = array();
|
||||||
|
$values = array();
|
||||||
|
|
||||||
|
foreach ($data as $column => $value) {
|
||||||
|
|
||||||
|
$columns[] = $this->db->escapeIdentifier($column).'=?';
|
||||||
|
$values[] = $value;
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach ($this->values as $value) {
|
||||||
|
|
||||||
|
$values[] = $value;
|
||||||
|
}
|
||||||
|
|
||||||
|
$sql = sprintf(
|
||||||
|
'UPDATE %s SET %s %s',
|
||||||
|
$this->db->escapeIdentifier($this->table_name),
|
||||||
|
implode(', ', $columns),
|
||||||
|
$this->conditions()
|
||||||
|
);
|
||||||
|
|
||||||
|
return false !== $this->db->execute($sql, $values);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public function insert(array $data)
|
||||||
|
{
|
||||||
|
$columns = array();
|
||||||
|
|
||||||
|
foreach ($data as $column => $value) {
|
||||||
|
|
||||||
|
$columns[] = $this->db->escapeIdentifier($column);
|
||||||
|
}
|
||||||
|
|
||||||
|
$sql = sprintf(
|
||||||
|
'INSERT INTO %s (%s) VALUES (%s)',
|
||||||
|
$this->db->escapeIdentifier($this->table_name),
|
||||||
|
implode(', ', $columns),
|
||||||
|
implode(', ', array_fill(0, count($data), '?'))
|
||||||
|
);
|
||||||
|
|
||||||
|
return false !== $this->db->execute($sql, array_values($data));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public function remove()
|
||||||
|
{
|
||||||
|
$sql = sprintf(
|
||||||
|
'DELETE FROM %s %s',
|
||||||
|
$this->db->escapeIdentifier($this->table_name),
|
||||||
|
$this->conditions()
|
||||||
|
);
|
||||||
|
|
||||||
|
return false !== $this->db->execute($sql, $this->values);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public function listing($key, $value)
|
||||||
|
{
|
||||||
|
$this->columns($key, $value);
|
||||||
|
|
||||||
|
$listing = array();
|
||||||
|
$results = $this->findAll();
|
||||||
|
|
||||||
|
if ($results) {
|
||||||
|
|
||||||
|
foreach ($results as $result) {
|
||||||
|
|
||||||
|
$listing[$result[$key]] = $result[$value];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $listing;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public function findAll()
|
||||||
|
{
|
||||||
|
$sql = sprintf(
|
||||||
|
'SELECT %s FROM %s %s %s %s %s %s',
|
||||||
|
empty($this->columns) ? '*' : implode(', ', $this->columns),
|
||||||
|
$this->db->escapeIdentifier($this->table_name),
|
||||||
|
implode(' ', $this->joins),
|
||||||
|
$this->conditions(),
|
||||||
|
$this->sql_order,
|
||||||
|
$this->sql_limit,
|
||||||
|
$this->sql_offset
|
||||||
|
);
|
||||||
|
|
||||||
|
$rq = $this->db->execute($sql, $this->values);
|
||||||
|
|
||||||
|
if (false === $rq) {
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $rq->fetchAll(\PDO::FETCH_ASSOC);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public function findOne()
|
||||||
|
{
|
||||||
|
$this->limit(1);
|
||||||
|
$result = $this->findAll();
|
||||||
|
|
||||||
|
return isset($result[0]) ? $result[0] : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public function count()
|
||||||
|
{
|
||||||
|
$sql = sprintf(
|
||||||
|
'SELECT COUNT(*) AS count FROM %s'.$this->conditions().$this->sql_order.$this->sql_limit.$this->sql_offset,
|
||||||
|
$this->db->escapeIdentifier($this->table_name)
|
||||||
|
);
|
||||||
|
|
||||||
|
$rq = $this->db->execute($sql, $this->values);
|
||||||
|
|
||||||
|
if (false === $rq) {
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
$result = $rq->fetch(\PDO::FETCH_ASSOC);
|
||||||
|
|
||||||
|
return isset($result['count']) ? (int) $result['count'] : 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public function join($table, $foreign_column, $local_column)
|
||||||
|
{
|
||||||
|
$this->joins[] = sprintf(
|
||||||
|
'LEFT JOIN %s ON %s=%s',
|
||||||
|
$this->db->escapeIdentifier($table),
|
||||||
|
$this->db->escapeIdentifier($table).'.'.$this->db->escapeIdentifier($foreign_column),
|
||||||
|
$this->db->escapeIdentifier($this->table_name).'.'.$this->db->escapeIdentifier($local_column)
|
||||||
|
);
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public function conditions()
|
||||||
|
{
|
||||||
|
if (! empty($this->conditions)) {
|
||||||
|
|
||||||
|
return ' WHERE '.implode(' AND ', $this->conditions);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public function addCondition($sql)
|
||||||
|
{
|
||||||
|
if ($this->is_or_condition) {
|
||||||
|
|
||||||
|
$this->or_conditions[] = $sql;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
|
||||||
|
$this->conditions[] = $sql;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public function beginOr()
|
||||||
|
{
|
||||||
|
$this->is_or_condition = true;
|
||||||
|
$this->or_conditions = array();
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public function closeOr()
|
||||||
|
{
|
||||||
|
$this->is_or_condition = false;
|
||||||
|
|
||||||
|
if (! empty($this->or_conditions)) {
|
||||||
|
|
||||||
|
$this->conditions[] = '('.implode(' OR ', $this->or_conditions).')';
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public function asc($column)
|
||||||
|
{
|
||||||
|
$this->sql_order = ' ORDER BY '.$this->db->escapeIdentifier($column).' ASC';
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public function desc($column)
|
||||||
|
{
|
||||||
|
$this->sql_order = ' ORDER BY '.$this->db->escapeIdentifier($column).' DESC';
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public function limit($value)
|
||||||
|
{
|
||||||
|
$this->sql_limit = ' LIMIT '.(int) $value;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public function offset($value)
|
||||||
|
{
|
||||||
|
$this->sql_offset = ' OFFSET '.(int) $value;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public function columns()
|
||||||
|
{
|
||||||
|
$this->columns = \func_get_args();
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public function __call($name, array $arguments)
|
||||||
|
{
|
||||||
|
if (2 !== count($arguments)) {
|
||||||
|
|
||||||
|
throw new \LogicException('You must define a column and a value.');
|
||||||
|
}
|
||||||
|
|
||||||
|
$column = $arguments[0];
|
||||||
|
$sql = '';
|
||||||
|
|
||||||
|
switch ($name) {
|
||||||
|
|
||||||
|
case 'in':
|
||||||
|
if (is_array($arguments[1])) {
|
||||||
|
|
||||||
|
$sql = sprintf(
|
||||||
|
'%s IN (%s)',
|
||||||
|
$this->db->escapeIdentifier($column),
|
||||||
|
implode(', ', array_fill(0, count($arguments[1]), '?'))
|
||||||
|
);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'like':
|
||||||
|
$sql = sprintf('%s LIKE ?', $this->db->escapeIdentifier($column));
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'eq':
|
||||||
|
case 'equal':
|
||||||
|
case 'equals':
|
||||||
|
$sql = sprintf('%s = ?', $this->db->escapeIdentifier($column));
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'gt':
|
||||||
|
case 'greaterThan':
|
||||||
|
$sql = sprintf('%s > ?', $this->db->escapeIdentifier($column));
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'lt':
|
||||||
|
case 'lowerThan':
|
||||||
|
$sql = sprintf('%s < ?', $this->db->escapeIdentifier($column));
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'gte':
|
||||||
|
case 'greaterThanOrEquals':
|
||||||
|
$sql = sprintf('%s >= ?', $this->db->escapeIdentifier($column));
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'lte':
|
||||||
|
case 'lowerThanOrEquals':
|
||||||
|
$sql = sprintf('%s <= ?', $this->db->escapeIdentifier($column));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ('' !== $sql) {
|
||||||
|
|
||||||
|
$this->addCondition($sql);
|
||||||
|
|
||||||
|
if (is_array($arguments[1])) {
|
||||||
|
|
||||||
|
foreach ($arguments[1] as $value) {
|
||||||
|
|
||||||
|
$this->values[] = $value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
|
||||||
|
$this->values[] = $arguments[1];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
}
|
50
src/vendor/PicoFarad/Request.php
vendored
Normal file
50
src/vendor/PicoFarad/Request.php
vendored
Normal file
@ -0,0 +1,50 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace PicoFarad\Request;
|
||||||
|
|
||||||
|
|
||||||
|
function param($name)
|
||||||
|
{
|
||||||
|
return isset($_GET[$name]) ? $_GET[$name] : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function int_param($name)
|
||||||
|
{
|
||||||
|
return isset($_GET[$name]) && ctype_digit($_GET[$name]) ? (int) $_GET[$name] : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function values()
|
||||||
|
{
|
||||||
|
if (! empty($_POST)) {
|
||||||
|
|
||||||
|
return $_POST;
|
||||||
|
}
|
||||||
|
|
||||||
|
$result = json_decode(body(), true);
|
||||||
|
|
||||||
|
if ($result) {
|
||||||
|
|
||||||
|
return $result;
|
||||||
|
}
|
||||||
|
|
||||||
|
return array();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function body()
|
||||||
|
{
|
||||||
|
return file_get_contents('php://input');
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function file_content($name)
|
||||||
|
{
|
||||||
|
if (isset($_FILES[$name])) {
|
||||||
|
|
||||||
|
return file_get_contents($_FILES[$name]['tmp_name']);
|
||||||
|
}
|
||||||
|
|
||||||
|
return '';
|
||||||
|
}
|
115
src/vendor/PicoFarad/Response.php
vendored
Normal file
115
src/vendor/PicoFarad/Response.php
vendored
Normal file
@ -0,0 +1,115 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace PicoFarad\Response;
|
||||||
|
|
||||||
|
|
||||||
|
function force_download($filename)
|
||||||
|
{
|
||||||
|
header('Content-Disposition: attachment; filename="'.$filename.'"');
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function status($status_code)
|
||||||
|
{
|
||||||
|
if (strpos(php_sapi_name(), 'apache') !== false) {
|
||||||
|
|
||||||
|
header('HTTP/1.0 '.$status_code);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
|
||||||
|
header('Status: '.$status_code);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function redirect($url)
|
||||||
|
{
|
||||||
|
header('Location: '.$url);
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function json(array $data, $status_code = 200)
|
||||||
|
{
|
||||||
|
status($status_code);
|
||||||
|
|
||||||
|
header('Content-Type: application/json');
|
||||||
|
echo json_encode($data);
|
||||||
|
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function text($data, $status_code = 200)
|
||||||
|
{
|
||||||
|
status($status_code);
|
||||||
|
|
||||||
|
header('Content-Type: text/plain; charset=utf-8');
|
||||||
|
echo $data;
|
||||||
|
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function html($data, $status_code = 200)
|
||||||
|
{
|
||||||
|
status($status_code);
|
||||||
|
|
||||||
|
header('Content-Type: text/html; charset=utf-8');
|
||||||
|
echo $data;
|
||||||
|
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function xml($data, $status_code = 200)
|
||||||
|
{
|
||||||
|
status($status_code);
|
||||||
|
|
||||||
|
header('Content-Type: text/xml; charset=utf-8');
|
||||||
|
echo $data;
|
||||||
|
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function csp(array $policies = array())
|
||||||
|
{
|
||||||
|
$policies['default-src'] = "'self'";
|
||||||
|
|
||||||
|
foreach (array('X-WebKit-CSP', 'X-Content-Security-Policy', 'Content-Security-Policy') as $header) {
|
||||||
|
|
||||||
|
$values = '';
|
||||||
|
|
||||||
|
foreach ($policies as $policy => $hosts) {
|
||||||
|
|
||||||
|
$values .= $policy.' '.$hosts.'; ';
|
||||||
|
}
|
||||||
|
|
||||||
|
header($header.': '.$values);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function nosniff()
|
||||||
|
{
|
||||||
|
header('X-Content-Type-Options: nosniff');
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function xss()
|
||||||
|
{
|
||||||
|
header('X-XSS-Protection: 1; mode=block');
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function hsts()
|
||||||
|
{
|
||||||
|
header('Strict-Transport-Security: max-age=31536000');
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function xframe($mode = 'DENY', array $urls = array())
|
||||||
|
{
|
||||||
|
header('X-Frame-Options: '.$mode.' '.implode(' ', $urls));
|
||||||
|
}
|
138
src/vendor/PicoFarad/Router.php
vendored
Normal file
138
src/vendor/PicoFarad/Router.php
vendored
Normal file
@ -0,0 +1,138 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace PicoFarad\Router;
|
||||||
|
|
||||||
|
|
||||||
|
function before($value = null)
|
||||||
|
{
|
||||||
|
static $before_callback = null;
|
||||||
|
|
||||||
|
if (is_callable($value)) {
|
||||||
|
|
||||||
|
$before_callback = $value;
|
||||||
|
}
|
||||||
|
else if (is_callable($before_callback)) {
|
||||||
|
|
||||||
|
$before_callback($value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function action($name, \Closure $callback)
|
||||||
|
{
|
||||||
|
$handler = isset($_GET['action']) ? $_GET['action'] : 'default';
|
||||||
|
|
||||||
|
if ($handler === $name) {
|
||||||
|
|
||||||
|
before($handler);
|
||||||
|
$callback();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function post_action($name, \Closure $callback)
|
||||||
|
{
|
||||||
|
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||||
|
|
||||||
|
action($name, $callback);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function get_action($name, \Closure $callback)
|
||||||
|
{
|
||||||
|
if ($_SERVER['REQUEST_METHOD'] === 'GET') {
|
||||||
|
|
||||||
|
action($name, $callback);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function notfound(\Closure $callback)
|
||||||
|
{
|
||||||
|
before();
|
||||||
|
$callback();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function get($url, \Closure $callback)
|
||||||
|
{
|
||||||
|
find_route('GET', $url, $callback);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function post($url, \Closure $callback)
|
||||||
|
{
|
||||||
|
find_route('POST', $url, $callback);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function put($url, \Closure $callback)
|
||||||
|
{
|
||||||
|
find_route('PUT', $url, $callback);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function delete($url, \Closure $callback)
|
||||||
|
{
|
||||||
|
find_route('DELETE', $url, $callback);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function find_route($method, $route, \Closure $callback)
|
||||||
|
{
|
||||||
|
if ($_SERVER['REQUEST_METHOD'] === $method) {
|
||||||
|
|
||||||
|
if ($_SERVER['QUERY_STRING']) {
|
||||||
|
|
||||||
|
$url = substr($_SERVER['REQUEST_URI'], 0, -(strlen($_SERVER['QUERY_STRING']) + 1));
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
|
||||||
|
$url = $_SERVER['REQUEST_URI'];
|
||||||
|
}
|
||||||
|
|
||||||
|
$params = array();
|
||||||
|
|
||||||
|
if (url_match($route, $url, $params)) {
|
||||||
|
|
||||||
|
before($handler);
|
||||||
|
\call_user_func_array($callback, $params);
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function url_match($route_uri, $request_uri, array &$params)
|
||||||
|
{
|
||||||
|
if ($request_uri === $route_uri) return true;
|
||||||
|
if ($route_uri === '/' || $request_uri === '/') return false;
|
||||||
|
|
||||||
|
$route_uri = trim($route_uri, '/');
|
||||||
|
$request_uri = trim($request_uri, '/');
|
||||||
|
|
||||||
|
$route_items = explode('/', $route_uri);
|
||||||
|
$request_items = explode('/', $request_uri);
|
||||||
|
$nb_route_items = count($route_items);
|
||||||
|
|
||||||
|
if ($nb_route_items === count($request_items)) {
|
||||||
|
|
||||||
|
for ($i = 0; $i < $nb_route_items; ++$i) {
|
||||||
|
|
||||||
|
if ($route_items[$i][0] === ':') {
|
||||||
|
|
||||||
|
$params[substr($route_items[$i], 1)] = $request_items[$i];
|
||||||
|
}
|
||||||
|
else if ($route_items[$i] !== $request_items[$i]) {
|
||||||
|
|
||||||
|
$params = array();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
30
src/vendor/PicoFarad/Session.php
vendored
Normal file
30
src/vendor/PicoFarad/Session.php
vendored
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace PicoFarad\Session;
|
||||||
|
|
||||||
|
const SESSION_LIFETIME = 2678400;
|
||||||
|
|
||||||
|
|
||||||
|
function open($base_path = '/')
|
||||||
|
{
|
||||||
|
session_set_cookie_params(SESSION_LIFETIME, $base_path, null, false, true);
|
||||||
|
session_start();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function close()
|
||||||
|
{
|
||||||
|
session_destroy();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function flash($message)
|
||||||
|
{
|
||||||
|
$_SESSION['flash_message'] = $message;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function flash_error($message)
|
||||||
|
{
|
||||||
|
$_SESSION['flash_error_message'] = $message;
|
||||||
|
}
|
39
src/vendor/PicoFeed/Export.php
vendored
Normal file
39
src/vendor/PicoFeed/Export.php
vendored
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace PicoFeed;
|
||||||
|
|
||||||
|
class Export
|
||||||
|
{
|
||||||
|
private $content = array();
|
||||||
|
|
||||||
|
|
||||||
|
public function __construct(array $content)
|
||||||
|
{
|
||||||
|
$this->content = $content;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public function execute()
|
||||||
|
{
|
||||||
|
$xml = new \SimpleXMLElement('<?xml version="1.0" encoding="utf-8"?><opml/>');
|
||||||
|
|
||||||
|
$head = $xml->addChild('head');
|
||||||
|
$head->addChild('title', 'OPML Export');
|
||||||
|
|
||||||
|
$body = $xml->addChild('body');
|
||||||
|
|
||||||
|
foreach ($this->content as $feed) {
|
||||||
|
|
||||||
|
$outline = $body->addChild('outline');
|
||||||
|
$outline->addAttribute('xmlUrl', $feed['feed_url']);
|
||||||
|
$outline->addAttribute('htmlUrl', $feed['site_url']);
|
||||||
|
$outline->addAttribute('title', $feed['title']);
|
||||||
|
$outline->addAttribute('text', $feed['title']);
|
||||||
|
$outline->addAttribute('description', isset($feed['description']) ? $feed['description'] : $feed['title']);
|
||||||
|
$outline->addAttribute('type', 'rss');
|
||||||
|
$outline->addAttribute('version', 'RSS');
|
||||||
|
}
|
||||||
|
|
||||||
|
return $xml->asXML();
|
||||||
|
}
|
||||||
|
}
|
271
src/vendor/PicoFeed/Filter.php
vendored
Normal file
271
src/vendor/PicoFeed/Filter.php
vendored
Normal file
@ -0,0 +1,271 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace PicoFeed;
|
||||||
|
|
||||||
|
class Filter
|
||||||
|
{
|
||||||
|
private $data = '';
|
||||||
|
private $url = '';
|
||||||
|
private $input = '';
|
||||||
|
private $empty_tag = false;
|
||||||
|
private $strip_content = false;
|
||||||
|
|
||||||
|
public $ignored_tags = array();
|
||||||
|
|
||||||
|
public $allowed_tags = array(
|
||||||
|
'h2' => array(),
|
||||||
|
'h3' => array(),
|
||||||
|
'h4' => array(),
|
||||||
|
'h5' => array(),
|
||||||
|
'h6' => array(),
|
||||||
|
'strong' => array(),
|
||||||
|
'em' => array(),
|
||||||
|
'code' => array(),
|
||||||
|
'pre' => array(),
|
||||||
|
'blockquote' => array(),
|
||||||
|
'p' => array(),
|
||||||
|
'ul' => array(),
|
||||||
|
'li' => array(),
|
||||||
|
'ol' => array(),
|
||||||
|
'br' => array(),
|
||||||
|
'del' => array(),
|
||||||
|
'a' => array('href'),
|
||||||
|
'img' => array('src', 'width', 'height')
|
||||||
|
);
|
||||||
|
|
||||||
|
public $strip_tags_content = array(
|
||||||
|
'script'
|
||||||
|
);
|
||||||
|
|
||||||
|
public $allowed_protocols = array(
|
||||||
|
'http://',
|
||||||
|
'https://',
|
||||||
|
'ftp://',
|
||||||
|
'mailto://',
|
||||||
|
'//'
|
||||||
|
);
|
||||||
|
|
||||||
|
public $protocol_attributes = array(
|
||||||
|
'src',
|
||||||
|
'href',
|
||||||
|
);
|
||||||
|
|
||||||
|
public $blacklist_media = array(
|
||||||
|
'feeds.feedburner.com',
|
||||||
|
'feedsportal.com',
|
||||||
|
'rss.nytimes.com',
|
||||||
|
'feeds.wordpress.com',
|
||||||
|
'stats.wordpress.com'
|
||||||
|
);
|
||||||
|
|
||||||
|
public $required_attributes = array(
|
||||||
|
'a' => array('href'),
|
||||||
|
'img' => array('src')
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
public function __construct($data, $url)
|
||||||
|
{
|
||||||
|
$this->url = $url;
|
||||||
|
$data = iconv("UTF-8", "ISO-8859-15//IGNORE", $data);
|
||||||
|
|
||||||
|
$dom = new \DOMDocument();
|
||||||
|
$dom->loadHTML($data);
|
||||||
|
$this->input = $dom->saveXML($dom->getElementsByTagName('body')->item(0));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public function execute()
|
||||||
|
{
|
||||||
|
$parser = xml_parser_create();
|
||||||
|
xml_set_object($parser, $this);
|
||||||
|
xml_set_element_handler($parser, 'startTag', 'endTag');
|
||||||
|
xml_set_character_data_handler($parser, 'dataTag');
|
||||||
|
xml_parser_set_option($parser, XML_OPTION_CASE_FOLDING, false);
|
||||||
|
|
||||||
|
if (! xml_parse($parser, $this->input, true)) {
|
||||||
|
|
||||||
|
var_dump($this->input);
|
||||||
|
die(xml_get_current_line_number($parser).'|'.xml_error_string(xml_get_error_code($parser)));
|
||||||
|
}
|
||||||
|
|
||||||
|
xml_parser_free($parser);
|
||||||
|
|
||||||
|
return $this->data;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public function startTag($parser, $name, $attributes)
|
||||||
|
{
|
||||||
|
$this->empty_tag = false;
|
||||||
|
$this->strip_content = false;
|
||||||
|
|
||||||
|
if ($this->isPixelTracker($name, $attributes)) {
|
||||||
|
|
||||||
|
$this->empty_tag = true;
|
||||||
|
}
|
||||||
|
else if ($this->isAllowedTag($name)) {
|
||||||
|
|
||||||
|
$attr_data = '';
|
||||||
|
$used_attributes = array();
|
||||||
|
|
||||||
|
foreach ($attributes as $attribute => $value) {
|
||||||
|
|
||||||
|
if ($this->isAllowedAttribute($name, $attribute)) {
|
||||||
|
|
||||||
|
if ($this->isResource($attribute)) {
|
||||||
|
|
||||||
|
if ($this->isRelativePath($value)) {
|
||||||
|
|
||||||
|
$attr_data .= ' '.$attribute.'="'.$this->getAbsoluteUrl($value, $this->url).'"';
|
||||||
|
$used_attributes[] = $attribute;
|
||||||
|
}
|
||||||
|
else if ($this->isAllowedProtocol($value) && ! $this->isBlacklistMedia($value)) {
|
||||||
|
|
||||||
|
$attr_data .= ' '.$attribute.'="'.$value.'"';
|
||||||
|
$used_attributes[] = $attribute;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
|
||||||
|
$attr_data .= ' '.$attribute.'="'.$value.'"';
|
||||||
|
$used_attributes[] = $attribute;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isset($this->required_attributes[$name])) {
|
||||||
|
|
||||||
|
foreach ($this->required_attributes[$name] as $required_attribute) {
|
||||||
|
|
||||||
|
if (! in_array($required_attribute, $used_attributes)) {
|
||||||
|
|
||||||
|
$this->empty_tag = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (! $this->empty_tag) {
|
||||||
|
|
||||||
|
$this->data .= '<'.$name.$attr_data;
|
||||||
|
|
||||||
|
if ($name !== 'img' && $name !== 'br') $this->data .= '>';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
|
||||||
|
$this->ignored_tags[] = $name;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (in_array($name, $this->strip_tags_content)) {
|
||||||
|
|
||||||
|
$this->strip_content = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public function endTag($parser, $name)
|
||||||
|
{
|
||||||
|
if (! $this->empty_tag && $this->isAllowedTag($name)) {
|
||||||
|
|
||||||
|
$this->data .= $name !== 'img' && $name !== 'br' ? '</'.$name.'>' : '/>';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public function dataTag($parser, $content)
|
||||||
|
{
|
||||||
|
if (! $this->strip_content) $this->data .= htmlspecialchars($content, ENT_QUOTES, 'UTF-8', false);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public function getAbsoluteUrl($path, $url)
|
||||||
|
{
|
||||||
|
$components = parse_url($url);
|
||||||
|
|
||||||
|
if ($path{0} === '/') {
|
||||||
|
|
||||||
|
// Absolute path
|
||||||
|
return $components['scheme'].'://'.$components['host'].$path;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
|
||||||
|
// Relative path
|
||||||
|
|
||||||
|
$url_path = $components['path'];
|
||||||
|
|
||||||
|
if ($url_path{strlen($url_path) - 1} !== '/') {
|
||||||
|
|
||||||
|
$url_path = dirname($url_path).'/';
|
||||||
|
}
|
||||||
|
|
||||||
|
if (substr($path, 0, 2) === './') {
|
||||||
|
|
||||||
|
$path = substr($path, 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $components['scheme'].'://'.$components['host'].$url_path.$path;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public function isRelativePath($value)
|
||||||
|
{
|
||||||
|
return strpos($value, '://') === false && strpos($value, '//') !== 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public function isAllowedTag($name)
|
||||||
|
{
|
||||||
|
return isset($this->allowed_tags[$name]);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public function isAllowedAttribute($tag, $attribute)
|
||||||
|
{
|
||||||
|
return in_array($attribute, $this->allowed_tags[$tag]);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public function isResource($attribute)
|
||||||
|
{
|
||||||
|
return in_array($attribute, $this->protocol_attributes);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public function isAllowedProtocol($value)
|
||||||
|
{
|
||||||
|
foreach ($this->allowed_protocols as $protocol) {
|
||||||
|
|
||||||
|
if (strpos($value, $protocol) === 0) {
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public function isBlacklistMedia($resource)
|
||||||
|
{
|
||||||
|
foreach ($this->blacklist_media as $name) {
|
||||||
|
|
||||||
|
if (strpos($resource, $name) !== false) {
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public function isPixelTracker($tag, array $attributes)
|
||||||
|
{
|
||||||
|
return $tag === 'img' &&
|
||||||
|
isset($attributes['height']) && isset($attributes['width']) &&
|
||||||
|
$attributes['height'] == 1 && $attributes['width'] == 1;
|
||||||
|
}
|
||||||
|
}
|
61
src/vendor/PicoFeed/Import.php
vendored
Normal file
61
src/vendor/PicoFeed/Import.php
vendored
Normal file
@ -0,0 +1,61 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace PicoFeed;
|
||||||
|
|
||||||
|
class Import
|
||||||
|
{
|
||||||
|
private $content = '';
|
||||||
|
private $items = array();
|
||||||
|
|
||||||
|
|
||||||
|
public function __construct($content)
|
||||||
|
{
|
||||||
|
$this->content = $content;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public function execute()
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
|
||||||
|
\libxml_use_internal_errors(true);
|
||||||
|
|
||||||
|
$xml = new \SimpleXMLElement(trim($this->content));
|
||||||
|
|
||||||
|
if ($xml->getName() !== 'opml') {
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->parseEntries($xml->body);
|
||||||
|
}
|
||||||
|
catch (\Exception $e) {
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->items;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public function parseEntries($tree)
|
||||||
|
{
|
||||||
|
foreach ($tree->outline as $item) {
|
||||||
|
|
||||||
|
if (isset($item['type']) && strtolower($item['type']) === 'folder' && isset($item->outline)) {
|
||||||
|
|
||||||
|
$this->parseEntries($item);
|
||||||
|
}
|
||||||
|
else if (isset($item['type']) && strtolower($item['type']) === 'rss') {
|
||||||
|
|
||||||
|
$entry = new \StdClass;
|
||||||
|
$entry->title = (string) $item['text'];
|
||||||
|
$entry->site_url = (string) $item['htmlUrl'];
|
||||||
|
$entry->feed_url = (string) $item['xmlUrl'];
|
||||||
|
$entry->type = isset($item['version']) ? (string) $item['version'] : (string) $item['type'];
|
||||||
|
$entry->description = (string) $item['description'];
|
||||||
|
$this->items[] = $entry;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
182
src/vendor/PicoFeed/Parser.php
vendored
Normal file
182
src/vendor/PicoFeed/Parser.php
vendored
Normal file
@ -0,0 +1,182 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace PicoFeed;
|
||||||
|
|
||||||
|
require_once __DIR__.'/Filter.php';
|
||||||
|
|
||||||
|
|
||||||
|
abstract class Parser
|
||||||
|
{
|
||||||
|
protected $content = '';
|
||||||
|
|
||||||
|
public $id = '';
|
||||||
|
public $url = '';
|
||||||
|
public $title = '';
|
||||||
|
public $updated = '';
|
||||||
|
public $items = array();
|
||||||
|
|
||||||
|
|
||||||
|
abstract public function execute();
|
||||||
|
|
||||||
|
|
||||||
|
public function __construct($content)
|
||||||
|
{
|
||||||
|
$this->content = $content;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public function filterHtml($str, $item_url)
|
||||||
|
{
|
||||||
|
$content = '';
|
||||||
|
|
||||||
|
if ($str) {
|
||||||
|
|
||||||
|
$filter = new Filter($str, $item_url);
|
||||||
|
$content = $filter->execute();
|
||||||
|
}
|
||||||
|
|
||||||
|
return $content;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
class Atom extends Parser
|
||||||
|
{
|
||||||
|
public function execute()
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
|
||||||
|
\libxml_use_internal_errors(true);
|
||||||
|
|
||||||
|
$xml = new \SimpleXMLElement($this->content);
|
||||||
|
|
||||||
|
$this->url = $this->getUrl($xml);
|
||||||
|
$this->title = (string) $xml->title;
|
||||||
|
$this->id = (string) $xml->id;
|
||||||
|
$this->updated = strtotime((string) $xml->updated);
|
||||||
|
$author = (string) $xml->author->name;
|
||||||
|
|
||||||
|
foreach ($xml->entry as $entry) {
|
||||||
|
|
||||||
|
if (isset($entry->author->name)) {
|
||||||
|
|
||||||
|
$author = $entry->author->name;
|
||||||
|
}
|
||||||
|
|
||||||
|
$item = new \StdClass;
|
||||||
|
$item->id = (string) $entry->id;
|
||||||
|
$item->title = (string) $entry->title;
|
||||||
|
$item->url = $this->getUrl($entry);
|
||||||
|
$item->updated = strtotime((string) $entry->updated);
|
||||||
|
$item->author = $author;
|
||||||
|
$item->content = $this->filterHtml($this->getContent($entry), $item->url);
|
||||||
|
|
||||||
|
$this->items[] = $item;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (\Exception $e) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public function getContent($entry)
|
||||||
|
{
|
||||||
|
if (isset($entry->content) && ! empty($entry->content)) {
|
||||||
|
|
||||||
|
if (count($entry->content->children())) {
|
||||||
|
|
||||||
|
return (string) $entry->content->asXML();
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
|
||||||
|
return (string) $entry->content;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (isset($entry->summary) && ! empty($entry->summary)) {
|
||||||
|
|
||||||
|
return (string) $entry->summary;
|
||||||
|
}
|
||||||
|
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public function getUrl($xml)
|
||||||
|
{
|
||||||
|
foreach ($xml->link as $link) {
|
||||||
|
|
||||||
|
if ((string) $link['type'] === 'text/html') {
|
||||||
|
|
||||||
|
return (string) $link['href'];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return (string) $xml->link['href'];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
class Rss20 extends Parser
|
||||||
|
{
|
||||||
|
public function execute()
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
|
||||||
|
\libxml_use_internal_errors(true);
|
||||||
|
|
||||||
|
$xml = new \SimpleXMLElement($this->content);
|
||||||
|
$ns = $xml->getNamespaces(true);
|
||||||
|
|
||||||
|
$this->title = (string) $xml->channel->title;
|
||||||
|
$this->url = (string) $xml->channel->link;
|
||||||
|
$this->id = $this->url;
|
||||||
|
$this->updated = isset($xml->channel->pubDate) ? (string) $xml->channel->pubDate : (string) $xml->channel->lastBuildDate;
|
||||||
|
$this->updated = strtotime($this->updated);
|
||||||
|
|
||||||
|
foreach ($xml->channel->item as $entry) {
|
||||||
|
|
||||||
|
$author = '';
|
||||||
|
$content = '';
|
||||||
|
|
||||||
|
if (isset($ns['dc'])) {
|
||||||
|
|
||||||
|
$ns_dc = $entry->children($ns['dc']);
|
||||||
|
$author = (string) $ns_dc->creator;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isset($ns['content'])) {
|
||||||
|
|
||||||
|
$ns_content = $entry->children($ns['content']);
|
||||||
|
|
||||||
|
if (! empty($entry->content)) {
|
||||||
|
|
||||||
|
$content = (string) $ns_content->encoded;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (! $content) {
|
||||||
|
|
||||||
|
$content = (string) $entry->description;
|
||||||
|
}
|
||||||
|
|
||||||
|
$item = new \StdClass;
|
||||||
|
$item->id = (string) $entry->guid;
|
||||||
|
$item->title = (string) $entry->title;
|
||||||
|
$item->url = (string) $entry->link;
|
||||||
|
$item->updated = strtotime((string) $entry->pubDate);
|
||||||
|
$item->content = $this->filterHtml($content, $item->url);
|
||||||
|
$item->author = $author ?: (string) $xml->channel->webMaster;
|
||||||
|
|
||||||
|
$this->items[] = $item;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (\Exception $e) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
}
|
117
src/vendor/PicoFeed/Reader.php
vendored
Normal file
117
src/vendor/PicoFeed/Reader.php
vendored
Normal file
@ -0,0 +1,117 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace PicoFeed;
|
||||||
|
|
||||||
|
class Reader
|
||||||
|
{
|
||||||
|
private $url = '';
|
||||||
|
private $content = '';
|
||||||
|
|
||||||
|
|
||||||
|
public function __construct($content = '')
|
||||||
|
{
|
||||||
|
$this->content = $content;
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public function download($url)
|
||||||
|
{
|
||||||
|
if (strpos($url, 'http') !== 0) {
|
||||||
|
|
||||||
|
$url = 'http://'.$url;
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->url = $url;
|
||||||
|
$this->content = @file_get_contents($this->url);
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public function getContent()
|
||||||
|
{
|
||||||
|
return $this->content;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public function getUrl()
|
||||||
|
{
|
||||||
|
return $this->url;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public function getParser()
|
||||||
|
{
|
||||||
|
$first_lines = substr($this->content, 0, 512);
|
||||||
|
|
||||||
|
if (stripos($first_lines, 'html') !== false) {
|
||||||
|
|
||||||
|
if ($this->discover()) {
|
||||||
|
|
||||||
|
$first_lines = substr($this->content, 0, 512);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (strpos($first_lines, '<feed ') !== false) {
|
||||||
|
|
||||||
|
return new Atom($this->content);
|
||||||
|
}
|
||||||
|
else if (strpos($first_lines, '<rss ') !== false && strpos($first_lines, 'version="2.0"') !== false) {
|
||||||
|
|
||||||
|
return new Rss20($this->content);
|
||||||
|
}/*
|
||||||
|
else if (strpos($first_lines, '<rdf') !== false && strpos($first_lines, 'xmlns="http://purl.org/rss/1.0/"') !== false) {
|
||||||
|
|
||||||
|
return new Rss10($this->content);
|
||||||
|
}*/
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public function discover()
|
||||||
|
{
|
||||||
|
\libxml_use_internal_errors(true);
|
||||||
|
|
||||||
|
$dom = new \DOMDocument;
|
||||||
|
$dom->loadHTML($this->content);
|
||||||
|
|
||||||
|
$xpath = new \DOMXPath($dom);
|
||||||
|
|
||||||
|
$queries = array(
|
||||||
|
"//link[@type='application/atom+xml']",
|
||||||
|
"//link[@type='application/rss+xml']"
|
||||||
|
);
|
||||||
|
|
||||||
|
foreach ($queries as $query) {
|
||||||
|
|
||||||
|
$nodes = $xpath->query($query);
|
||||||
|
|
||||||
|
if ($nodes->length !== 0) {
|
||||||
|
|
||||||
|
$link = $nodes->item(0)->getAttribute('href');
|
||||||
|
|
||||||
|
// Relative links
|
||||||
|
if (strpos($link, 'http') !== 0) {
|
||||||
|
|
||||||
|
if ($link{0} === '/') $link = substr($link, 1);
|
||||||
|
if ($this->url{strlen($this->url) - 1} !== '/') $this->url .= '/';
|
||||||
|
|
||||||
|
$link = $this->url.$link;
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->download($link);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
0
src/vendor/PicoFeed/Writer.php
vendored
Normal file
0
src/vendor/PicoFeed/Writer.php
vendored
Normal file
100
src/vendor/PicoTools/Chrono.php
vendored
Normal file
100
src/vendor/PicoTools/Chrono.php
vendored
Normal file
@ -0,0 +1,100 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This file is part of picoTools.
|
||||||
|
*
|
||||||
|
* (c) Frédéric Guillot http://fredericguillot.com
|
||||||
|
*
|
||||||
|
* For the full copyright and license information, please view the LICENSE
|
||||||
|
* file that was distributed with this source code.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace PicoTools;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Chrono, tool for benchmarking
|
||||||
|
* Calculate the duration of your code
|
||||||
|
*
|
||||||
|
* @author Frédéric Guillot
|
||||||
|
*/
|
||||||
|
class Chrono
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Chronos values
|
||||||
|
*
|
||||||
|
* @access private
|
||||||
|
* @static
|
||||||
|
* @var array
|
||||||
|
*/
|
||||||
|
private static $chronos = array();
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Start a chrono
|
||||||
|
*
|
||||||
|
* @access public
|
||||||
|
* @static
|
||||||
|
* @param string $name Chrono name
|
||||||
|
*/
|
||||||
|
public static function start($name)
|
||||||
|
{
|
||||||
|
self::$chronos[$name] = array(
|
||||||
|
'start' => microtime(true),
|
||||||
|
'finish' => 0
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Stop a chrono
|
||||||
|
*
|
||||||
|
* @access public
|
||||||
|
* @static
|
||||||
|
* @param string $name Chrono name
|
||||||
|
*/
|
||||||
|
public static function stop($name)
|
||||||
|
{
|
||||||
|
if (! isset(self::$chronos[$name])) {
|
||||||
|
|
||||||
|
throw new \RuntimeException('Chrono not started!');
|
||||||
|
}
|
||||||
|
|
||||||
|
self::$chronos[$name]['finish'] = microtime(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get a duration of a chrono
|
||||||
|
*
|
||||||
|
* @access public
|
||||||
|
* @static
|
||||||
|
* @return float
|
||||||
|
*/
|
||||||
|
public static function duration($name)
|
||||||
|
{
|
||||||
|
if (! isset(self::$chronos[$name])) {
|
||||||
|
|
||||||
|
throw new \RuntimeException('Chrono not started!');
|
||||||
|
}
|
||||||
|
|
||||||
|
return self::$chronos[$name]['finish'] - self::$chronos[$name]['start'];
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Show all durations
|
||||||
|
*
|
||||||
|
* @access public
|
||||||
|
* @static
|
||||||
|
*/
|
||||||
|
public static function show()
|
||||||
|
{
|
||||||
|
foreach (self::$chronos as $name => $values) {
|
||||||
|
|
||||||
|
echo $name.' = ';
|
||||||
|
echo round($values['finish'] - $values['start'], 2).'s';
|
||||||
|
echo PHP_EOL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
175
src/vendor/PicoTools/Command.php
vendored
Normal file
175
src/vendor/PicoTools/Command.php
vendored
Normal file
@ -0,0 +1,175 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This file is part of picoTools.
|
||||||
|
*
|
||||||
|
* (c) Frédéric Guillot http://fredericguillot.com
|
||||||
|
*
|
||||||
|
* For the full copyright and license information, please view the LICENSE
|
||||||
|
* file that was distributed with this source code.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace PicoTools;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Execute an external command
|
||||||
|
*
|
||||||
|
* @author Frédéric Guillot
|
||||||
|
*/
|
||||||
|
class Command
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Command line
|
||||||
|
*
|
||||||
|
* @access private
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
private $cmd_line = '';
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Command stdout
|
||||||
|
*
|
||||||
|
* @access private
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
private $cmd_stdout = '';
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Command stderr
|
||||||
|
*
|
||||||
|
* @access private
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
private $cmd_sdterr = '';
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Command environements variables
|
||||||
|
*
|
||||||
|
* @access private
|
||||||
|
* @var array
|
||||||
|
*/
|
||||||
|
private $cmd_env = array();
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Command working directory
|
||||||
|
*
|
||||||
|
* @access private
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
private $cmd_dir = null;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Command return value
|
||||||
|
*
|
||||||
|
* @access private
|
||||||
|
* @var integer
|
||||||
|
*/
|
||||||
|
private $cmd_return = 0;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor
|
||||||
|
*
|
||||||
|
* @access public
|
||||||
|
* @param string $command Command line
|
||||||
|
*/
|
||||||
|
public function __construct($command)
|
||||||
|
{
|
||||||
|
$this->cmd_line = $command;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Execute the command
|
||||||
|
*
|
||||||
|
* @access public
|
||||||
|
*/
|
||||||
|
public function execute()
|
||||||
|
{
|
||||||
|
$process = proc_open(
|
||||||
|
$this->cmd_line,
|
||||||
|
array(
|
||||||
|
0 => array('pipe', 'r'),
|
||||||
|
1 => array('pipe', 'w'),
|
||||||
|
2 => array('pipe', 'w')
|
||||||
|
),
|
||||||
|
$pipes,
|
||||||
|
$this->cmd_dir,
|
||||||
|
$this->cmd_env
|
||||||
|
);
|
||||||
|
|
||||||
|
if (is_resource($process)) {
|
||||||
|
|
||||||
|
$this->cmd_stdout = stream_get_contents($pipes[1]);
|
||||||
|
$this->cmd_stderr = stream_get_contents($pipes[2]);
|
||||||
|
$this->cmd_return = proc_close($process);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set working directory
|
||||||
|
*
|
||||||
|
* @access public
|
||||||
|
* @param string $dir Working directory
|
||||||
|
*/
|
||||||
|
public function setDir($dir)
|
||||||
|
{
|
||||||
|
$this->cmd_dir = $dir;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set command env variables
|
||||||
|
*
|
||||||
|
* @access public
|
||||||
|
* @param array $env Environnement variables
|
||||||
|
*/
|
||||||
|
public function setEnv(array $env)
|
||||||
|
{
|
||||||
|
$this->cmd_env = $env;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the return value
|
||||||
|
*
|
||||||
|
* @access public
|
||||||
|
* @return integer Return value
|
||||||
|
*/
|
||||||
|
public function getReturnValue()
|
||||||
|
{
|
||||||
|
return $this->cmd_return;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get stdout
|
||||||
|
*
|
||||||
|
* @access public
|
||||||
|
* @return string stdout
|
||||||
|
*/
|
||||||
|
public function getStdout()
|
||||||
|
{
|
||||||
|
return $this->cmd_stdout;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get stderr
|
||||||
|
*
|
||||||
|
* @access public
|
||||||
|
* @return string stderr
|
||||||
|
*/
|
||||||
|
public function getStderr()
|
||||||
|
{
|
||||||
|
return $this->cmd_stderr;
|
||||||
|
}
|
||||||
|
}
|
97
src/vendor/PicoTools/Config.php
vendored
Normal file
97
src/vendor/PicoTools/Config.php
vendored
Normal file
@ -0,0 +1,97 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This file is part of PicoTools.
|
||||||
|
*
|
||||||
|
* (c) Frédéric Guillot http://fredericguillot.com
|
||||||
|
*
|
||||||
|
* For the full copyright and license information, please view the LICENSE
|
||||||
|
* file that was distributed with this source code.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace PicoTools;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handle configuration parameters
|
||||||
|
*
|
||||||
|
* @author Frédéric Guillot
|
||||||
|
*/
|
||||||
|
class Config
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Container
|
||||||
|
*
|
||||||
|
* @access private
|
||||||
|
* @static
|
||||||
|
* @var array
|
||||||
|
*/
|
||||||
|
private static $container = array();
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set a new configuration parameter
|
||||||
|
*
|
||||||
|
* @access public
|
||||||
|
* @static
|
||||||
|
* @param string $name Parameter name
|
||||||
|
* @param string $value Parameter value
|
||||||
|
*/
|
||||||
|
public static function set($name, $value)
|
||||||
|
{
|
||||||
|
self::$container[$name] = $value;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fetch a parameter value
|
||||||
|
*
|
||||||
|
* @access public
|
||||||
|
* @static
|
||||||
|
* @param string $name Parameter name
|
||||||
|
* @param string $defaultValue Default parameter value
|
||||||
|
*/
|
||||||
|
public static function get($name, $defaultValue = null)
|
||||||
|
{
|
||||||
|
return isset(self::$container[$name]) ? self::$container[$name] : $defaultValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Load a PHP config file
|
||||||
|
*
|
||||||
|
* @access public
|
||||||
|
* @static
|
||||||
|
*/
|
||||||
|
public static function load($env = null)
|
||||||
|
{
|
||||||
|
if ($env !== null) {
|
||||||
|
|
||||||
|
$filename = 'config/'.$env.'.php';
|
||||||
|
|
||||||
|
if (file_exists($filename)) {
|
||||||
|
|
||||||
|
require $filename;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
|
||||||
|
throw new \RuntimeException('Unable to load the config file: '.$filename);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
|
||||||
|
if (file_exists('config/prod.php')) {
|
||||||
|
|
||||||
|
require 'config/prod.php';
|
||||||
|
}
|
||||||
|
else if (file_exists('config/dev.php')) {
|
||||||
|
|
||||||
|
require 'config/dev.php';
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
|
||||||
|
throw new \RuntimeException('No config file loaded.');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
150
src/vendor/PicoTools/Crypto.php
vendored
Normal file
150
src/vendor/PicoTools/Crypto.php
vendored
Normal file
@ -0,0 +1,150 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This file is part of PicoTools.
|
||||||
|
*
|
||||||
|
* (c) Frédéric Guillot http://fredericguillot.com
|
||||||
|
*
|
||||||
|
* For the full copyright and license information, please view the LICENSE
|
||||||
|
* file that was distributed with this source code.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace PicoTools\Crypto;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generate a random token
|
||||||
|
*
|
||||||
|
* @return string Random token
|
||||||
|
*/
|
||||||
|
function token()
|
||||||
|
{
|
||||||
|
return hash('sha256', uniqid('', true).microtime());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generate a signature with a key
|
||||||
|
*
|
||||||
|
* @param string $data Data
|
||||||
|
* @param string $key Key
|
||||||
|
* @return string Signature
|
||||||
|
*/
|
||||||
|
function signature($data, $key)
|
||||||
|
{
|
||||||
|
return hash_hmac('sha256', $data, $key);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Import of the PHP 5.5 password hashing method
|
||||||
|
// https://github.com/ircmaxell/password_compat/blob/master/lib/password.php
|
||||||
|
|
||||||
|
function password_verify($password, $hash)
|
||||||
|
{
|
||||||
|
$ret = crypt($password, $hash);
|
||||||
|
|
||||||
|
if (! is_string($ret) || strlen($ret) != strlen($hash) || strlen($ret) <= 13) {
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
$status = 0;
|
||||||
|
|
||||||
|
for ($i = 0; $i < strlen($ret); $i++) {
|
||||||
|
|
||||||
|
$status |= (ord($ret[$i]) ^ ord($hash[$i]));
|
||||||
|
}
|
||||||
|
|
||||||
|
return $status === 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function password_hash($password)
|
||||||
|
{
|
||||||
|
$cost = 10;
|
||||||
|
$required_salt_len = 22;
|
||||||
|
$hash_format = sprintf("$2y$%02d$", $cost);
|
||||||
|
|
||||||
|
$hash = $hash_format.password_salt($required_salt_len);
|
||||||
|
|
||||||
|
$ret = crypt($password, $hash);
|
||||||
|
|
||||||
|
if (! is_string($ret) || strlen($ret) <= 13) {
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function password_salt($required_salt_len)
|
||||||
|
{
|
||||||
|
$buffer = '';
|
||||||
|
$raw_length = (int) ($required_salt_len * 3 / 4 + 1);
|
||||||
|
$buffer_valid = false;
|
||||||
|
|
||||||
|
if (function_exists('mcrypt_create_iv')) {
|
||||||
|
|
||||||
|
$buffer = mcrypt_create_iv($raw_length, MCRYPT_DEV_URANDOM);
|
||||||
|
|
||||||
|
if ($buffer) {
|
||||||
|
|
||||||
|
$buffer_valid = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (! $buffer_valid && function_exists('openssl_random_pseudo_bytes')) {
|
||||||
|
|
||||||
|
$buffer = openssl_random_pseudo_bytes($raw_length);
|
||||||
|
|
||||||
|
if ($buffer) {
|
||||||
|
|
||||||
|
$buffer_valid = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (! $buffer_valid && file_exists('/dev/urandom')) {
|
||||||
|
|
||||||
|
$f = @fopen('/dev/urandom', 'r');
|
||||||
|
|
||||||
|
if ($f) {
|
||||||
|
|
||||||
|
$read = strlen($buffer);
|
||||||
|
|
||||||
|
while ($read < $raw_length) {
|
||||||
|
|
||||||
|
$buffer .= fread($f, $raw_length - $read);
|
||||||
|
$read = strlen($buffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
fclose($f);
|
||||||
|
|
||||||
|
if ($read >= $raw_length) {
|
||||||
|
|
||||||
|
$buffer_valid = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (! $buffer_valid || strlen($buffer) < $raw_length) {
|
||||||
|
|
||||||
|
$bl = strlen($buffer);
|
||||||
|
|
||||||
|
for ($i = 0; $i < $raw_length; $i++) {
|
||||||
|
|
||||||
|
if ($i < $bl) {
|
||||||
|
|
||||||
|
$buffer[$i] = $buffer[$i] ^ chr(mt_rand(0, 255));
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
|
||||||
|
$buffer .= chr(mt_rand(0, 255));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$salt = str_replace('+', '.', base64_encode($buffer));
|
||||||
|
|
||||||
|
return substr($salt, 0, $required_salt_len);
|
||||||
|
}
|
49
src/vendor/PicoTools/Dependency_Injection.php
vendored
Normal file
49
src/vendor/PicoTools/Dependency_Injection.php
vendored
Normal file
@ -0,0 +1,49 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This file is part of PicoTools.
|
||||||
|
*
|
||||||
|
* (c) Frédéric Guillot http://fredericguillot.com
|
||||||
|
*
|
||||||
|
* For the full copyright and license information, please view the LICENSE
|
||||||
|
* file that was distributed with this source code.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace PicoTools;
|
||||||
|
|
||||||
|
|
||||||
|
function singleton($name)
|
||||||
|
{
|
||||||
|
static $instance = array();
|
||||||
|
|
||||||
|
if (! isset($instance[$name])) {
|
||||||
|
|
||||||
|
$callback = container($name);
|
||||||
|
|
||||||
|
if (! is_callable($callback)) {
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
$instance[$name] = $callback();
|
||||||
|
}
|
||||||
|
|
||||||
|
return $instance[$name];
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function container($name, $value = null)
|
||||||
|
{
|
||||||
|
static $container = array();
|
||||||
|
|
||||||
|
if (null !== $value) {
|
||||||
|
|
||||||
|
$container[$name] = $value;
|
||||||
|
}
|
||||||
|
else if (isset($container[$name])) {
|
||||||
|
|
||||||
|
return $container[$name];
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
176
src/vendor/PicoTools/Helper.php
vendored
Normal file
176
src/vendor/PicoTools/Helper.php
vendored
Normal file
@ -0,0 +1,176 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Helper;
|
||||||
|
|
||||||
|
|
||||||
|
function escape($value)
|
||||||
|
{
|
||||||
|
return htmlspecialchars($value, ENT_QUOTES, 'UTF-8', false);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the flash message if there is something
|
||||||
|
*
|
||||||
|
* @param string $html HTML tags of the flash message parsed with sprintf
|
||||||
|
* return string HTML tags with the message or empty string if nothing
|
||||||
|
*/
|
||||||
|
function flash($html)
|
||||||
|
{
|
||||||
|
$data = '';
|
||||||
|
|
||||||
|
if (isset($_SESSION['flash_message'])) {
|
||||||
|
|
||||||
|
$data = sprintf($html, escape($_SESSION['flash_message']));
|
||||||
|
unset($_SESSION['flash_message']);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $data;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the flash error message if there is something
|
||||||
|
*
|
||||||
|
* @param string $html HTML tags of the flash message parsed with sprintf
|
||||||
|
* return string HTML tags with the message or empty string if nothing
|
||||||
|
*/
|
||||||
|
function flash_error($html)
|
||||||
|
{
|
||||||
|
$data = '';
|
||||||
|
|
||||||
|
if (isset($_SESSION['flash_error_message'])) {
|
||||||
|
|
||||||
|
$data = sprintf($html, escape($_SESSION['flash_error_message']));
|
||||||
|
unset($_SESSION['flash_error_message']);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $data;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function in_list($id, array $listing)
|
||||||
|
{
|
||||||
|
if (isset($listing[$id])) {
|
||||||
|
|
||||||
|
return escape($listing[$id]);
|
||||||
|
}
|
||||||
|
|
||||||
|
return '?';
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function error_class(array $errors, $name)
|
||||||
|
{
|
||||||
|
return ! isset($errors[$name]) ? '' : ' form-error';
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function error_list(array $errors, $name)
|
||||||
|
{
|
||||||
|
$html = '';
|
||||||
|
|
||||||
|
if (isset($errors[$name])) {
|
||||||
|
|
||||||
|
$html .= '<ul class="form-errors">';
|
||||||
|
|
||||||
|
foreach ($errors[$name] as $error) {
|
||||||
|
|
||||||
|
$html .= '<li>'.escape($error).'</li>';
|
||||||
|
}
|
||||||
|
|
||||||
|
$html .= '</ul>';
|
||||||
|
}
|
||||||
|
|
||||||
|
return $html;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function form_value($values, $name)
|
||||||
|
{
|
||||||
|
if (isset($values->$name)) {
|
||||||
|
|
||||||
|
return 'value="'.escape($values->$name).'"';
|
||||||
|
}
|
||||||
|
|
||||||
|
return isset($values[$name]) ? 'value="'.escape($values[$name]).'"' : '';
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function form_hidden($name, $values = array())
|
||||||
|
{
|
||||||
|
return '<input type="hidden" name="'.$name.'" id="form-'.$name.'" '.form_value($values, $name).'/>';
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function form_default_select($name, array $options, $values = array(), array $errors = array(), $class = '')
|
||||||
|
{
|
||||||
|
$options = array('' => '?') + $options;
|
||||||
|
|
||||||
|
return form_select($name, $options, $values, $errors, $class);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function form_select($name, array $options, $values = array(), array $errors = array(), $class = '')
|
||||||
|
{
|
||||||
|
$html = '<select name="'.$name.'" id="form-'.$name.'" class="'.$class.'">';
|
||||||
|
|
||||||
|
foreach ($options as $id => $value) {
|
||||||
|
|
||||||
|
$html .= '<option value="'.escape($id).'"';
|
||||||
|
|
||||||
|
if (isset($values->$name) && $id == $values->$name) $html .= ' selected="selected"';
|
||||||
|
if (isset($values[$name]) && $id == $values[$name]) $html .= ' selected="selected"';
|
||||||
|
|
||||||
|
$html .= '>'.escape($value).'</option>';
|
||||||
|
}
|
||||||
|
|
||||||
|
$html .= '</select>';
|
||||||
|
$html .= error_list($errors, $name);
|
||||||
|
|
||||||
|
return $html;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function form_label($label, $name, $class = '')
|
||||||
|
{
|
||||||
|
return '<label for="form-'.$name.'" class="'.$class.'">'.escape($label).'</label>';
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function form_text($name, $values = array(), array $errors = array(), array $attributes = array(), $class = '')
|
||||||
|
{
|
||||||
|
$class .= error_class($errors, $name);
|
||||||
|
|
||||||
|
$html = '<input type="text" name="'.$name.'" id="form-'.$name.'" '.form_value($values, $name).' class="'.$class.'" ';
|
||||||
|
$html .= implode(' ', $attributes).'/>';
|
||||||
|
$html .= error_list($errors, $name);
|
||||||
|
|
||||||
|
return $html;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function form_password($name, $values = array(), array $errors = array(), array $attributes = array(), $class = '')
|
||||||
|
{
|
||||||
|
$class .= error_class($errors, $name);
|
||||||
|
|
||||||
|
$html = '<input type="password" name="'.$name.'" id="form-'.$name.'" '.form_value($values, $name).' class="'.$class.'" ';
|
||||||
|
$html .= implode(' ', $attributes).'/>';
|
||||||
|
$html .= error_list($errors, $name);
|
||||||
|
|
||||||
|
return $html;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function form_textarea($name, $values = array(), array $errors = array(), array $attributes = array(), $class = '')
|
||||||
|
{
|
||||||
|
$class .= error_class($errors, $name);
|
||||||
|
|
||||||
|
$html = '<textarea name="'.$name.'" id="form-'.$name.'" class="'.$class.'" ';
|
||||||
|
$html .= implode(' ', $attributes).'>';
|
||||||
|
$html .= isset($values->$name) ? escape($values->$name) : isset($values[$name]) ? $values[$name] : '';
|
||||||
|
$html .= '</textarea>';
|
||||||
|
$html .= error_list($errors, $name);
|
||||||
|
|
||||||
|
return $html;
|
||||||
|
}
|
243
src/vendor/PicoTools/Pixtag.php
vendored
Normal file
243
src/vendor/PicoTools/Pixtag.php
vendored
Normal file
@ -0,0 +1,243 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This file is part of picoTools.
|
||||||
|
*
|
||||||
|
* (c) Frédéric Guillot http://fredericguillot.com
|
||||||
|
*
|
||||||
|
* For the full copyright and license information, please view the LICENSE
|
||||||
|
* file that was distributed with this source code.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace PicoTools;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A wrapper around exiv2 command line utility
|
||||||
|
* You can write and read IPTC, XMP and EXIF metadata inside a picture
|
||||||
|
*
|
||||||
|
* @author Frédéric Guillot
|
||||||
|
*/
|
||||||
|
class Pixtag implements \ArrayAccess, \Iterator
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Filename
|
||||||
|
*
|
||||||
|
* @access private
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
private $filename;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Container
|
||||||
|
*
|
||||||
|
* @access private
|
||||||
|
* @var array
|
||||||
|
*/
|
||||||
|
private $container = array();
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor
|
||||||
|
*
|
||||||
|
* @access public
|
||||||
|
* @param string $filename Path to the picture
|
||||||
|
*/
|
||||||
|
public function __construct($filename)
|
||||||
|
{
|
||||||
|
$this->filename = $filename;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Read metadata from the picture
|
||||||
|
*
|
||||||
|
* @access public
|
||||||
|
*/
|
||||||
|
public function read()
|
||||||
|
{
|
||||||
|
$c = new Command('exiv2 -PIEXkt '.$this->filename);
|
||||||
|
$c->execute();
|
||||||
|
$this->parse($c->getStdout());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parse metadata bloc from exiv2 output command
|
||||||
|
*
|
||||||
|
* @access public
|
||||||
|
* @param string $data Raw command output of exiv2
|
||||||
|
*/
|
||||||
|
public function parse($data)
|
||||||
|
{
|
||||||
|
$lines = explode("\n", trim($data));
|
||||||
|
|
||||||
|
foreach ($lines as $line) {
|
||||||
|
|
||||||
|
$results = preg_split('/ /', $line, -1, PREG_SPLIT_OFFSET_CAPTURE);
|
||||||
|
|
||||||
|
if (isset($results[0][0])) {
|
||||||
|
|
||||||
|
$key = $results[0][0];
|
||||||
|
$value = '';
|
||||||
|
|
||||||
|
for ($i = 1, $ilen = count($results); $i < $ilen; ++$i) {
|
||||||
|
|
||||||
|
if ($results[$i][0] !== '') {
|
||||||
|
|
||||||
|
$value = substr($line, $results[$i][1]);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($value === '(Binary value suppressed)') {
|
||||||
|
|
||||||
|
$value = '';
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->container[$key] = $value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Write metadata to the picture
|
||||||
|
* This method erase all keys and then add them to the picture
|
||||||
|
*
|
||||||
|
* @access public
|
||||||
|
*/
|
||||||
|
public function write()
|
||||||
|
{
|
||||||
|
$commands = array();
|
||||||
|
|
||||||
|
foreach ($this->container as $key => $value) {
|
||||||
|
|
||||||
|
$commands[] = sprintf('-M "del %s"', $key);
|
||||||
|
$commands[] = sprintf('-M "add %s %s"', $key, $value);
|
||||||
|
}
|
||||||
|
|
||||||
|
$c = new Command(sprintf(
|
||||||
|
'exiv2 %s %s',
|
||||||
|
implode(' ', $commands),
|
||||||
|
$this->filename
|
||||||
|
));
|
||||||
|
|
||||||
|
$c->execute();
|
||||||
|
|
||||||
|
if ($c->getReturnValue() !== 0) {
|
||||||
|
|
||||||
|
throw new \RuntimeException('Unable to write metadata');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set a metadata
|
||||||
|
*
|
||||||
|
* @access public
|
||||||
|
* @param string $offset Key name, see exiv2 documentation for keys list
|
||||||
|
* @param string $value Key value
|
||||||
|
*/
|
||||||
|
public function offsetSet($offset, $value)
|
||||||
|
{
|
||||||
|
$this->container[$offset] = $value;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if a key exists
|
||||||
|
*
|
||||||
|
* @access public
|
||||||
|
* @param string $offset Key name, see exiv2 documentation for keys list
|
||||||
|
* @return boolean True if the key exists
|
||||||
|
*/
|
||||||
|
public function offsetExists($offset)
|
||||||
|
{
|
||||||
|
return isset($this->container[$offset]);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove a metadata
|
||||||
|
*
|
||||||
|
* @access public
|
||||||
|
* @param string $offset Key name, see exiv2 documentation for keys list
|
||||||
|
*/
|
||||||
|
public function offsetUnset($offset)
|
||||||
|
{
|
||||||
|
unset($this->container[$offset]);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get a metadata
|
||||||
|
*
|
||||||
|
* @access public
|
||||||
|
* @param string $offset Key name, see exiv2 documentation for keys list
|
||||||
|
* @return string Key value
|
||||||
|
*/
|
||||||
|
public function offsetGet($offset)
|
||||||
|
{
|
||||||
|
return isset($this->container[$offset]) ? $this->container[$offset] : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reset the position of the container
|
||||||
|
*
|
||||||
|
* @access public
|
||||||
|
*/
|
||||||
|
public function rewind()
|
||||||
|
{
|
||||||
|
reset($this->container);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Current
|
||||||
|
*
|
||||||
|
* @access public
|
||||||
|
* @return string Current value
|
||||||
|
*/
|
||||||
|
public function current()
|
||||||
|
{
|
||||||
|
return current($this->container);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Key
|
||||||
|
*
|
||||||
|
* @access public
|
||||||
|
* @return string Current key
|
||||||
|
*/
|
||||||
|
public function key()
|
||||||
|
{
|
||||||
|
return key($this->container);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Next
|
||||||
|
*
|
||||||
|
* @access public
|
||||||
|
*/
|
||||||
|
public function next()
|
||||||
|
{
|
||||||
|
next($this->container);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Valid
|
||||||
|
*
|
||||||
|
* @access public
|
||||||
|
* @return boolean True if the current key is valid
|
||||||
|
*/
|
||||||
|
public function valid()
|
||||||
|
{
|
||||||
|
return isset($this->container[key($this->container)]);
|
||||||
|
}
|
||||||
|
}
|
55
src/vendor/PicoTools/Template.php
vendored
Normal file
55
src/vendor/PicoTools/Template.php
vendored
Normal file
@ -0,0 +1,55 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This file is part of PicoTools.
|
||||||
|
*
|
||||||
|
* (c) Frédéric Guillot http://fredericguillot.com
|
||||||
|
*
|
||||||
|
* For the full copyright and license information, please view the LICENSE
|
||||||
|
* file that was distributed with this source code.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace PicoTools\Template;
|
||||||
|
|
||||||
|
const PATH = 'templates/';
|
||||||
|
|
||||||
|
|
||||||
|
// Template\load('template_name', ['bla' => 'value']);
|
||||||
|
function load()
|
||||||
|
{
|
||||||
|
if (func_num_args() < 1 || func_num_args() > 2) {
|
||||||
|
|
||||||
|
die('Invalid template arguments');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (! file_exists(PATH.func_get_arg(0).'.php')) {
|
||||||
|
|
||||||
|
die('Unable to load the template: "'.func_get_arg(0).'"');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (func_num_args() === 2) {
|
||||||
|
|
||||||
|
if (! is_array(func_get_arg(1))) {
|
||||||
|
|
||||||
|
die('Template variables must be an array');
|
||||||
|
}
|
||||||
|
|
||||||
|
extract(func_get_arg(1));
|
||||||
|
}
|
||||||
|
|
||||||
|
ob_start();
|
||||||
|
|
||||||
|
include PATH.func_get_arg(0).'.php';
|
||||||
|
|
||||||
|
return ob_get_clean();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function layout($template_name, array $template_args = array())
|
||||||
|
{
|
||||||
|
$output = load('app_header', $template_args);
|
||||||
|
$output .= load($template_name, $template_args);
|
||||||
|
$output .= load('app_footer', $template_args);
|
||||||
|
|
||||||
|
return $output;
|
||||||
|
}
|
121
src/vendor/PicoTools/Translator.php
vendored
Normal file
121
src/vendor/PicoTools/Translator.php
vendored
Normal file
@ -0,0 +1,121 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This file is part of PicoTools.
|
||||||
|
*
|
||||||
|
* (c) Frédéric Guillot http://fredericguillot.com
|
||||||
|
*
|
||||||
|
* For the full copyright and license information, please view the LICENSE
|
||||||
|
* file that was distributed with this source code.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace PicoTools\Translator {
|
||||||
|
|
||||||
|
const PATH = 'locales/';
|
||||||
|
|
||||||
|
|
||||||
|
function translate($identifier)
|
||||||
|
{
|
||||||
|
$args = \func_get_args();
|
||||||
|
|
||||||
|
\array_shift($args);
|
||||||
|
\array_unshift($args, get($identifier));
|
||||||
|
|
||||||
|
return \call_user_func_array(
|
||||||
|
'sprintf',
|
||||||
|
$args
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function number($number)
|
||||||
|
{
|
||||||
|
return number_format(
|
||||||
|
$number,
|
||||||
|
get('number.decimals', 2),
|
||||||
|
get('number.decimals_separator', '.'),
|
||||||
|
get('number.thousands_separator', ',')
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function currency($amount)
|
||||||
|
{
|
||||||
|
$position = get('currency.position', 'before');
|
||||||
|
$symbol = get('currency.symbol', '$');
|
||||||
|
$str = '';
|
||||||
|
|
||||||
|
if ($position === 'before') {
|
||||||
|
|
||||||
|
$str .= $symbol;
|
||||||
|
}
|
||||||
|
|
||||||
|
$str .= number($amount);
|
||||||
|
|
||||||
|
if ($position === 'after') {
|
||||||
|
|
||||||
|
$str .= ' '.$symbol;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $str;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function get($identifier, $default = '')
|
||||||
|
{
|
||||||
|
$locales = container();
|
||||||
|
|
||||||
|
if (isset($locales[$identifier])) {
|
||||||
|
|
||||||
|
return $locales[$identifier];
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
|
||||||
|
return $default;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function load($language)
|
||||||
|
{
|
||||||
|
$path = PATH.$language;
|
||||||
|
$locales = array();
|
||||||
|
|
||||||
|
if (is_dir($path)) {
|
||||||
|
|
||||||
|
$dir = new \DirectoryIterator($path);
|
||||||
|
|
||||||
|
foreach ($dir as $fileinfo) {
|
||||||
|
|
||||||
|
if (strpos($fileinfo->getFilename(), '.php') !== false) {
|
||||||
|
|
||||||
|
$locales = array_merge($locales, include $fileinfo->getPathname());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
container($locales);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function container($locales = null)
|
||||||
|
{
|
||||||
|
static $values = array();
|
||||||
|
|
||||||
|
if ($locales) {
|
||||||
|
|
||||||
|
$values = $locales;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $values;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
function t() {
|
||||||
|
|
||||||
|
return call_user_func_array('\PicoTools\Translator\translate', func_get_args());
|
||||||
|
}
|
||||||
|
}
|
44
src/vendor/SimpleValidator/Base.php
vendored
Normal file
44
src/vendor/SimpleValidator/Base.php
vendored
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This file is part of Simple Validator.
|
||||||
|
*
|
||||||
|
* (c) Frédéric Guillot <contact@fredericguillot.com>
|
||||||
|
*
|
||||||
|
* This source file is subject to the MIT license that is bundled
|
||||||
|
* with this source code in the file LICENSE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace SimpleValidator;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Frédéric Guillot <contact@fredericguillot.com>
|
||||||
|
*/
|
||||||
|
abstract class Base
|
||||||
|
{
|
||||||
|
protected $field = '';
|
||||||
|
protected $error_message = '';
|
||||||
|
protected $data = array();
|
||||||
|
|
||||||
|
|
||||||
|
abstract public function execute(array $data);
|
||||||
|
|
||||||
|
|
||||||
|
public function __construct($field, $error_message)
|
||||||
|
{
|
||||||
|
$this->field = $field;
|
||||||
|
$this->error_message = $error_message;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public function getErrorMessage()
|
||||||
|
{
|
||||||
|
return $this->error_message;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public function getField()
|
||||||
|
{
|
||||||
|
return $this->field;
|
||||||
|
}
|
||||||
|
}
|
67
src/vendor/SimpleValidator/Validator.php
vendored
Normal file
67
src/vendor/SimpleValidator/Validator.php
vendored
Normal file
@ -0,0 +1,67 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This file is part of Simple Validator.
|
||||||
|
*
|
||||||
|
* (c) Frédéric Guillot <contact@fredericguillot.com>
|
||||||
|
*
|
||||||
|
* This source file is subject to the MIT license that is bundled
|
||||||
|
* with this source code in the file LICENSE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace SimpleValidator;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Frédéric Guillot <contact@fredericguillot.com>
|
||||||
|
*/
|
||||||
|
class Validator
|
||||||
|
{
|
||||||
|
private $data = array();
|
||||||
|
private $errors = array();
|
||||||
|
private $validators = array();
|
||||||
|
|
||||||
|
|
||||||
|
public function __construct(array $data, array $validators)
|
||||||
|
{
|
||||||
|
$this->data = $data;
|
||||||
|
$this->validators = $validators;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public function execute()
|
||||||
|
{
|
||||||
|
$result = true;
|
||||||
|
|
||||||
|
foreach ($this->validators as $validator) {
|
||||||
|
|
||||||
|
if (! $validator->execute($this->data)) {
|
||||||
|
|
||||||
|
$this->addError(
|
||||||
|
$validator->getField(),
|
||||||
|
$validator->getErrorMessage()
|
||||||
|
);
|
||||||
|
|
||||||
|
$result = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $result;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public function addError($field, $message)
|
||||||
|
{
|
||||||
|
if (! isset($this->errors[$field])) {
|
||||||
|
|
||||||
|
$this->errors[$field] = array();
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->errors[$field][] = $message;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public function getErrors()
|
||||||
|
{
|
||||||
|
return $this->errors;
|
||||||
|
}
|
||||||
|
}
|
33
src/vendor/SimpleValidator/Validators/Alpha.php
vendored
Normal file
33
src/vendor/SimpleValidator/Validators/Alpha.php
vendored
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This file is part of Simple Validator.
|
||||||
|
*
|
||||||
|
* (c) Frédéric Guillot <contact@fredericguillot.com>
|
||||||
|
*
|
||||||
|
* This source file is subject to the MIT license that is bundled
|
||||||
|
* with this source code in the file LICENSE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace SimpleValidator\Validators;
|
||||||
|
|
||||||
|
use SimpleValidator\Base;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Frédéric Guillot <contact@fredericguillot.com>
|
||||||
|
*/
|
||||||
|
class Alpha extends Base
|
||||||
|
{
|
||||||
|
public function execute(array $data)
|
||||||
|
{
|
||||||
|
if (isset($data[$this->field]) && $data[$this->field] !== '') {
|
||||||
|
|
||||||
|
if (! ctype_alpha($data[$this->field])) {
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
33
src/vendor/SimpleValidator/Validators/AlphaNumeric.php
vendored
Normal file
33
src/vendor/SimpleValidator/Validators/AlphaNumeric.php
vendored
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This file is part of Simple Validator.
|
||||||
|
*
|
||||||
|
* (c) Frédéric Guillot <contact@fredericguillot.com>
|
||||||
|
*
|
||||||
|
* This source file is subject to the MIT license that is bundled
|
||||||
|
* with this source code in the file LICENSE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace SimpleValidator\Validators;
|
||||||
|
|
||||||
|
use SimpleValidator\Base;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Frédéric Guillot <contact@fredericguillot.com>
|
||||||
|
*/
|
||||||
|
class AlphaNumeric extends Base
|
||||||
|
{
|
||||||
|
public function execute(array $data)
|
||||||
|
{
|
||||||
|
if (isset($data[$this->field]) && $data[$this->field] !== '') {
|
||||||
|
|
||||||
|
if (! ctype_alnum($data[$this->field])) {
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
81
src/vendor/SimpleValidator/Validators/Email.php
vendored
Normal file
81
src/vendor/SimpleValidator/Validators/Email.php
vendored
Normal file
@ -0,0 +1,81 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This file is part of Simple Validator.
|
||||||
|
*
|
||||||
|
* (c) Frédéric Guillot <contact@fredericguillot.com>
|
||||||
|
*
|
||||||
|
* This source file is subject to the MIT license that is bundled
|
||||||
|
* with this source code in the file LICENSE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace SimpleValidator\Validators;
|
||||||
|
|
||||||
|
use SimpleValidator\Base;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Frédéric Guillot <contact@fredericguillot.com>
|
||||||
|
*/
|
||||||
|
class Email extends Base
|
||||||
|
{
|
||||||
|
public function execute(array $data)
|
||||||
|
{
|
||||||
|
if (isset($data[$this->field]) && $data[$this->field] !== '') {
|
||||||
|
|
||||||
|
// I use the same validation method as Firefox
|
||||||
|
// http://hg.mozilla.org/mozilla-central/file/cf5da681d577/content/html/content/src/nsHTMLInputElement.cpp#l3967
|
||||||
|
|
||||||
|
$value = $data[$this->field];
|
||||||
|
$length = strlen($value);
|
||||||
|
|
||||||
|
// If the email address begins with a '@' or ends with a '.',
|
||||||
|
// we know it's invalid.
|
||||||
|
if ($value[0] === '@' || $value[$length - 1] === '.') {
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check the username
|
||||||
|
for ($i = 0; $i < $length && $value[$i] !== '@'; ++$i) {
|
||||||
|
|
||||||
|
$c = $value[$i];
|
||||||
|
|
||||||
|
if (! (ctype_alnum($c) || $c === '.' || $c === '!' || $c === '#' || $c === '$' ||
|
||||||
|
$c === '%' || $c === '&' || $c === '\'' || $c === '*' || $c === '+' ||
|
||||||
|
$c === '-' || $c === '/' || $c === '=' || $c === '?' || $c === '^' ||
|
||||||
|
$c === '_' || $c === '`' || $c === '{' || $c === '|' || $c === '}' ||
|
||||||
|
$c === '~')) {
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// There is no domain name (or it's one-character long),
|
||||||
|
// that's not a valid email address.
|
||||||
|
if (++$i >= $length) return false;
|
||||||
|
if (($i + 1) === $length) return false;
|
||||||
|
|
||||||
|
// The domain name can't begin with a dot.
|
||||||
|
if ($value[$i] === '.') return false;
|
||||||
|
|
||||||
|
// Parsing the domain name.
|
||||||
|
for (; $i < $length; ++$i) {
|
||||||
|
|
||||||
|
$c = $value[$i];
|
||||||
|
|
||||||
|
if ($c === '.') {
|
||||||
|
|
||||||
|
// A dot can't follow a dot.
|
||||||
|
if ($value[$i - 1] === '.') return false;
|
||||||
|
}
|
||||||
|
elseif (! (ctype_alnum($c) || $c === '-')) {
|
||||||
|
|
||||||
|
// The domain characters have to be in this list to be valid.
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
43
src/vendor/SimpleValidator/Validators/Equals.php
vendored
Normal file
43
src/vendor/SimpleValidator/Validators/Equals.php
vendored
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This file is part of Simple Validator.
|
||||||
|
*
|
||||||
|
* (c) Frédéric Guillot <contact@fredericguillot.com>
|
||||||
|
*
|
||||||
|
* This source file is subject to the MIT license that is bundled
|
||||||
|
* with this source code in the file LICENSE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace SimpleValidator\Validators;
|
||||||
|
|
||||||
|
use SimpleValidator\Base;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Frédéric Guillot <contact@fredericguillot.com>
|
||||||
|
*/
|
||||||
|
class Equals extends Base
|
||||||
|
{
|
||||||
|
private $field2;
|
||||||
|
|
||||||
|
|
||||||
|
public function __construct($field1, $field2, $error_message)
|
||||||
|
{
|
||||||
|
parent::__construct($field1, $error_message);
|
||||||
|
|
||||||
|
$this->field2 = $field2;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public function execute(array $data)
|
||||||
|
{
|
||||||
|
if (isset($data[$this->field]) && $data[$this->field] !== '') {
|
||||||
|
|
||||||
|
if (! isset($data[$this->field2])) return false;
|
||||||
|
|
||||||
|
return $data[$this->field] === $data[$this->field2];
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
42
src/vendor/SimpleValidator/Validators/Integer.php
vendored
Normal file
42
src/vendor/SimpleValidator/Validators/Integer.php
vendored
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This file is part of Simple Validator.
|
||||||
|
*
|
||||||
|
* (c) Frédéric Guillot <contact@fredericguillot.com>
|
||||||
|
*
|
||||||
|
* This source file is subject to the MIT license that is bundled
|
||||||
|
* with this source code in the file LICENSE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace SimpleValidator\Validators;
|
||||||
|
|
||||||
|
use SimpleValidator\Base;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Frédéric Guillot <contact@fredericguillot.com>
|
||||||
|
*/
|
||||||
|
class Integer extends Base
|
||||||
|
{
|
||||||
|
public function execute(array $data)
|
||||||
|
{
|
||||||
|
if (isset($data[$this->field]) && $data[$this->field] !== '') {
|
||||||
|
|
||||||
|
if (is_string($data[$this->field])) {
|
||||||
|
|
||||||
|
if ($data[$this->field][0] === '-') {
|
||||||
|
|
||||||
|
return ctype_digit(substr($data[$this->field], 1));
|
||||||
|
}
|
||||||
|
|
||||||
|
return ctype_digit($data[$this->field]);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
|
||||||
|
return is_int($data[$this->field]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
33
src/vendor/SimpleValidator/Validators/Ip.php
vendored
Normal file
33
src/vendor/SimpleValidator/Validators/Ip.php
vendored
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This file is part of Simple Validator.
|
||||||
|
*
|
||||||
|
* (c) Frédéric Guillot <contact@fredericguillot.com>
|
||||||
|
*
|
||||||
|
* This source file is subject to the MIT license that is bundled
|
||||||
|
* with this source code in the file LICENSE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace SimpleValidator\Validators;
|
||||||
|
|
||||||
|
use SimpleValidator\Base;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Frédéric Guillot <contact@fredericguillot.com>
|
||||||
|
*/
|
||||||
|
class Ip extends Base
|
||||||
|
{
|
||||||
|
public function execute(array $data)
|
||||||
|
{
|
||||||
|
if (isset($data[$this->field]) && $data[$this->field] !== '') {
|
||||||
|
|
||||||
|
if (! filter_var($data[$this->field], FILTER_VALIDATE_IP)) {
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
48
src/vendor/SimpleValidator/Validators/Length.php
vendored
Normal file
48
src/vendor/SimpleValidator/Validators/Length.php
vendored
Normal file
@ -0,0 +1,48 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This file is part of Simple Validator.
|
||||||
|
*
|
||||||
|
* (c) Frédéric Guillot <contact@fredericguillot.com>
|
||||||
|
*
|
||||||
|
* This source file is subject to the MIT license that is bundled
|
||||||
|
* with this source code in the file LICENSE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace SimpleValidator\Validators;
|
||||||
|
|
||||||
|
use SimpleValidator\Base;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Frédéric Guillot <contact@fredericguillot.com>
|
||||||
|
*/
|
||||||
|
class Length extends Base
|
||||||
|
{
|
||||||
|
private $min;
|
||||||
|
private $max;
|
||||||
|
|
||||||
|
|
||||||
|
public function __construct($field, $error_message, $min, $max)
|
||||||
|
{
|
||||||
|
parent::__construct($field, $error_message);
|
||||||
|
|
||||||
|
$this->min = $min;
|
||||||
|
$this->max = $max;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public function execute(array $data)
|
||||||
|
{
|
||||||
|
if (isset($data[$this->field]) && $data[$this->field] !== '') {
|
||||||
|
|
||||||
|
$length = mb_strlen($data[$this->field], 'UTF-8');
|
||||||
|
|
||||||
|
if ($length < $this->min || $length > $this->max) {
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
37
src/vendor/SimpleValidator/Validators/MacAddress.php
vendored
Normal file
37
src/vendor/SimpleValidator/Validators/MacAddress.php
vendored
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This file is part of Simple Validator.
|
||||||
|
*
|
||||||
|
* (c) Frédéric Guillot <contact@fredericguillot.com>
|
||||||
|
*
|
||||||
|
* This source file is subject to the MIT license that is bundled
|
||||||
|
* with this source code in the file LICENSE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace SimpleValidator\Validators;
|
||||||
|
|
||||||
|
use SimpleValidator\Base;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Frédéric Guillot <contact@fredericguillot.com>
|
||||||
|
*/
|
||||||
|
class MacAddress extends Base
|
||||||
|
{
|
||||||
|
public function execute(array $data)
|
||||||
|
{
|
||||||
|
if (isset($data[$this->field]) && $data[$this->field] !== '') {
|
||||||
|
|
||||||
|
$groups = explode(':', $data[$this->field]);
|
||||||
|
|
||||||
|
if (count($groups) !== 6) return false;
|
||||||
|
|
||||||
|
foreach ($groups as $group) {
|
||||||
|
|
||||||
|
if (! ctype_xdigit($group)) return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
46
src/vendor/SimpleValidator/Validators/MaxLength.php
vendored
Normal file
46
src/vendor/SimpleValidator/Validators/MaxLength.php
vendored
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This file is part of Simple Validator.
|
||||||
|
*
|
||||||
|
* (c) Frédéric Guillot <contact@fredericguillot.com>
|
||||||
|
*
|
||||||
|
* This source file is subject to the MIT license that is bundled
|
||||||
|
* with this source code in the file LICENSE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace SimpleValidator\Validators;
|
||||||
|
|
||||||
|
use SimpleValidator\Base;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Frédéric Guillot <contact@fredericguillot.com>
|
||||||
|
*/
|
||||||
|
class MaxLength extends Base
|
||||||
|
{
|
||||||
|
private $max;
|
||||||
|
|
||||||
|
|
||||||
|
public function __construct($field, $error_message, $max)
|
||||||
|
{
|
||||||
|
parent::__construct($field, $error_message);
|
||||||
|
|
||||||
|
$this->max = $max;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public function execute(array $data)
|
||||||
|
{
|
||||||
|
if (isset($data[$this->field]) && $data[$this->field] !== '') {
|
||||||
|
|
||||||
|
$length = mb_strlen($data[$this->field], 'UTF-8');
|
||||||
|
|
||||||
|
if ($length > $this->max) {
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
46
src/vendor/SimpleValidator/Validators/MinLength.php
vendored
Normal file
46
src/vendor/SimpleValidator/Validators/MinLength.php
vendored
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This file is part of Simple Validator.
|
||||||
|
*
|
||||||
|
* (c) Frédéric Guillot <contact@fredericguillot.com>
|
||||||
|
*
|
||||||
|
* This source file is subject to the MIT license that is bundled
|
||||||
|
* with this source code in the file LICENSE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace SimpleValidator\Validators;
|
||||||
|
|
||||||
|
use SimpleValidator\Base;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Frédéric Guillot <contact@fredericguillot.com>
|
||||||
|
*/
|
||||||
|
class MinLength extends Base
|
||||||
|
{
|
||||||
|
private $min;
|
||||||
|
|
||||||
|
|
||||||
|
public function __construct($field, $error_message, $min)
|
||||||
|
{
|
||||||
|
parent::__construct($field, $error_message);
|
||||||
|
|
||||||
|
$this->min = $min;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public function execute(array $data)
|
||||||
|
{
|
||||||
|
if (isset($data[$this->field]) && $data[$this->field] !== '') {
|
||||||
|
|
||||||
|
$length = mb_strlen($data[$this->field], 'UTF-8');
|
||||||
|
|
||||||
|
if ($length < $this->min) {
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
33
src/vendor/SimpleValidator/Validators/Numeric.php
vendored
Normal file
33
src/vendor/SimpleValidator/Validators/Numeric.php
vendored
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This file is part of Simple Validator.
|
||||||
|
*
|
||||||
|
* (c) Frédéric Guillot <contact@fredericguillot.com>
|
||||||
|
*
|
||||||
|
* This source file is subject to the MIT license that is bundled
|
||||||
|
* with this source code in the file LICENSE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace SimpleValidator\Validators;
|
||||||
|
|
||||||
|
use SimpleValidator\Base;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Frédéric Guillot <contact@fredericguillot.com>
|
||||||
|
*/
|
||||||
|
class Numeric extends Base
|
||||||
|
{
|
||||||
|
public function execute(array $data)
|
||||||
|
{
|
||||||
|
if (isset($data[$this->field]) && $data[$this->field] !== '') {
|
||||||
|
|
||||||
|
if (! is_numeric($data[$this->field])) {
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
51
src/vendor/SimpleValidator/Validators/Range.php
vendored
Normal file
51
src/vendor/SimpleValidator/Validators/Range.php
vendored
Normal file
@ -0,0 +1,51 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This file is part of Simple Validator.
|
||||||
|
*
|
||||||
|
* (c) Frédéric Guillot <contact@fredericguillot.com>
|
||||||
|
*
|
||||||
|
* This source file is subject to the MIT license that is bundled
|
||||||
|
* with this source code in the file LICENSE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace SimpleValidator\Validators;
|
||||||
|
|
||||||
|
use SimpleValidator\Base;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Frédéric Guillot <contact@fredericguillot.com>
|
||||||
|
*/
|
||||||
|
class Range extends Base
|
||||||
|
{
|
||||||
|
private $min;
|
||||||
|
private $max;
|
||||||
|
|
||||||
|
|
||||||
|
public function __construct($field, $error_message, $min, $max)
|
||||||
|
{
|
||||||
|
parent::__construct($field, $error_message);
|
||||||
|
|
||||||
|
$this->min = $min;
|
||||||
|
$this->max = $max;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public function execute(array $data)
|
||||||
|
{
|
||||||
|
if (isset($data[$this->field]) && $data[$this->field] !== '') {
|
||||||
|
|
||||||
|
if (! is_numeric($data[$this->field])) {
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($data[$this->field] < $this->min || $data[$this->field] > $this->max) {
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
30
src/vendor/SimpleValidator/Validators/Required.php
vendored
Normal file
30
src/vendor/SimpleValidator/Validators/Required.php
vendored
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This file is part of Simple Validator.
|
||||||
|
*
|
||||||
|
* (c) Frédéric Guillot <contact@fredericguillot.com>
|
||||||
|
*
|
||||||
|
* This source file is subject to the MIT license that is bundled
|
||||||
|
* with this source code in the file LICENSE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace SimpleValidator\Validators;
|
||||||
|
|
||||||
|
use SimpleValidator\Base;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Frédéric Guillot <contact@fredericguillot.com>
|
||||||
|
*/
|
||||||
|
class Required extends Base
|
||||||
|
{
|
||||||
|
public function execute(array $data)
|
||||||
|
{
|
||||||
|
if (! isset($data[$this->field]) || $data[$this->field] === '') {
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
78
src/vendor/SimpleValidator/Validators/Unique.php
vendored
Normal file
78
src/vendor/SimpleValidator/Validators/Unique.php
vendored
Normal file
@ -0,0 +1,78 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This file is part of Simple Validator.
|
||||||
|
*
|
||||||
|
* (c) Frédéric Guillot <contact@fredericguillot.com>
|
||||||
|
*
|
||||||
|
* This source file is subject to the MIT license that is bundled
|
||||||
|
* with this source code in the file LICENSE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace SimpleValidator\Validators;
|
||||||
|
|
||||||
|
use SimpleValidator\Base;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Frédéric Guillot <contact@fredericguillot.com>
|
||||||
|
*/
|
||||||
|
class Unique extends Base
|
||||||
|
{
|
||||||
|
private $pdo;
|
||||||
|
private $primary_key;
|
||||||
|
private $table;
|
||||||
|
|
||||||
|
|
||||||
|
public function __construct($field, $error_message, \PDO $pdo, $table, $primary_key = 'id')
|
||||||
|
{
|
||||||
|
parent::__construct($field, $error_message);
|
||||||
|
|
||||||
|
$this->pdo = $pdo;
|
||||||
|
$this->primary_key = $primary_key;
|
||||||
|
$this->table = $table;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public function execute(array $data)
|
||||||
|
{
|
||||||
|
if (isset($data[$this->field]) && $data[$this->field] !== '') {
|
||||||
|
|
||||||
|
if (! isset($data[$this->primary_key])) {
|
||||||
|
|
||||||
|
$rq = $this->pdo->prepare('SELECT COUNT(*) FROM '.$this->table.' WHERE '.$this->field.'=?');
|
||||||
|
|
||||||
|
$rq->execute(array(
|
||||||
|
$data[$this->field]
|
||||||
|
));
|
||||||
|
|
||||||
|
$result = $rq->fetch(\PDO::FETCH_NUM);
|
||||||
|
|
||||||
|
if (isset($result[0]) && $result[0] === '1') {
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
|
||||||
|
$rq = $this->pdo->prepare(
|
||||||
|
'SELECT COUNT(*) FROM '.$this->table.'
|
||||||
|
WHERE '.$this->field.'=? AND '.$this->primary_key.' != ?'
|
||||||
|
);
|
||||||
|
|
||||||
|
$rq->execute(array(
|
||||||
|
$data[$this->field],
|
||||||
|
$data[$this->primary_key]
|
||||||
|
));
|
||||||
|
|
||||||
|
$result = $rq->fetch(\PDO::FETCH_NUM);
|
||||||
|
|
||||||
|
if (isset($result[0]) && $result[0] === '1') {
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
32
src/vendor/SimpleValidator/Validators/Version.php
vendored
Normal file
32
src/vendor/SimpleValidator/Validators/Version.php
vendored
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This file is part of Simple Validator.
|
||||||
|
*
|
||||||
|
* (c) Frédéric Guillot <contact@fredericguillot.com>
|
||||||
|
*
|
||||||
|
* This source file is subject to the MIT license that is bundled
|
||||||
|
* with this source code in the file LICENSE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace SimpleValidator\Validators;
|
||||||
|
|
||||||
|
use SimpleValidator\Base;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Frédéric Guillot <contact@fredericguillot.com>
|
||||||
|
* @link http://semver.org/
|
||||||
|
*/
|
||||||
|
class Version extends Base
|
||||||
|
{
|
||||||
|
public function execute(array $data)
|
||||||
|
{
|
||||||
|
if (isset($data[$this->field]) && $data[$this->field] !== '') {
|
||||||
|
|
||||||
|
$pattern = '/^[0-9]+\.[0-9]+\.[0-9]+([+-][^+-][0-9A-Za-z-.]*)?$/';
|
||||||
|
return (bool) preg_match($pattern, $data[$this->field]);
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user