Add Pinboard bookmark sync

This commit is contained in:
Frédéric Guillot 2014-12-24 10:47:24 -05:00
parent 5690f91fb7
commit 635e3bb813
24 changed files with 219 additions and 55 deletions

View File

@ -13,6 +13,7 @@
"files": [ "files": [
"lib/Translator.php", "lib/Translator.php",
"models/config.php", "models/config.php",
"models/service.php",
"models/user.php", "models/user.php",
"models/feed.php", "models/feed.php",
"models/item.php", "models/item.php",

View File

@ -209,3 +209,30 @@ Router\get_action('api', function() {
'title' => t('API') 'title' => t('API')
))); )));
}); });
// Display bookmark services page
Router\get_action('services', function() {
Response\html(Template\layout('services', array(
'errors' => array(),
'values' => Model\Config\get_all() + array('csrf' => Model\Config\generate_csrf()),
'menu' => 'config',
'title' => t('External services')
)));
});
// Update bookmark services
Router\post_action('services', function() {
$values = Request\values() + array('pinboard_enabled' => 0);
Model\Config\check_csrf_values($values);
if (Model\Config\save($values)) {
Session\flash(t('Your preferences are updated.'));
}
else {
Session\flash_error(t('Unable to update your preferences.'));
}
Response\redirect('?action=services');
});

View File

@ -217,4 +217,9 @@ return array(
// 'Must be an integer' => '', // 'Must be an integer' => '',
// 'Remove automatically unread items' => '', // 'Remove automatically unread items' => '',
// 'Toggle RTL mode' => '', // 'Toggle RTL mode' => '',
// 'external services' => '',
// 'External services' => '',
// 'Send bookmarks to Pinboard' => '',
// 'Pinboard API token' => '',
// 'Pinboard tags' => '',
); );

View File

@ -217,4 +217,9 @@ return array(
'Must be an integer' => 'Muss eine Ganzzahl sein', 'Must be an integer' => 'Muss eine Ganzzahl sein',
// 'Remove automatically unread items' => '', // 'Remove automatically unread items' => '',
// 'Toggle RTL mode' => '', // 'Toggle RTL mode' => '',
// 'external services' => '',
// 'External services' => '',
// 'Send bookmarks to Pinboard' => '',
// 'Pinboard API token' => '',
// 'Pinboard tags' => '',
); );

View File

@ -217,4 +217,9 @@ return array(
// 'Must be an integer' => '', // 'Must be an integer' => '',
// 'Remove automatically unread items' => '', // 'Remove automatically unread items' => '',
// 'Toggle RTL mode' => '', // 'Toggle RTL mode' => '',
// 'external services' => '',
// 'External services' => '',
// 'Send bookmarks to Pinboard' => '',
// 'Pinboard API token' => '',
// 'Pinboard tags' => '',
); );

View File

@ -217,4 +217,9 @@ return array(
'Must be an integer' => 'Must be an integer', 'Must be an integer' => 'Must be an integer',
'Remove automatically unread items' => 'Supprimer automatiquement les éléments non lus', 'Remove automatically unread items' => 'Supprimer automatiquement les éléments non lus',
'Toggle RTL mode' => 'Basculer entre le mode RTL', 'Toggle RTL mode' => 'Basculer entre le mode RTL',
'external services' => 'services externes',
'External services' => 'Services externes',
'Send bookmarks to Pinboard' => 'Envoyer les favoris vers Pinboard',
'Pinboard API token' => 'Jeton d\'accès à l\'API de Pinboard',
'Pinboard tags' => 'Tags Pinboard',
); );

View File

@ -217,4 +217,9 @@ return array(
// 'Must be an integer' => '', // 'Must be an integer' => '',
// 'Remove automatically unread items' => '', // 'Remove automatically unread items' => '',
// 'Toggle RTL mode' => '', // 'Toggle RTL mode' => '',
// 'external services' => '',
// 'External services' => '',
// 'Send bookmarks to Pinboard' => '',
// 'Pinboard API token' => '',
// 'Pinboard tags' => '',
); );

View File

@ -217,4 +217,9 @@ return array(
// 'Must be an integer' => '', // 'Must be an integer' => '',
// 'Remove automatically unread items' => '', // 'Remove automatically unread items' => '',
// 'Toggle RTL mode' => '', // 'Toggle RTL mode' => '',
// 'external services' => '',
// 'External services' => '',
// 'Send bookmarks to Pinboard' => '',
// 'Pinboard API token' => '',
// 'Pinboard tags' => '',
); );

View File

@ -217,4 +217,9 @@ return array(
// 'Must be an integer' => '', // 'Must be an integer' => '',
// 'Remove automatically unread items' => '', // 'Remove automatically unread items' => '',
// 'Toggle RTL mode' => '', // 'Toggle RTL mode' => '',
// 'external services' => '',
// 'External services' => '',
// 'Send bookmarks to Pinboard' => '',
// 'Pinboard API token' => '',
// 'Pinboard tags' => '',
); );

View File

@ -9,7 +9,7 @@ use PicoDb\Database;
use PicoFeed\Config\Config as ReaderConfig; use PicoFeed\Config\Config as ReaderConfig;
use PicoFeed\Logging\Logger; use PicoFeed\Logging\Logger;
const DB_VERSION = 30; const DB_VERSION = 31;
const HTTP_USER_AGENT = 'Miniflux (http://miniflux.net)'; const HTTP_USER_AGENT = 'Miniflux (http://miniflux.net)';
// Get PicoFeed config // Get PicoFeed config
@ -300,7 +300,10 @@ function get_all()
'items_sorting_direction', 'items_sorting_direction',
'items_display_mode', 'items_display_mode',
'redirect_nothing_to_read', 'redirect_nothing_to_read',
'auto_update_url' 'auto_update_url',
'pinboard_enabled',
'pinboard_token',
'pinboard_tags'
) )
->findOne(); ->findOne();
} }
@ -349,18 +352,24 @@ function save(array $values)
unset($values['confirmation']); unset($values['confirmation']);
// Reload configuration in session
$_SESSION['config'] = $values;
// Reload translations for flash session message
\Translator\load($values['language']);
// If the user does not want content of feeds, remove it in previous ones // If the user does not want content of feeds, remove it in previous ones
if (isset($values['nocontent']) && (bool) $values['nocontent']) { if (isset($values['nocontent']) && (bool) $values['nocontent']) {
Database::get('db')->table('items')->update(array('content' => '')); Database::get('db')->table('items')->update(array('content' => ''));
} }
return Database::get('db')->table('config')->update($values); if (Database::get('db')->table('config')->update($values)) {
reload();
return true;
}
return false;
}
// Reload the cache in session
function reload()
{
$_SESSION['config'] = get_all();
\Translator\load(get('language'));
} }
// Get the user agent of the connected user // Get the user agent of the connected user

View File

@ -2,6 +2,7 @@
namespace Model\Item; namespace Model\Item;
use Model\Service;
use Model\Config; use Model\Config;
use PicoDb\Database; use PicoDb\Database;
use PicoFeed\Logging\Logger; use PicoFeed\Logging\Logger;
@ -300,6 +301,10 @@ function set_status($status, array $items)
// Enable/disable bookmark flag // Enable/disable bookmark flag
function set_bookmark_value($id, $value) function set_bookmark_value($id, $value)
{ {
if ($value == 1) {
Service\push($id);
}
return Database::get('db') return Database::get('db')
->table('items') ->table('items')
->eq('id', $id) ->eq('id', $id)

View File

@ -5,6 +5,13 @@ namespace Schema;
use PDO; use PDO;
use Model\Config; use Model\Config;
function version_31($pdo)
{
$pdo->exec('ALTER TABLE config ADD COLUMN pinboard_enabled INTEGER DEFAULT 0');
$pdo->exec('ALTER TABLE config ADD COLUMN pinboard_token TEXT');
$pdo->exec('ALTER TABLE config ADD COLUMN pinboard_tags TEXT DEFAULT "miniflux"');
}
function version_30($pdo) function version_30($pdo)
{ {
$pdo->exec('ALTER TABLE config ADD COLUMN autoflush_unread INTEGER DEFAULT 45'); $pdo->exec('ALTER TABLE config ADD COLUMN autoflush_unread INTEGER DEFAULT 45');

46
models/service.php Normal file
View File

@ -0,0 +1,46 @@
<?php
namespace Model\Service;
use Model\Config;
use Model\Item;
use PicoFeed\Client\Client;
use PicoFeed\Client\ClientException;
// Sync the item to an external service
function push($item_id)
{
if ((bool) Config\get('pinboard_enabled')) {
pinboard_add(Item\get($item_id));
}
}
// Add a Pinboard bookmark
function pinboard_add(array $item)
{
return pinboard_api('/posts/add', array(
'url' => $item['url'],
'description' => $item['title'],
'tags' => Config\get('pinboard_tags'),
));
}
// Pinboard API client
function pinboard_api($method, array $params)
{
try {
$params += array('auth_token' => Config\get('pinboard_token'), 'format' => 'json');
$url = 'https://api.pinboard.in/v1'.$method.'?'.http_build_query($params);
$client = Client::getInstance();
$client->setUserAgent(Config\HTTP_USER_AGENT);
$client->execute($url);
$response = json_decode($client->getContent(), true);
return is_array($response) && $response['result_code'] === 'done';
}
catch (ClientException $e) {
return false;
}
}

View File

@ -2,6 +2,7 @@
<h2><?= t('About') ?></h2> <h2><?= t('About') ?></h2>
<ul> <ul>
<li><a href="?action=config"><?= t('settings') ?></a></li> <li><a href="?action=config"><?= t('settings') ?></a></li>
<li><a href="?action=services"><?= t('external services') ?></a></li>
<li><a href="?action=help"><?= t('help') ?></a></li> <li><a href="?action=help"><?= t('help') ?></a></li>
<li><a href="?action=database"><?= t('database') ?></a></li> <li><a href="?action=database"><?= t('database') ?></a></li>
<li><a href="?action=api"><?= t('api') ?></a></li> <li><a href="?action=api"><?= t('api') ?></a></li>

View File

@ -2,6 +2,7 @@
<h2><?= t('API') ?></h2> <h2><?= t('API') ?></h2>
<ul> <ul>
<li><a href="?action=config"><?= t('settings') ?></a></li> <li><a href="?action=config"><?= t('settings') ?></a></li>
<li><a href="?action=services"><?= t('external services') ?></a></li>
<li><a href="?action=about"><?= t('about') ?></a></li> <li><a href="?action=about"><?= t('about') ?></a></li>
<li><a href="?action=help"><?= t('help') ?></a></li> <li><a href="?action=help"><?= t('help') ?></a></li>
<li><a href="?action=database"><?= t('database') ?></a></li> <li><a href="?action=database"><?= t('database') ?></a></li>

View File

@ -1,6 +1,7 @@
<div class="page-header"> <div class="page-header">
<h2><?= t('Preferences') ?></h2> <h2><?= t('Preferences') ?></h2>
<ul> <ul>
<li><a href="?action=services"><?= t('external services') ?></a></li>
<li><a href="?action=about"><?= t('about') ?></a></li> <li><a href="?action=about"><?= t('about') ?></a></li>
<li><a href="?action=help"><?= t('help') ?></a></li> <li><a href="?action=help"><?= t('help') ?></a></li>
<li><a href="?action=database"><?= t('database') ?></a></li> <li><a href="?action=database"><?= t('database') ?></a></li>

View File

@ -1,8 +1,9 @@
<div class="page-header"> <div class="page-header">
<h2><?= t('Database') ?></h2> <h2><?= t('Database') ?></h2>
<ul> <ul>
<li><a href="?action=about"><?= t('about') ?></a></li>
<li><a href="?action=config"><?= t('settings') ?></a></li> <li><a href="?action=config"><?= t('settings') ?></a></li>
<li><a href="?action=services"><?= t('external services') ?></a></li>
<li><a href="?action=about"><?= t('about') ?></a></li>
<li><a href="?action=help"><?= t('help') ?></a></li> <li><a href="?action=help"><?= t('help') ?></a></li>
<li><a href="?action=api"><?= t('api') ?></a></li> <li><a href="?action=api"><?= t('api') ?></a></li>
</ul> </ul>

View File

@ -2,6 +2,7 @@
<h2><?= t('Help') ?></h2> <h2><?= t('Help') ?></h2>
<ul> <ul>
<li><a href="?action=config"><?= t('settings') ?></a></li> <li><a href="?action=config"><?= t('settings') ?></a></li>
<li><a href="?action=services"><?= t('external services') ?></a></li>
<li><a href="?action=about"><?= t('about') ?></a></li> <li><a href="?action=about"><?= t('about') ?></a></li>
<li><a href="?action=database"><?= t('database') ?></a></li> <li><a href="?action=database"><?= t('database') ?></a></li>
<li><a href="?action=api"><?= t('api') ?></a></li> <li><a href="?action=api"><?= t('api') ?></a></li>

28
templates/services.php Normal file
View File

@ -0,0 +1,28 @@
<div class="page-header">
<h2><?= t('External services') ?></h2>
<ul>
<li><a href="?action=config"><?= t('settings') ?></a></li>
<li><a href="?action=about"><?= t('about') ?></a></li>
<li><a href="?action=help"><?= t('help') ?></a></li>
<li><a href="?action=database"><?= t('database') ?></a></li>
<li><a href="?action=api"><?= t('api') ?></a></li>
</ul>
</div>
<section>
<form method="post" action="?action=services" autocomplete="off">
<?= Helper\form_hidden('csrf', $values) ?>
<?= Helper\form_checkbox('pinboard_enabled', t('Send bookmarks to Pinboard'), 1, isset($values['pinboard_enabled']) && $values['pinboard_enabled'] == 1) ?><br />
<?= Helper\form_label(t('Pinboard API token'), 'pinboard_token') ?>
<?= Helper\form_text('pinboard_token', $values, $errors) ?><br/>
<?= Helper\form_label(t('Pinboard tags'), 'pinboard_tags') ?>
<?= Helper\form_text('pinboard_tags', $values, $errors) ?><br/>
<div class="form-actions">
<input type="submit" value="<?= t('Save') ?>" class="btn btn-blue"/>
</div>
</form>
</section>

2
vendor/autoload.php vendored
View File

@ -4,4 +4,4 @@
require_once __DIR__ . '/composer' . '/autoload_real.php'; require_once __DIR__ . '/composer' . '/autoload_real.php';
return ComposerAutoloaderInit1f0385c01a6e1fee49b051180b96fd66::getLoader(); return ComposerAutoloaderInit75276362b3cf9253f7e5cb38a60a4693::getLoader();

View File

@ -8,6 +8,7 @@ $baseDir = dirname($vendorDir);
return array( return array(
$baseDir . '/lib/Translator.php', $baseDir . '/lib/Translator.php',
$baseDir . '/models/config.php', $baseDir . '/models/config.php',
$baseDir . '/models/service.php',
$baseDir . '/models/user.php', $baseDir . '/models/user.php',
$baseDir . '/models/feed.php', $baseDir . '/models/feed.php',
$baseDir . '/models/item.php', $baseDir . '/models/item.php',

View File

@ -2,7 +2,7 @@
// autoload_real.php @generated by Composer // autoload_real.php @generated by Composer
class ComposerAutoloaderInit1f0385c01a6e1fee49b051180b96fd66 class ComposerAutoloaderInit75276362b3cf9253f7e5cb38a60a4693
{ {
private static $loader; private static $loader;
@ -19,9 +19,9 @@ class ComposerAutoloaderInit1f0385c01a6e1fee49b051180b96fd66
return self::$loader; return self::$loader;
} }
spl_autoload_register(array('ComposerAutoloaderInit1f0385c01a6e1fee49b051180b96fd66', 'loadClassLoader'), true, true); spl_autoload_register(array('ComposerAutoloaderInit75276362b3cf9253f7e5cb38a60a4693', 'loadClassLoader'), true, true);
self::$loader = $loader = new \Composer\Autoload\ClassLoader(); self::$loader = $loader = new \Composer\Autoload\ClassLoader();
spl_autoload_unregister(array('ComposerAutoloaderInit1f0385c01a6e1fee49b051180b96fd66', 'loadClassLoader')); spl_autoload_unregister(array('ComposerAutoloaderInit75276362b3cf9253f7e5cb38a60a4693', 'loadClassLoader'));
$map = require __DIR__ . '/autoload_namespaces.php'; $map = require __DIR__ . '/autoload_namespaces.php';
foreach ($map as $namespace => $path) { foreach ($map as $namespace => $path) {
@ -42,14 +42,14 @@ class ComposerAutoloaderInit1f0385c01a6e1fee49b051180b96fd66
$includeFiles = require __DIR__ . '/autoload_files.php'; $includeFiles = require __DIR__ . '/autoload_files.php';
foreach ($includeFiles as $file) { foreach ($includeFiles as $file) {
composerRequire1f0385c01a6e1fee49b051180b96fd66($file); composerRequire75276362b3cf9253f7e5cb38a60a4693($file);
} }
return $loader; return $loader;
} }
} }
function composerRequire1f0385c01a6e1fee49b051180b96fd66($file) function composerRequire75276362b3cf9253f7e5cb38a60a4693($file)
{ {
require $file; require $file;
} }

View File

@ -116,45 +116,6 @@
"description": "Minimalist database query builder", "description": "Minimalist database query builder",
"homepage": "https://github.com/fguillot/picoDb" "homepage": "https://github.com/fguillot/picoDb"
}, },
{
"name": "fguillot/picofeed",
"version": "dev-master",
"version_normalized": "9999999-dev",
"source": {
"type": "git",
"url": "https://github.com/fguillot/picoFeed.git",
"reference": "11589851f91cc3f04c84ba873484486d1457e638"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/fguillot/picoFeed/zipball/11589851f91cc3f04c84ba873484486d1457e638",
"reference": "11589851f91cc3f04c84ba873484486d1457e638",
"shasum": ""
},
"require": {
"php": ">=5.3.0"
},
"time": "2014-12-22 03:23:04",
"type": "library",
"installation-source": "dist",
"autoload": {
"psr-0": {
"PicoFeed": "lib/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"Unlicense"
],
"authors": [
{
"name": "Frédéric Guillot",
"homepage": "http://fredericguillot.com"
}
],
"description": "Modern library to write or read feeds (RSS/Atom)",
"homepage": "http://fguillot.github.io/picoFeed"
},
{ {
"name": "fguillot/picofarad", "name": "fguillot/picofarad",
"version": "dev-master", "version": "dev-master",
@ -193,5 +154,44 @@
], ],
"description": "Minimalist micro-framework", "description": "Minimalist micro-framework",
"homepage": "https://github.com/fguillot/picoFarad" "homepage": "https://github.com/fguillot/picoFarad"
},
{
"name": "fguillot/picofeed",
"version": "dev-master",
"version_normalized": "9999999-dev",
"source": {
"type": "git",
"url": "https://github.com/fguillot/picoFeed.git",
"reference": "2d538a140020794fad248ccc0f88bb29263a61d6"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/fguillot/picoFeed/zipball/2d538a140020794fad248ccc0f88bb29263a61d6",
"reference": "2d538a140020794fad248ccc0f88bb29263a61d6",
"shasum": ""
},
"require": {
"php": ">=5.3.0"
},
"time": "2014-12-24 02:16:53",
"type": "library",
"installation-source": "dist",
"autoload": {
"psr-0": {
"PicoFeed": "lib/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"Unlicense"
],
"authors": [
{
"name": "Frédéric Guillot",
"homepage": "http://fredericguillot.com"
}
],
"description": "Modern library to write or read feeds (RSS/Atom)",
"homepage": "http://fguillot.github.io/picoFeed"
} }
] ]