Skip to content

Commit e9d6f9f

Browse files
author
Christian Putzke
committed
First running version of Pocket Button
1 parent 4ebb209 commit e9d6f9f

File tree

11 files changed

+384
-2
lines changed

11 files changed

+384
-2
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
.DS_Store
Lines changed: 137 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,137 @@
1+
<?php
2+
3+
class FreshExtension_pocketButton_Controller extends Minz_ActionController
4+
{
5+
public function authorizeAction()
6+
{
7+
$post_data = array(
8+
'consumer_key' => FreshRSS_Context::$user_conf->pocket_consumer_key,
9+
'code' => FreshRSS_Context::$user_conf->pocket_request_token
10+
);
11+
12+
$result = $this->curlPostRequest('https://getpocket.com/v3/oauth/authorize', $post_data);
13+
$url_redirect = array('c' => 'extension', 'a' => 'configure', 'params' => array('e' => 'Pocket Button'));
14+
15+
if ($result['status'] == 200) {
16+
FreshRSS_Context::$user_conf->pocket_username = $result['response']->username;
17+
FreshRSS_Context::$user_conf->pocket_access_token = $result['response']->access_token;
18+
FreshRSS_Context::$user_conf->save();
19+
20+
// TODO: Add proper localization
21+
Minz_Request::good('authorized!', $url_redirect);
22+
} else {
23+
// TODO: Add proper error localizations
24+
if ($result['errorCode'] == 158) {
25+
Minz_Request::bad('aborted!', $url_redirect);
26+
} else {
27+
Minz_Request::bad('error' . $result['errorCode'], $url_redirect);
28+
}
29+
}
30+
}
31+
32+
public function requestAccessAction()
33+
{
34+
$post_data = array(
35+
'consumer_key' => FreshRSS_Context::$user_conf->pocket_consumer_key,
36+
'redirect_uri' => 'not_needed'
37+
);
38+
39+
$result = $this->curlPostRequest('https://getpocket.com/v3/oauth/request', $post_data);
40+
41+
if ($result['status'] == 200) {
42+
FreshRSS_Context::$user_conf->pocket_request_token = $result['response']->code;
43+
FreshRSS_Context::$user_conf->save();
44+
45+
$redirect_url = Minz_Url::display(array('c' => 'pocketButton', 'a' => 'authorize'), 'html', true);
46+
$redirect_url = str_replace('&', urlencode('&'), $redirect_url);
47+
$pocket_redirect_url = 'https://getpocket.com/auth/authorize?request_token=' . $result['response']->code . '&redirect_uri=' . $redirect_url;
48+
49+
Minz_Request::forward($pocket_redirect_url);
50+
} else {
51+
// TODO: Add proper error localizations
52+
$url_redirect = array('c' => 'extension', 'a' => 'configure', 'params' => array('e' => 'Pocket Button'));
53+
Minz_Request::bad('Error ' . $result['errorCode'], $url_redirect);
54+
}
55+
}
56+
57+
public function shareAction()
58+
{
59+
$this->view->_layout(false);
60+
61+
$entry_id = Minz_Request::param('id');
62+
$entry_dao = FreshRSS_Factory::createEntryDao();
63+
$entry = $entry_dao->searchById($entry_id);
64+
65+
if ($entry === null) {
66+
// TODO: Add proper error localizations
67+
echo json_encode(array(
68+
'content' => 'Entry with ID ' . $entry_id . ' not found!',
69+
'status' => 404
70+
));
71+
return;
72+
}
73+
74+
$post_data = array(
75+
'consumer_key' => FreshRSS_Context::$user_conf->pocket_consumer_key,
76+
'access_token' => FreshRSS_Context::$user_conf->pocket_access_token,
77+
'url' => $entry->link(),
78+
'title' => $entry->title(),
79+
'time' => time()
80+
);
81+
82+
$result = $this->curlPostRequest('https://getpocket.com/v3/add', $post_data);
83+
84+
echo json_encode($result);
85+
}
86+
87+
private function curlPostRequest($url, $post_data)
88+
{
89+
$headers = array(
90+
'Content-Type: application/json; charset=UTF-8',
91+
'X-Accept: application/json'
92+
);
93+
94+
$curl = curl_init();
95+
curl_setopt($curl, CURLOPT_URL, $url);
96+
curl_setopt($curl, CURLOPT_HTTPHEADER, $headers);
97+
curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);
98+
curl_setopt($curl, CURLOPT_HEADER, true);
99+
curl_setopt($curl, CURLOPT_POST, true);
100+
curl_setopt($curl, CURLOPT_POSTFIELDS, json_encode($post_data));
101+
102+
$response = curl_exec($curl);
103+
104+
$header_size = curl_getinfo($curl, CURLINFO_HEADER_SIZE);
105+
$response_header = substr($response, 0, $header_size);
106+
$response_body = substr($response, $header_size);
107+
$response_headers = $this->httpHeaderToArray($response_header);
108+
109+
return array(
110+
'response' => json_decode($response_body),
111+
'status' => curl_getinfo($curl, CURLINFO_HTTP_CODE),
112+
'errorCode' => isset($response_headers['x-error-code']) ? intval($response_headers['x-error-code']) : 0
113+
);
114+
}
115+
116+
private function httpHeaderToArray($header)
117+
{
118+
$headers = array();
119+
$headers_parts = explode("\r\n", $header);
120+
121+
foreach ($headers_parts as $header_part) {
122+
// skip empty header parts
123+
if (strlen($header_part) <= 0) {
124+
continue;
125+
}
126+
127+
// Filter the beginning of the header which is the basic HTTP status code
128+
if (strpos($header_part, ':')) {
129+
$header_name = substr($header_part, 0, strpos($header_part, ':'));
130+
$header_value = substr($header_part, strpos($header_part, ':') + 1);
131+
$headers[$header_name] = trim($header_value);
132+
}
133+
}
134+
135+
return $headers;
136+
}
137+
}

README.md

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,5 @@
1-
# freshrss-pocket-button
2-
Add FreshRSS articles to Pocket with one simple button click or a keyboard shortcut.
1+
# FreshRSS Pocket Button
2+
3+
Add [FreshRSS](https://freshrss.org) articles to [Pocket](https://getpocket.com/) with one simple button click or a keyboard shortcut.
4+
5+
More coming soon...

configure.phtml

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
<?php
2+
$consumer_key = FreshRSS_Context::$user_conf->pocket_consumer_key;
3+
$username = FreshRSS_Context::$user_conf->pocket_username;
4+
$access_token = FreshRSS_Context::$user_conf->pocket_access_token;
5+
?>
6+
7+
<form action="<?php echo _url('extension', 'configure', 'e', urlencode($this->getName())); ?>" method="post">
8+
<input type="hidden" name="_csrf" value="<?php echo FreshRSS_Auth::csrfToken(); ?>" />
9+
<div class="form-group">
10+
<label class="group-name" for="consumer_key"><?php echo _t('ext.pocketButton.consumer_key'); ?></label>
11+
<div class="group-controls">
12+
<input type="text" name="consumer_key" id="consumer_key" value="<?php echo $consumer_key; ?>">
13+
</div>
14+
</div>
15+
16+
<div class="form-group form-actions">
17+
<div class="group-controls">
18+
<button type="submit" class="btn btn-important"><?php echo _t('gen.action.submit'); ?></button>
19+
</div>
20+
</div>
21+
</form>
22+
23+
<?php if (!empty($consumer_key) && $this->isEnabled()): ?>
24+
<form action="<?php echo _url('pocketButton', 'requestAccess'); ?>" method="post">
25+
<input type="hidden" name="_csrf" value="<?php echo FreshRSS_Auth::csrfToken(); ?>" />
26+
<div class="form-group">
27+
<label class="group-name"><?php echo _t('ext.pocketButton.consumer_key'); ?></label>
28+
<div class="group-controls">
29+
<?php echo $consumer_key; ?>
30+
</div>
31+
</div>
32+
<div class="form-group form-actions">
33+
<div class="group-controls">
34+
<button type="submit" class="btn btn-important"><?php echo _t('ext.pocketButton.connect_to_pocket'); ?></button>
35+
</div>
36+
</div>
37+
</form>
38+
<?php endif ?>
39+
40+
<?php if (!empty($username)): ?>
41+
<div class="form-group">
42+
<label class="group-name"><?php echo _t('ext.pocketButton.username'); ?></label>
43+
<div class="group-controls">
44+
<?php echo $username; ?>
45+
</div>
46+
</div>
47+
<?php endif ?>
48+
<?php if (!empty($access_token)): ?>
49+
<div class="form-group">
50+
<label class="group-name"><?php echo _t('ext.pocketButton.access_token'); ?></label>
51+
<div class="group-controls">
52+
<?php echo $access_token; ?>
53+
</div>
54+
</div>
55+
<?php endif ?>

extension.php

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
<?php
2+
3+
class PocketButtonExtension extends Minz_Extension {
4+
public function init() {
5+
$this->registerTranslates();
6+
7+
Minz_View::appendScript($this->getFileUrl('jquerymin.js', 'js'), false, false, false);
8+
Minz_View::appendScript($this->getFileUrl('script.js', 'js'), false, false, false);
9+
10+
$this->registerController('pocketButton');
11+
$this->registerViews();
12+
}
13+
14+
public function handleConfigureAction() {
15+
if (Minz_Request::isPost()) {
16+
$consumer_key = Minz_Request::param('consumer_key', '');
17+
FreshRSS_Context::$user_conf->pocket_consumer_key = $consumer_key;
18+
FreshRSS_Context::$user_conf->save();
19+
}
20+
}
21+
}

i18n/en/ext.php

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
<?php
2+
3+
return array(
4+
'pocketButton' => array(
5+
'consumer_key' => 'Consumer Key',
6+
'connect_to_pocket' => 'Connect to Pocket',
7+
'username' => 'Username',
8+
'access_token' => 'Access Token',
9+
),
10+
);

metadata.json

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
{
2+
"name": "Pocket Button",
3+
"author": "Christian Putzke",
4+
"description": "Add articles to Pocket with one simple button click or a keyboard shortcut.",
5+
"version": 0.1,
6+
"entrypoint": "PocketButton",
7+
"type": "user"
8+
}

static/add_to_pocket.svg

Lines changed: 1 addition & 0 deletions
Loading

static/jquerymin.js

Lines changed: 2 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

static/script.js

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
function add_to_pocket(active) {
2+
if (active.length === 0) {
3+
return false;
4+
}
5+
6+
var pocketButton = active.find("a.pocketButton");
7+
var url = pocketButton.attr("href");
8+
if (url === undefined) {
9+
return false;
10+
}
11+
pocketButton.text('.');
12+
13+
if (pending_entries[active.attr('id')]) {
14+
return false;
15+
}
16+
17+
pending_entries[active.attr('id')] = true;
18+
19+
$.ajax({
20+
type: 'POST',
21+
url: url,
22+
data: {
23+
ajax: true,
24+
_csrf: context.csrf,
25+
},
26+
}).done(function(data) {
27+
let response = JSON.parse(data);
28+
delete pending_entries[active.attr('id')];
29+
pocketButton.text('✓');
30+
31+
if (response.status === 200) {
32+
// TODO: Add loca
33+
openNotification("worked :)", 'good');
34+
} else {
35+
// TODO: Add loca
36+
openNotification("failed :(", 'bad');
37+
}
38+
}).fail(function(data) {
39+
let response = JSON.parse(data);
40+
41+
// TODO: Add loca
42+
openNotification("failed :(", 'bad');
43+
delete pending_entries[active.attr('id')];
44+
});
45+
}
46+
47+
$(document).ready(function() {
48+
$('#stream .flux a.pocketButton').on('click', function() {
49+
var active = $(this).parents(".flux");
50+
add_to_pocket(active);
51+
return false;
52+
});
53+
54+
$(this).keydown(function(e) {
55+
if (e.target.closest('input, textarea') || e.ctrlKey || e.metaKey || e.altKey || e.shiftKey) {
56+
return true;
57+
}
58+
59+
var active = $('#stream .flux.active');
60+
var shortcut = 'b'; // TODO: Fix hardcoded shortcut
61+
62+
if (e.key === shortcut) {
63+
add_to_pocket(active);
64+
}
65+
});
66+
});

0 commit comments

Comments
 (0)