Compare commits

...

13 commits

Author SHA1 Message Date
Hypolite Petovan c4876d46b4 Merge pull request 'Tumblr: Added support for hook "support_probe"' (#1376) from heluecht/friendica-addons:tumblr-support-probe into develop
Reviewed-on: friendica/friendica-addons#1376
2023-04-27 13:59:07 +02:00
Michael 8400258f07 Tumblr: Added support for hook "support_probe" 2023-04-27 05:28:05 +00:00
Tobias Diekershoff 8f29a51277 Merge branch 'develop' into stable 2023-04-27 07:24:05 +02:00
Tobias Diekershoff b34c9131ca recovered [mailstream] various modernisations
These changes where made on github as #1354 but got lost due mirroring
issued from the development branch. Original author is mexon.

https://github.com/friendica/friendica-addons/pull/1354
2023-04-27 07:21:20 +02:00
Tobias Diekershoff a733e82c21 Merge pull request 'Tumblr: Small fixes' (#1375) from heluecht/friendica-addons:tumblr-fixes into develop
Reviewed-on: friendica/friendica-addons#1375
2023-04-27 07:12:19 +02:00
Michael 5d8a53b8ec Tumblr: Small fixes 2023-04-27 05:00:04 +00:00
Hypolite Petovan dac297b942 Merge pull request 'Tumblr: We can now follow, unfollow, add posts, ...' (#1374) from heluecht/friendica-addons:tumblr-complete into develop
Reviewed-on: friendica/friendica-addons#1374
2023-04-27 01:39:01 +02:00
Michael dc02e16575 Tumblr: We can now follow, unfollow, add posts, ... 2023-04-26 22:15:59 +00:00
Hypolite Petovan d8af084933 Merge pull request 'Tumblr is now using OAuth2' (#1373) from heluecht/friendica-addons:tumblr-oauth2 into develop
Reviewed-on: friendica/friendica-addons#1373
2023-04-25 21:15:07 +02:00
Michael 477e646fba Further updated the readme 2023-04-25 18:48:58 +00:00
Michael 5307ef3a58 Updated readme 2023-04-25 18:47:54 +00:00
Michael 4c3b4dea96 Improved result check 2023-04-25 18:38:18 +00:00
Michael c811f54958 Tumblr is now using OAuth2 2023-04-25 18:33:39 +00:00
12 changed files with 646 additions and 523 deletions

View file

@ -29,68 +29,72 @@ msgstr ""
msgid "Save Settings"
msgstr ""
#: mailstream.php:301
#: mailstream.php:311
msgid "Re:"
msgstr ""
#: mailstream.php:314 mailstream.php:317
#: mailstream.php:324 mailstream.php:327
msgid "Friendica post"
msgstr ""
#: mailstream.php:320
#: mailstream.php:330
msgid "Diaspora post"
msgstr ""
#: mailstream.php:330
#: mailstream.php:340
msgid "Feed item"
msgstr ""
#: mailstream.php:333
#: mailstream.php:343
msgid "Email"
msgstr ""
#: mailstream.php:335
#: mailstream.php:345
msgid "Friendica Item"
msgstr ""
#: mailstream.php:404
#: mailstream.php:419
msgid "Upstream"
msgstr ""
#: mailstream.php:405
#: mailstream.php:420
msgid "URI"
msgstr ""
#: mailstream.php:421
msgid "Local"
msgstr ""
#: mailstream.php:481
#: mailstream.php:499
msgid "Enabled"
msgstr ""
#: mailstream.php:486
#: mailstream.php:504
msgid "Email Address"
msgstr ""
#: mailstream.php:488
#: mailstream.php:506
msgid "Leave blank to use your account email address"
msgstr ""
#: mailstream.php:492
#: mailstream.php:510
msgid "Exclude Likes"
msgstr ""
#: mailstream.php:494
#: mailstream.php:512
msgid "Check this to omit mailing \"Like\" notifications"
msgstr ""
#: mailstream.php:498
#: mailstream.php:516
msgid "Attach Images"
msgstr ""
#: mailstream.php:500
#: mailstream.php:518
msgid ""
"Download images in posts and attach them to the email. Useful for reading "
"email while offline."
msgstr ""
#: mailstream.php:507
#: mailstream.php:525
msgid "Mail Stream Settings"
msgstr ""

View file

@ -4,96 +4,77 @@
#
#
# Translators:
# Aditoo, 2018
# michal_s <msupler@gmail.com>, 2014
# Michal Šupler <msupler@gmail.com>, 2014
msgid ""
msgstr ""
"Project-Id-Version: friendica\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2021-11-21 19:15-0500\n"
"PO-Revision-Date: 2014-06-23 09:54+0000\n"
"Last-Translator: Aditoo, 2018\n"
"Language-Team: Czech (http://app.transifex.com/Friendica/friendica/language/cs/)\n"
"POT-Creation-Date: 2014-06-23 14:45+0200\n"
"PO-Revision-Date: 2014-07-07 19:19+0000\n"
"Last-Translator: Michal Šupler <msupler@gmail.com>\n"
"Language-Team: Czech (http://www.transifex.com/projects/p/friendica/language/cs/)\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Language: cs\n"
"Plural-Forms: nplurals=4; plural=(n == 1 && n % 1 == 0) ? 0 : (n >= 2 && n <= 4 && n % 1 == 0) ? 1: (n % 1 != 0 ) ? 2 : 3;\n"
"Plural-Forms: nplurals=3; plural=(n==1) ? 0 : (n>=2 && n<=4) ? 1 : 2;\n"
#: mailstream.php:77
#: mailstream.php:55
msgid "From Address"
msgstr "Adresa odesílatele"
#: mailstream.php:79
#: mailstream.php:57
msgid "Email address that stream items will appear to be from."
msgstr "Adresa, která vysílá položky, se objeví jako odesílatel."
#: mailstream.php:82
#: mailstream.php:60 mailstream.php:282
msgid "Save Settings"
msgstr "Uložit nastavení"
msgstr "Uložit Nastavení"
#: mailstream.php:301
#: mailstream.php:165
msgid "Re:"
msgstr "Re:"
#: mailstream.php:314 mailstream.php:317
#: mailstream.php:173
msgid "Friendica post"
msgstr "Příspěvek z Friendica"
msgstr "Friendica příspěvky"
#: mailstream.php:320
#: mailstream.php:176
msgid "Diaspora post"
msgstr "Příspěvek z Diaspora"
msgstr "Diaspora příspvěvky"
#: mailstream.php:330
#: mailstream.php:183
msgid "Feed item"
msgstr "Položka kanálu"
msgstr "Zdrojová položka"
#: mailstream.php:333
#: mailstream.php:186
msgid "Email"
msgstr "E-mail"
#: mailstream.php:335
#: mailstream.php:188
msgid "Friendica Item"
msgstr "Položka z Friendica"
msgstr "Friendica položka"
#: mailstream.php:404
#: mailstream.php:229
msgid "Upstream"
msgstr "Upstream"
#: mailstream.php:405
#: mailstream.php:230
msgid "Local"
msgstr "Místní"
msgstr "Lokální"
#: mailstream.php:481
msgid "Enabled"
msgstr "Povoleno"
#: mailstream.php:486
#: mailstream.php:274
msgid "Email Address"
msgstr "E-mailová adresa"
#: mailstream.php:488
#: mailstream.php:276
msgid "Leave blank to use your account email address"
msgstr "Ponechte prázdné pro použití vaší e-mailové adresy"
#: mailstream.php:492
msgid "Exclude Likes"
msgstr "Vynechávat \"lajky\""
#: mailstream.php:279
msgid "Enabled"
msgstr "Povoleno"
#: mailstream.php:494
msgid "Check this to omit mailing \"Like\" notifications"
msgstr "Zaškrtnutím vypnete posílání oznámení o \"To se mi líbí\""
#: mailstream.php:498
msgid "Attach Images"
msgstr "Připojit obrázky"
#: mailstream.php:500
msgid ""
"Download images in posts and attach them to the email. Useful for reading "
"email while offline."
msgstr "Stahovat obrázky v příspěvcích a připojovat je k e-mailu. Užitečné pro čtení e-mailu, když jste offline."
#: mailstream.php:507
#: mailstream.php:281
msgid "Mail Stream Settings"
msgstr "Nastavení Mail Stream"
msgstr "Mail Stream nastavení"

View file

@ -3,24 +3,20 @@
if(! function_exists("string_plural_select_cs")) {
function string_plural_select_cs($n){
$n = intval($n);
if (($n == 1 && $n % 1 == 0)) { return 0; } else if (($n >= 2 && $n <= 4 && $n % 1 == 0)) { return 1; } else if (($n % 1 != 0 )) { return 2; } else { return 3; }
if (($n==1)) { return 0; } else if (($n>=2 && $n<=4)) { return 1; } else { return 2; }
}}
$a->strings['From Address'] = 'Adresa odesílatele';
$a->strings['Email address that stream items will appear to be from.'] = 'Adresa, která vysílá položky, se objeví jako odesílatel.';
$a->strings['Save Settings'] = 'Uložit nastavení';
$a->strings['Save Settings'] = 'Uložit Nastavení';
$a->strings['Re:'] = 'Re:';
$a->strings['Friendica post'] = 'Příspěvek z Friendica';
$a->strings['Diaspora post'] = 'Příspěvek z Diaspora';
$a->strings['Feed item'] = 'Položka kanálu';
$a->strings['Friendica post'] = 'Friendica příspěvky';
$a->strings['Diaspora post'] = 'Diaspora příspvěvky';
$a->strings['Feed item'] = 'Zdrojová položka';
$a->strings['Email'] = 'E-mail';
$a->strings['Friendica Item'] = 'Položka z Friendica';
$a->strings['Friendica Item'] = 'Friendica položka';
$a->strings['Upstream'] = 'Upstream';
$a->strings['Local'] = 'Místní';
$a->strings['Enabled'] = 'Povoleno';
$a->strings['Local'] = 'Lokální';
$a->strings['Email Address'] = 'E-mailová adresa';
$a->strings['Leave blank to use your account email address'] = 'Ponechte prázdné pro použití vaší e-mailové adresy';
$a->strings['Exclude Likes'] = 'Vynechávat "lajky"';
$a->strings['Check this to omit mailing "Like" notifications'] = 'Zaškrtnutím vypnete posílání oznámení o "To se mi líbí"';
$a->strings['Attach Images'] = 'Připojit obrázky';
$a->strings['Download images in posts and attach them to the email. Useful for reading email while offline.'] = 'Stahovat obrázky v příspěvcích a připojovat je k e-mailu. Užitečné pro čtení e-mailu, když jste offline.';
$a->strings['Mail Stream Settings'] = 'Nastavení Mail Stream';
$a->strings['Enabled'] = 'Povoleno';
$a->strings['Mail Stream Settings'] = 'Mail Stream nastavení';

View file

@ -5,17 +5,16 @@
#
# Translators:
# Andreas H., 2014
# foss <oss@disr.it>, 2022
# Tobias Diekershoff <tobias.diekershoff@gmx.net>, 2018
# Ulf Rompe <transifex.com@rompe.org>, 2019
msgid ""
msgstr ""
"Project-Id-Version: friendica\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2021-11-21 19:15-0500\n"
"PO-Revision-Date: 2014-06-23 09:54+0000\n"
"Last-Translator: foss <oss@disr.it>, 2022\n"
"Language-Team: German (http://app.transifex.com/Friendica/friendica/language/de/)\n"
"POT-Creation-Date: 2018-03-11 19:13+0100\n"
"PO-Revision-Date: 2019-02-18 15:05+0000\n"
"Last-Translator: Ulf Rompe <transifex.com@rompe.org>\n"
"Language-Team: German (http://www.transifex.com/Friendica/friendica/language/de/)\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
@ -30,72 +29,72 @@ msgstr "Absender"
msgid "Email address that stream items will appear to be from."
msgstr "E-Mail-Adresse, die in hochgeladenen Artikeln erscheint."
#: mailstream.php:82
#: mailstream.php:82 mailstream.php:380
msgid "Save Settings"
msgstr "Einstellungen speichern"
#: mailstream.php:301
#: mailstream.php:223
msgid "Re:"
msgstr "Re:"
#: mailstream.php:314 mailstream.php:317
#: mailstream.php:231
msgid "Friendica post"
msgstr "Friendica-Veröffentlichung"
#: mailstream.php:320
#: mailstream.php:234
msgid "Diaspora post"
msgstr "Diaspora-Veröffentlichung"
#: mailstream.php:330
#: mailstream.php:244
msgid "Feed item"
msgstr "Artikel-Feed"
#: mailstream.php:333
#: mailstream.php:247
msgid "Email"
msgstr "E-Mail"
#: mailstream.php:335
#: mailstream.php:249
msgid "Friendica Item"
msgstr "Friendica-Artikel"
#: mailstream.php:404
#: mailstream.php:293
msgid "Upstream"
msgstr "Upstream"
#: mailstream.php:405
#: mailstream.php:294
msgid "Local"
msgstr "Lokal"
#: mailstream.php:481
#: mailstream.php:362
msgid "Enabled"
msgstr "Aktiv"
msgstr "eingeschaltet"
#: mailstream.php:486
#: mailstream.php:366
msgid "Email Address"
msgstr "E-Mail-Adresse"
#: mailstream.php:488
#: mailstream.php:368
msgid "Leave blank to use your account email address"
msgstr "Leer lassen für deine Konto-E-Mail-Addresse"
#: mailstream.php:492
#: mailstream.php:371
msgid "Exclude Likes"
msgstr "Likes ignorieren"
#: mailstream.php:494
#: mailstream.php:373
msgid "Check this to omit mailing \"Like\" notifications"
msgstr "Diese Option verhindert das Versenden von \"Like\"-Benachrichtigungen per E-Mail."
#: mailstream.php:498
#: mailstream.php:376
msgid "Attach Images"
msgstr "Bilder anhängen"
#: mailstream.php:500
#: mailstream.php:378
msgid ""
"Download images in posts and attach them to the email. Useful for reading "
"email while offline."
msgstr "Sollen Bilder, die im Beitrag eingebettet sind, als Dateianhang in den E-Mails verschickt werden?"
#: mailstream.php:507
#: mailstream.php:379
msgid "Mail Stream Settings"
msgstr "Mail-Nachrichten-Einstellungen"

View file

@ -16,7 +16,7 @@ $a->strings['Email'] = 'E-Mail';
$a->strings['Friendica Item'] = 'Friendica-Artikel';
$a->strings['Upstream'] = 'Upstream';
$a->strings['Local'] = 'Lokal';
$a->strings['Enabled'] = 'Aktiv';
$a->strings['Enabled'] = 'eingeschaltet';
$a->strings['Email Address'] = 'E-Mail-Adresse';
$a->strings['Leave blank to use your account email address'] = 'Leer lassen für deine Konto-E-Mail-Addresse';
$a->strings['Exclude Likes'] = 'Likes ignorieren';

View file

@ -9,90 +9,90 @@ msgid ""
msgstr ""
"Project-Id-Version: friendica\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2021-11-21 19:15-0500\n"
"PO-Revision-Date: 2014-06-23 09:54+0000\n"
"Last-Translator: fabrixxm <fabrix.xm@gmail.com>, 2014,2018\n"
"Language-Team: Italian (http://app.transifex.com/Friendica/friendica/language/it/)\n"
"POT-Creation-Date: 2021-02-01 18:15+0100\n"
"PO-Revision-Date: 2018-03-19 13:12+0000\n"
"Last-Translator: fabrixxm <fabrix.xm@gmail.com>\n"
"Language-Team: Italian (http://www.transifex.com/Friendica/friendica/language/it/)\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Language: it\n"
"Plural-Forms: nplurals=3; plural=n == 1 ? 0 : n != 0 && n % 1000000 == 0 ? 1 : 2;\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
#: mailstream.php:77
#: mailstream.php:66
msgid "From Address"
msgstr "Indirizzo di invio"
#: mailstream.php:79
#: mailstream.php:68
msgid "Email address that stream items will appear to be from."
msgstr "Indirizzo email da cui i messaggi appariranno inviati"
#: mailstream.php:82
#: mailstream.php:71 mailstream.php:382
msgid "Save Settings"
msgstr "Salva Impostazioni"
#: mailstream.php:301
#: mailstream.php:225
msgid "Re:"
msgstr "R:"
#: mailstream.php:314 mailstream.php:317
#: mailstream.php:233
msgid "Friendica post"
msgstr "Messaggio Friendica"
#: mailstream.php:320
#: mailstream.php:236
msgid "Diaspora post"
msgstr "Messaggio Diaspora"
#: mailstream.php:330
#: mailstream.php:246
msgid "Feed item"
msgstr "Elemento da feed"
#: mailstream.php:333
#: mailstream.php:249
msgid "Email"
msgstr "Email"
#: mailstream.php:335
#: mailstream.php:251
msgid "Friendica Item"
msgstr "Elemento da Friendica"
#: mailstream.php:404
#: mailstream.php:296
msgid "Upstream"
msgstr "Upstream"
#: mailstream.php:405
#: mailstream.php:297
msgid "Local"
msgstr "Locale"
#: mailstream.php:481
#: mailstream.php:364
msgid "Enabled"
msgstr "Abilitato"
#: mailstream.php:486
#: mailstream.php:368
msgid "Email Address"
msgstr "Indirizzo Email"
#: mailstream.php:488
#: mailstream.php:370
msgid "Leave blank to use your account email address"
msgstr "Lascia in bianco per usare l'indirizzo email del tuo account"
#: mailstream.php:492
#: mailstream.php:373
msgid "Exclude Likes"
msgstr "Escludi \"Mi Piace\""
#: mailstream.php:494
#: mailstream.php:375
msgid "Check this to omit mailing \"Like\" notifications"
msgstr "Seleziona per evitare di inviare notifiche per \"Mi Piace\""
#: mailstream.php:498
#: mailstream.php:378
msgid "Attach Images"
msgstr "Allega Immagini"
#: mailstream.php:500
#: mailstream.php:380
msgid ""
"Download images in posts and attach them to the email. Useful for reading "
"email while offline."
msgstr "Scarica le immagini nei messaggi e le allega alle email. Utile per leggere le email mentre si è offline."
#: mailstream.php:507
#: mailstream.php:381
msgid "Mail Stream Settings"
msgstr "Impostazioni Mail Stream"

View file

@ -3,7 +3,7 @@
if(! function_exists("string_plural_select_it")) {
function string_plural_select_it($n){
$n = intval($n);
if ($n == 1) { return 0; } else if ($n != 0 && $n % 1000000 == 0) { return 1; } else { return 2; }
return intval($n != 1);
}}
$a->strings['From Address'] = 'Indirizzo di invio';
$a->strings['Email address that stream items will appear to be from.'] = 'Indirizzo email da cui i messaggi appariranno inviati';

View file

@ -32,7 +32,6 @@ function mailstream_install()
Hook::register('addon_settings_post', 'addon/mailstream/mailstream.php', 'mailstream_addon_settings_post');
Hook::register('post_local_end', 'addon/mailstream/mailstream.php', 'mailstream_post_hook');
Hook::register('post_remote_end', 'addon/mailstream/mailstream.php', 'mailstream_post_hook');
Hook::register('cron', 'addon/mailstream/mailstream.php', 'mailstream_cron');
Hook::register('mailstream_send_hook', 'addon/mailstream/mailstream.php', 'mailstream_send_hook');
Logger::info("mailstream: installed");
@ -67,9 +66,10 @@ function mailstream_module() {}
/**
* Adds an item in "addon features" in the admin menu of the site
*
* @param App $a App object (unused)
* @param string $o HTML form data
*/
function mailstream_addon_admin(string &$o)
function mailstream_addon_admin(App $a, string &$o)
{
$frommail = DI::config()->get('mailstream', 'frommail');
$template = Renderer::getMarkupTemplate('admin.tpl', 'addon/mailstream/');
@ -103,14 +103,14 @@ function mailstream_addon_admin_post()
*/
function mailstream_generate_id(string $uri): string
{
$host = DI::baseUrl()->getHost();
$host = DI::baseUrl()->getHostname();
$resource = hash('md5', $uri);
$message_id = "<" . $resource . "@" . $host . ">";
Logger::debug('mailstream: Generated message ID ' . $message_id . ' for URI ' . $uri);
return $message_id;
}
function mailstream_send_hook(array $data)
function mailstream_send_hook(App $a, array $data)
{
$criteria = array('uid' => $data['uid'], 'contact-id' => $data['contact-id'], 'uri' => $data['uri']);
$item = Post::selectFirst([], $criteria);
@ -138,15 +138,16 @@ function mailstream_send_hook(array $data)
* mailstream is enabled and the necessary data is available, forks a
* workerqueue item to send the email.
*
* @param App $a App object (unused)
* @param array $item content of the item (may or may not already be stored in the item table)
* @return void
*/
function mailstream_post_hook(array &$item)
function mailstream_post_hook(App $a, array &$item)
{
mailstream_check_version();
if (!DI::pConfig()->get($item['uid'], 'mailstream', 'enabled')) {
Logger::debug('mailstream: not enabled for item ' . $item['id']);
Logger::debug('mailstream: not enabled.', ['item' => $item['id'], ' uid ' => $item['uid']]);
return;
}
if (!$item['uid']) {
@ -161,8 +162,8 @@ function mailstream_post_hook(array &$item)
Logger::debug('mailstream: no uri for item ' . $item['id']);
return;
}
if (!$item['plink']) {
Logger::debug('mailstream: no plink for item ' . $item['id']);
if ($item['verb'] == Activity::ANNOUNCE) {
Logger::debug('mailstream: announce item ', ['item' => $item['id']]);
return;
}
if (DI::pConfig()->get($item['uid'], 'mailstream', 'nolikes')) {
@ -395,7 +396,9 @@ function mailstream_send(string $message_id, array $item, array $user): bool
$mail->addCustomHeader('In-Reply-To: ' . mailstream_generate_id($item['thr-parent']));
}
$mail->addCustomHeader('X-Friendica-Mailstream-URI: ' . $item['uri']);
$mail->addCustomHeader('X-Friendica-Mailstream-Plink: ' . $item['plink']);
if ($item['plink']) {
$mail->addCustomHeader('X-Friendica-Mailstream-Plink: ' . $item['plink']);
}
$encoding = 'base64';
foreach ($attachments as $url => $image) {
$mail->AddStringEmbeddedImage(
@ -411,12 +414,13 @@ function mailstream_send(string $message_id, array $item, array $user): bool
$template = Renderer::getMarkupTemplate('mail.tpl', 'addon/mailstream/');
$mail->AltBody = BBCode::toPlaintext($item['body']);
$item['body'] = BBCode::convertForUriId($item['uri-id'], $item['body'], BBCode::CONNECTORS);
$item['url'] = DI::baseUrl() . '/display/' . $item['guid'];
$item['url'] = DI::baseUrl()->get() . '/display/' . $item['guid'];
$mail->Body = Renderer::replaceMacros($template, [
'$upstream' => DI::l10n()->t('Upstream'),
'$uri' => DI::l10n()->t('URI'),
'$local' => DI::l10n()->t('Local'),
'$item' => $item]);
mailstream_html_wrap($mail->Body);
$mail->Body = mailstream_html_wrap($mail->Body);
if (!$mail->Send()) {
throw new Exception($mail->ErrorInfo);
}
@ -437,7 +441,8 @@ function mailstream_send(string $message_id, array $item, array $user): bool
* bbcode's output suitable for transmission, we try to break things
* up so that lines are about 200 characters.
*
* @param string $text text to word wrap - modified in-place
* @param string $text text to word wrap
* @return string wrapped text
*/
function mailstream_html_wrap(string &$text)
{
@ -446,6 +451,7 @@ function mailstream_html_wrap(string &$text)
$lines[$i] = preg_replace('/ /', "\n", $lines[$i], 1);
}
$text = implode($lines);
return $text;
}
/**
@ -462,8 +468,7 @@ function mailstream_convert_table_entries()
'message_id' => $ms_item_id['message-id'],
'tries' => 0);
if (!$ms_item_id['message-id'] || !strlen($ms_item_id['message-id'])) {
Logger::info('mailstream_cron: Item ' .
$ms_item_id['id'] . ' URI ' . $ms_item_id['uri'] . ' has no message-id');
Logger::info('mailstream_convert_table_entries: item has no message-id.', 'item' => $ms_item_id['id'], 'uri' => $ms_item_id['uri']]);
continue;
}
Logger::info('mailstream_convert_table_entries: convert item to workerqueue', $send_hook_data);
@ -475,10 +480,11 @@ function mailstream_convert_table_entries()
/**
* Form for configuring mailstream features for a user
*
* @param App $a App object
* @param array $data Hook data array
* @throws \Friendica\Network\HTTPException\ServiceUnavailableException
*/
function mailstream_addon_settings(array &$data)
function mailstream_addon_settings(App &$a, array &$data)
{
$enabled = DI::pConfig()->get(DI::userSession()->getLocalUserId(), 'mailstream', 'enabled');
$address = DI::pConfig()->get(DI::userSession()->getLocalUserId(), 'mailstream', 'address');
@ -522,10 +528,11 @@ function mailstream_addon_settings(array &$data)
/**
* Process data submitted to user's mailstream features form
* @param App $a
* @param array $post POST data
* @return void
*/
function mailstream_addon_settings_post(array $post)
function mailstream_addon_settings_post(App $a, array $post)
{
if (!DI::userSession()->getLocalUserId() || empty($post['mailstream-submit'])) {
return;

View file

@ -6,5 +6,6 @@
<div class="mailstream-item-body">{{$item.body nofilter}}</div>
{{if $item.plink}}
<div>{{$upstream}}: <a class="mailstream-item-plink" href="{{$item.plink}}">{{$item.plink}}</a><div>
<div>{{$uri}}: <a class="mailstream-item-uri" href="{{$item.uri}}">{{$item.uri}}</a><div>
<div>{{$local}}: <a class="mailstream-item-url" href="{{$item.url}}">{{$item.url}}</a></div>
{{/if}}

View file

@ -1,12 +1,7 @@
Installation
------------
[Register](http://www.tumblr.com/oauth/apps) an application and use (your server name)/addon/tumblr/callback.php as
callback URL
[Register](http://www.tumblr.com/oauth/apps) an application and use (your server name)/tumblr/callback as
callback URL and (your server name)/tumblr/redirect as OAuth2 redirect URL.
After the registration please enter the values for "Consumer Key" and "Consumer Secret" in the [administration](admin/addons/tumblr).
Notice
------
This connector is using the Tumblr-OAuth-Library:
[https://groups.google.com/d/msg/tumblr-api/g6SeIBWvsnE/gnWqT9jFSlEJ](https://groups.google.com/d/msg/tumblr-api/g6SeIBWvsnE/gnWqT9jFSlEJ)
After the registration please enter the values for "Consumer Key" and "Consumer Secret" in the [administration](admin/addons/tumblr).

View file

@ -1,211 +0,0 @@
<?php
/*
* Abraham Williams (abraham@abrah.am) http://abrah.am
*
* The first PHP Library to support OAuth for Tumblr's REST API. (Originally for Twitter, modified for Tumblr by Lucas)
*/
use Friendica\Core\Logger;
use Friendica\DI;
use Friendica\Security\OAuth1\OAuthConsumer;
use Friendica\Security\OAuth1\OAuthRequest;
use Friendica\Security\OAuth1\Signature\OAuthSignatureMethod_HMAC_SHA1;
use Friendica\Security\OAuth1\OAuthToken;
use Friendica\Security\OAuth1\OAuthUtil;
use GuzzleHttp\Client;
use GuzzleHttp\Exception\RequestException;
use GuzzleHttp\HandlerStack;
use GuzzleHttp\Subscriber\Oauth\Oauth1;
use Psr\Http\Message\ResponseInterface;
/**
* Tumblr OAuth class
*/
class TumblrOAuth
{
private $consumer_key;
private $consumer_secret;
private $oauth_token;
private $oauth_token_secret;
/** @var GuzzleHttp\Client */
private $client;
// API URLs
const accessTokenURL = 'https://www.tumblr.com/oauth/access_token';
const authorizeURL = 'https://www.tumblr.com/oauth/authorize';
const requestTokenURL = 'https://www.tumblr.com/oauth/request_token';
function __construct(string $consumer_key, string $consumer_secret, string $oauth_token = '', string $oauth_token_secret = '')
{
$this->consumer_key = $consumer_key;
$this->consumer_secret = $consumer_secret;
$this->oauth_token = $oauth_token;
$this->oauth_token_secret = $oauth_token_secret;
if (empty($this->oauth_token) || empty($this->oauth_token_secret)) {
return;
}
$stack = HandlerStack::create();
$middleware = new Oauth1([
'consumer_key' => $this->consumer_key,
'consumer_secret' => $this->consumer_secret,
'token' => $this->oauth_token,
'token_secret' => $this->oauth_token_secret
]);
$stack->push($middleware);
$this->client = new Client([
'base_uri' => 'https://api.tumblr.com/v2/',
'handler' => $stack
]);
}
/**
* Get a request_token from Tumblr
*
* @param string $oauth_callback
* @return array
*/
function getRequestToken(string $oauth_callback): array
{
$request = $this->oAuthRequest(self::requestTokenURL, ['oauth_callback' => $oauth_callback]);
if (empty($request)) {
return [];
}
return OAuthUtil::parse_parameters($request);
}
/**
* Get the authorize URL
*
* @param string $oauth_token
* @return string
*/
function getAuthorizeURL(string $oauth_token): string
{
return self::authorizeURL . "?oauth_token={$oauth_token}";
}
/**
* Exchange request token and secret for an access token and
* secret, to sign API calls.
*
* @param string $oauth_verifier
* @param string $request_token
* @param string $request_token_secret
* @return array ("oauth_token" => "the-access-token",
* "oauth_token_secret" => "the-access-secret",
* "user_id" => "9436992",
* "screen_name" => "abraham")
*/
function getAccessToken(string $oauth_verifier, string $request_token, string $request_token_secret): array
{
$token = new OAuthToken($request_token, $request_token_secret);
$parameters = [];
if (!empty($oauth_verifier)) {
$parameters['oauth_verifier'] = $oauth_verifier;
}
$request = $this->oAuthRequest(self::accessTokenURL, $parameters, $token);
if (empty($request)) {
return [];
}
return OAuthUtil::parse_parameters($request);
}
/**
* Format and sign an OAuth / API request
*
* @param string $url
* @param array $parameters
* @param OAuthToken $token $name
* @return string
*/
private function oAuthRequest(string $url, array $parameters, OAuthToken $token = null): string
{
$consumer = new OAuthConsumer($this->consumer_key, $this->consumer_secret);
$sha1_method = new OAuthSignatureMethod_HMAC_SHA1();
$request = OAuthRequest::from_consumer_and_token($consumer, 'GET', $url, $parameters, $token);
$request->sign_request($sha1_method, $consumer, $token);
$curlResult = DI::httpClient()->get($request->to_url());
if ($curlResult->isSuccess()) {
return $curlResult->getBody();
}
return '';
}
/**
* OAuth get from a given url with given parameters
*
* @param string $url
* @param array $parameters
* @return stdClass
*/
public function get(string $url, array $parameters = []): stdClass
{
if (!empty($parameters)) {
$url .= '?' . http_build_query($parameters);
}
try {
$response = $this->client->get($url, ['auth' => 'oauth']);
} catch (RequestException $exception) {
$response = $exception->getResponse();
Logger::notice('Get failed', ['code' => $exception->getCode(), 'message' => $exception->getMessage()]);
}
return $this->formatResponse($response);
}
/**
* OAuth Post to a given url with given parameters
*
* @param string $url
* @param array $parameter
* @return stdClass
*/
public function post(string $url, array $parameter): stdClass
{
try {
$response = $this->client->post($url, ['auth' => 'oauth', 'json' => $parameter]);
} catch (RequestException $exception) {
$response = $exception->getResponse();
Logger::notice('Post failed', ['code' => $exception->getCode(), 'message' => $exception->getMessage()]);
}
return $this->formatResponse($response);
}
/**
* Convert the body in the given response to a class
*
* @param ResponseInterface|null $response
* @return stdClass
*/
private function formatResponse(ResponseInterface $response = null): stdClass
{
if (!is_null($response)) {
$content = $response->getBody()->getContents();
if (!empty($content)) {
$result = json_decode($content);
}
}
if (empty($result) || empty($result->meta)) {
$result = new stdClass;
$result->meta = new stdClass;
$result->meta->status = 500;
$result->meta->msg = '';
$result->response = [];
$result->errors = [];
}
return $result;
}
}

View file

@ -7,13 +7,12 @@
* Author: Michael Vogel <https://pirati.ca/profile/heluecht>
*/
require_once __DIR__ . DIRECTORY_SEPARATOR . 'library' . DIRECTORY_SEPARATOR . 'tumblroauth.php';
use Friendica\Content\PageInfo;
use Friendica\Content\Text\BBCode;
use Friendica\Content\Text\HTML;
use Friendica\Content\Text\NPF;
use Friendica\Core\Cache\Enum\Duration;
use Friendica\Core\Config\Util\ConfigFileManager;
use Friendica\Core\Hook;
use Friendica\Core\Logger;
use Friendica\Core\Protocol;
@ -27,22 +26,207 @@ use Friendica\Model\ItemURI;
use Friendica\Model\Photo;
use Friendica\Model\Post;
use Friendica\Model\Tag;
use Friendica\Network\HTTPClient\Capability\ICanHandleHttpResponses;
use Friendica\Network\HTTPClient\Client\HttpClientAccept;
use Friendica\Network\HTTPClient\Client\HttpClientOptions;
use Friendica\Protocol\Activity;
use Friendica\Util\DateTimeFormat;
use Friendica\Util\Network;
use Friendica\Util\Strings;
use GuzzleHttp\Client;
use GuzzleHttp\Exception\RequestException;
use GuzzleHttp\HandlerStack;
use GuzzleHttp\Subscriber\Oauth\Oauth1;
define('TUMBLR_DEFAULT_POLL_INTERVAL', 10); // given in minutes
function tumblr_install()
{
Hook::register('load_config', __FILE__, 'tumblr_load_config');
Hook::register('hook_fork', __FILE__, 'tumblr_hook_fork');
Hook::register('post_local', __FILE__, 'tumblr_post_local');
Hook::register('notifier_normal', __FILE__, 'tumblr_send');
Hook::register('jot_networks', __FILE__, 'tumblr_jot_nets');
Hook::register('connector_settings', __FILE__, 'tumblr_settings');
Hook::register('connector_settings_post', __FILE__, 'tumblr_settings_post');
Hook::register('cron' , __FILE__, 'tumblr_cron');
Hook::register('cron', __FILE__, 'tumblr_cron');
Hook::register('support_follow', __FILE__, 'tumblr_support_follow');
Hook::register('support_probe', __FILE__, 'tumblr_support_probe');
Hook::register('follow', __FILE__, 'tumblr_follow');
Hook::register('unfollow', __FILE__, 'tumblr_unfollow');
Hook::register('block', __FILE__, 'tumblr_block');
Hook::register('unblock', __FILE__, 'tumblr_unblock');
Hook::register('check_item_notification', __FILE__, 'tumblr_check_item_notification');
Hook::register('probe_detect', __FILE__, 'tumblr_probe_detect');
Hook::register('item_by_link', __FILE__, 'tumblr_item_by_link');
Logger::info('installed tumblr');
}
function tumblr_load_config(ConfigFileManager $loader)
{
DI::app()->getConfigCache()->load($loader->loadAddonConfig('tumblr'), \Friendica\Core\Config\ValueObject\Cache::SOURCE_STATIC);
}
function tumblr_check_item_notification(array &$notification_data)
{
if (!tumblr_enabled_for_user($notification_data['uid'])) {
return;
}
$page = tumblr_get_page($notification_data['uid']);
if (empty($page)) {
return;
}
$own_user = Contact::selectFirst(['url', 'alias'], ['uid' => $notification_data['uid'], 'poll' => 'tumblr::'.$page]);
if ($own_user) {
$notification_data['profiles'][] = $own_user['url'];
$notification_data['profiles'][] = $own_user['alias'];
}
}
function tumblr_probe_detect(array &$hookData)
{
// Don't overwrite an existing result
if (isset($hookData['result'])) {
return;
}
// Avoid a lookup for the wrong network
if (!in_array($hookData['network'], ['', Protocol::TUMBLR])) {
return;
}
Logger::debug('Search for tumblr blog', ['url' => $hookData['uri']]);
$hookData['result'] = tumblr_get_contact_by_url($hookData['uri']);
}
function tumblr_item_by_link(array &$hookData)
{
// Don't overwrite an existing result
if (isset($hookData['item_id'])) {
return;
}
if (!tumblr_enabled_for_user($hookData['uid'])) {
return;
}
if (!preg_match('#^https?://www\.tumblr.com/blog/view/(.+)/(\d+).*#', $hookData['uri'], $matches) && !preg_match('#^https?://www\.tumblr.com/(.+)/(\d+).*#', $hookData['uri'], $matches)) {
return;
}
Logger::debug('Found tumblr post', ['url' => $hookData['uri'], 'blog' => $matches[1], 'id' => $matches[2]]);
$parameters = ['id' => $matches[2], 'reblog_info' => false, 'notes_info' => false, 'npf' => false];
$result = tumblr_get($hookData['uid'], 'blog/' . $matches[1] . '/posts', $parameters);
if ($result->meta->status > 399) {
Logger::notice('Error fetching status', ['meta' => $result->meta, 'response' => $result->response, 'errors' => $result->errors, 'blog' => $matches[1], 'id' => $matches[2]]);
return [];
}
Logger::debug('Got post', ['blog' => $matches[1], 'id' => $matches[2], 'result' => $result->response->posts]);
if (!empty($result->response->posts)) {
$hookData['item_id'] = tumblr_process_post($result->response->posts[0], $hookData['uid']);
}
}
function tumblr_support_follow(array &$data)
{
if ($data['protocol'] == Protocol::TUMBLR) {
$data['result'] = true;
}
}
function tumblr_support_probe(array &$data)
{
if ($data['protocol'] == Protocol::TUMBLR) {
$data['result'] = true;
}
}
function tumblr_follow(array &$hook_data)
{
$uid = DI::userSession()->getLocalUserId();
if (!tumblr_enabled_for_user($uid)) {
return;
}
Logger::debug('Check if contact is Tumblr', ['url' => $hook_data['url']]);
$fields = tumblr_get_contact_by_url($hook_data['url']);
if (empty($fields)) {
Logger::debug('Contact is not a Tumblr contact', ['url' => $hook_data['url']]);
return;
}
$result = tumblr_post($uid, 'user/follow', ['url' => $fields['url']]);
if ($result->meta->status <= 399) {
$hook_data['contact'] = $fields;
Logger::debug('Successfully start following', ['url' => $fields['url']]);
} else {
Logger::notice('Following failed', ['meta' => $result->meta, 'response' => $result->response, 'errors' => $result->errors, 'url' => $fields['url']]);
}
}
function tumblr_unfollow(array &$hook_data)
{
if (!tumblr_enabled_for_user($hook_data['uid'])) {
return;
}
if (!tumblr_get_contact_uuid($hook_data['contact'])) {
return;
}
$result = tumblr_post($hook_data['uid'], 'user/unfollow', ['url' => $hook_data['contact']['url']]);
$hook_data['result'] = ($result->meta->status <= 399);
}
function tumblr_block(array &$hook_data)
{
if (!tumblr_enabled_for_user($hook_data['uid'])) {
return;
}
$uuid = tumblr_get_contact_uuid($hook_data['contact']);
if (!$uuid) {
return;
}
$result = tumblr_post($hook_data['uid'], 'blog/' . tumblr_get_page($hook_data['uid']) . '/blocks', ['blocked_tumblelog' => $uuid]);
$hook_data['result'] = ($result->meta->status <= 399);
if ($hook_data['result']) {
$cdata = Contact::getPublicAndUserContactID($hook_data['contact']['id'], $hook_data['uid']);
if (!empty($cdata['user'])) {
Contact::remove($cdata['user']);
}
}
}
function tumblr_unblock(array &$hook_data)
{
if (!tumblr_enabled_for_user($hook_data['uid'])) {
return;
}
$uuid = tumblr_get_contact_uuid($hook_data['contact']);
if (!$uuid) {
return;
}
$result = tumblr_delete($hook_data['uid'], 'blog/' . tumblr_get_page($hook_data['uid']) . '/blocks', ['blocked_tumblelog' => $uuid]);
$hook_data['result'] = ($result->meta->status <= 399);
}
function tumblr_get_contact_uuid(array $contact): string
{
if (($contact['network'] != Protocol::TUMBLR) || (substr($contact['poll'], 0, 8) != 'tumblr::')) {
return '';
}
return substr($contact['poll'], 8);
}
/**
@ -58,28 +242,28 @@ function tumblr_content()
{
if (!DI::userSession()->getLocalUserId()) {
DI::sysmsg()->addNotice(DI::l10n()->t('Permission denied.'));
return '';
return;
}
if (!isset(DI::args()->getArgv()[1])) {
DI::baseUrl()->redirect('settings/connectors/tumblr');
}
switch (DI::args()->getArgv()[1]) {
switch (DI::args()->getArgv()[1] ?? '') {
case 'connect':
$o = tumblr_connect();
tumblr_connect();
break;
case 'callback':
$o = tumblr_callback();
break;
default:
DI::baseUrl()->redirect('settings/connectors/tumblr');
case 'redirect':
tumblr_redirect();
break;
}
DI::baseUrl()->redirect('settings/connectors/tumblr');
}
return $o;
function tumblr_redirect()
{
if (($_REQUEST['state'] ?? '') != DI::session()->get('oauth_state')) {
return;
}
tumblr_get_token(DI::userSession()->getLocalUserId(), $_REQUEST['code'] ?? '');
}
function tumblr_connect()
@ -89,81 +273,20 @@ function tumblr_connect()
$consumer_secret = DI::config()->get('tumblr', 'consumer_secret');
if (empty($consumer_key) || empty($consumer_secret)) {
DI::baseUrl()->redirect('settings/connectors/tumblr');
return;
}
// The callback URL is the script that gets called after the user authenticates with tumblr
// In this example, it would be the included callback.php
$callback_url = DI::baseUrl() . '/tumblr/callback';
$state = base64_encode(random_bytes(20));
DI::session()->set('oauth_state', $state);
// Let's begin. First we need a Request Token. The request token is required to send the user
// to Tumblr's login page.
$parameters = [
'client_id' => $consumer_key,
'response_type' => 'code',
'scope' => 'basic write offline_access',
'state' => $state
];
// Create a new instance of the TumblrOAuth library. For this step, all we need to give the library is our
// Consumer Key and Consumer Secret
$tum_oauth = new TumblrOAuth($consumer_key, $consumer_secret);
// Ask Tumblr for a Request Token. Specify the Callback URL here too (although this should be optional)
$request_token = $tum_oauth->getRequestToken($callback_url);
if (empty($request_token)) {
// Give an error message
return DI::l10n()->t('Could not connect to Tumblr. Refresh the page or try again later.');
}
// Store the request token and Request Token Secret as out callback.php script will need this
DI::session()->set('request_token', $request_token['oauth_token']);
DI::session()->set('request_token_secret', $request_token['oauth_token_secret']);
// Ask Tumblr to give us a special address to their login page
$url = $tum_oauth->getAuthorizeURL($request_token['oauth_token']);
// Redirect the user to the login URL given to us by Tumblr
System::externalRedirect($url);
/*
* That's it for our side. The user is sent to a Tumblr Login page and
* asked to authroize our app. After that, Tumblr sends the user back to
* our Callback URL (callback.php) along with some information we need to get
* an access token.
*/
}
function tumblr_callback()
{
// Define the needed keys
$consumer_key = DI::config()->get('tumblr', 'consumer_key');
$consumer_secret = DI::config()->get('tumblr', 'consumer_secret');
if (empty($_REQUEST['oauth_verifier']) || empty($consumer_key) || empty($consumer_secret)) {
DI::baseUrl()->redirect('settings/connectors/tumblr');
}
// Once the user approves your app at Tumblr, they are sent back to this script.
// This script is passed two parameters in the URL, oauth_token (our Request Token)
// and oauth_verifier (Key that we need to get Access Token).
// We'll also need out Request Token Secret, which we stored in a session.
// Create instance of TumblrOAuth.
// It'll need our Consumer Key and Secret as well as our Request Token and Secret
$tum_oauth = new TumblrOAuth($consumer_key, $consumer_secret);
// Ok, let's get an Access Token. We'll need to pass along our oauth_verifier which was given to us in the URL.
$access_token = $tum_oauth->getAccessToken($_REQUEST['oauth_verifier'], DI::session()->get('request_token'), DI::session()->get('request_token_secret'));
// We're done with the Request Token and Secret so let's remove those.
DI::session()->remove('request_token');
DI::session()->remove('request_token_secret');
if (empty($access_token)) {
return DI::l10n()->t('Unable to authenticate');
}
// What's next? Now that we have an Access Token and Secret, we can make an API call.
DI::pConfig()->set(DI::userSession()->getLocalUserId(), 'tumblr', 'oauth_token', $access_token['oauth_token']);
DI::pConfig()->set(DI::userSession()->getLocalUserId(), 'tumblr', 'oauth_token_secret', $access_token['oauth_token_secret']);
DI::baseUrl()->redirect('settings/connectors/tumblr');
System::externalRedirect('https://www.tumblr.com/oauth2/authorize?' . http_build_query($parameters));
}
function tumblr_addon_admin(string &$o)
@ -172,7 +295,6 @@ function tumblr_addon_admin(string &$o)
$o = Renderer::replaceMacros($t, [
'$submit' => DI::l10n()->t('Save Settings'),
// name, label, value, help, [extra values]
'$consumer_key' => ['consumer_key', DI::l10n()->t('Consumer Key'), DI::config()->get('tumblr', 'consumer_key'), ''],
'$consumer_secret' => ['consumer_secret', DI::l10n()->t('Consumer Secret'), DI::config()->get('tumblr', 'consumer_secret'), ''],
]);
@ -201,9 +323,6 @@ function tumblr_settings(array &$data)
if (!empty($blogs)) {
DI::cache()->set($cachekey, $blogs, Duration::HALF_HOUR);
}
} elseif (empty(tumblr_connection(DI::userSession()->getLocalUserId()))) {
$blogs = null;
DI::cache()->delete($cachekey);
}
if (!empty($blogs)) {
@ -331,7 +450,7 @@ function tumblr_hook_fork(array &$b)
return;
}
} elseif (!strstr($post['postopts'] ?? '', 'tumblr') || ($post['parent'] != $post['id']) || $post['private']) {
DI::logger()->info('Activities are never exported when we don\'t import the tumblr timeline');
DI::logger()->info('Activities are never exported when we don\'t import the tumblr timeline', ['uid' => $post['uid']]);
$b['execute'] = false;
return;
}
@ -387,11 +506,6 @@ function tumblr_send(array &$b)
Logger::debug('Parent found', ['parent' => $parent]);
$connection = tumblr_connection($b['uid']);
if (empty($connection)) {
return;
}
$page = tumblr_get_page($b['uid']);
if ($b['gravity'] == Item::GRAVITY_COMMENT) {
@ -399,20 +513,20 @@ function tumblr_send(array &$b)
} else {
if (($b['verb'] == Activity::LIKE) && !$b['deleted']) {
$params = ['id' => $parent['id'], 'reblog_key' => $parent['reblog_key']];
$result = $connection->post('user/like', $params);
$result = tumblr_post($b['uid'], 'user/like', $params);
} elseif (($b['verb'] == Activity::LIKE) && $b['deleted']) {
$params = ['id' => $parent['id'], 'reblog_key' => $parent['reblog_key']];
$result = $connection->post('user/unlike', $params);
$result = tumblr_post($b['uid'], 'user/unlike', $params);
} elseif (($b['verb'] == Activity::ANNOUNCE) && !$b['deleted']) {
$params = ['id' => $parent['id'], 'reblog_key' => $parent['reblog_key']];
$result = $connection->post('blog/' . $page . '/post/reblog', $params);
$result = tumblr_post($b['uid'], 'blog/' . $page . '/post/reblog', $params);
} elseif (($b['verb'] == Activity::ANNOUNCE) && $b['deleted']) {
$announce = tumblr_get_post_from_uri($b['extid']);
if (empty($announce)) {
return;
}
$params = ['id' => $announce['id']];
$result = $connection->post('blog/' . $page . '/post/delete', $params);
$result = tumblr_post($b['uid'], 'blog/' . $page . '/post/delete', $params);
} else {
// Unsupported activity
return;
@ -439,11 +553,6 @@ function tumblr_send(array &$b)
function tumblr_send_legacy(array $b)
{
$connection = tumblr_connection($b['uid']);
if (empty($connection)) {
return;
}
$b['body'] = BBCode::removeAttachment($b['body']);
$title = trim($b['title']);
@ -515,7 +624,7 @@ function tumblr_send_legacy(array $b)
$page = tumblr_get_page($b['uid']);
$result = $connection->post('blog/' . $page . '/post', $params);
$result = tumblr_post($b['uid'], 'blog/' . $page . '/post', $params);
if ($result->meta->status < 400) {
Logger::info('Success (legacy)', ['blog' => $page, 'meta' => $result->meta, 'response' => $result->response]);
@ -528,7 +637,6 @@ function tumblr_send_npf(array $post): bool
{
$page = tumblr_get_page($post['uid']);
$connection = tumblr_connection($post['uid']);
if (empty($page)) {
Logger::notice('Missing page, post will not be send to Tumblr.', ['uid' => $post['uid'], 'page' => $page, 'id' => $post['id']]);
// "true" is returned, since the legacy function will fail as well.
@ -549,7 +657,7 @@ function tumblr_send_npf(array $post): bool
'interactability_reblog' => 'everyone'
];
$result = $connection->post('blog/' . $page . '/posts', $params);
$result = tumblr_post($post['uid'], 'blog/' . $page . '/posts', $params);
if ($result->meta->status < 400) {
Logger::info('Success (NPF)', ['blog' => $page, 'meta' => $result->meta, 'response' => $result->response]);
@ -566,8 +674,8 @@ function tumblr_get_post_from_uri(string $uri): array
if (($parts[0] != 'tumblr') || empty($parts[2])) {
return [];
}
$post ['id'] = $parts[2];
$post['id'] = $parts[2];
$post['reblog_key'] = $parts[3] ?? '';
$post['reblog_key'] = str_replace('@t', '', $post['reblog_key']); // Temp
@ -591,8 +699,7 @@ function tumblr_fetch_dashboard(int $uid)
$parameters['since_id'] = $last;
}
$connection = tumblr_connection($uid);
$dashboard = $connection->get('user/dashboard', $parameters);
$dashboard = tumblr_get($uid, 'user/dashboard', $parameters);
if ($dashboard->meta->status > 399) {
Logger::notice('Error fetching dashboard', ['meta' => $dashboard->meta, 'response' => $dashboard->response, 'errors' => $dashboard->errors]);
return [];
@ -616,26 +723,37 @@ function tumblr_fetch_dashboard(int $uid)
continue;
}
$item = tumblr_get_header($post, $uri, $uid);
tumblr_process_post($post, $uid, $uri);
$item = tumblr_get_content($item, $post);
$id = item::insert($item);
if ($id) {
$stored = Post::selectFirst(['uri-id'], ['id' => $id]);
if (!empty($post->tags)) {
foreach ($post->tags as $tag) {
Tag::store($stored['uri-id'], Tag::HASHTAG, $tag);
}
}
}
DI::pConfig()->set($uid, 'tumblr', 'last_id', $last);
}
}
function tumblr_process_post(stdClass $post, int $uid, string $uri = ''): int
{
if (empty($uri)) {
$uri = 'tumblr::' . $post->id_string . ':' . $post->reblog_key;
}
$item = tumblr_get_header($post, $uri, $uid);
$item = tumblr_get_content($item, $post);
$id = item::insert($item);
if ($id) {
$stored = Post::selectFirst(['uri-id'], ['id' => $id]);
if (!empty($post->tags)) {
foreach ($post->tags as $tag) {
Tag::store($stored['uri-id'], Tag::HASHTAG, $tag);
}
}
}
return $id;
}
/**
* Sets the initial data for the item array
*
@ -719,23 +837,23 @@ function tumblr_get_content(array $item, stdClass $post): array
$item['body'] = HTML::toBBCode($post->caption);
if (!empty($post->video_url)) {
$item['body'] .= "\n[video]" . $post->video_url . "[/video]\n";
} elseif(!empty($post->thumbnail_url)) {
$item['body'] .= "\n[url=" . $post->permalink_url ."][img]" . $post->thumbnail_url . "[/img][/url]\n";
} elseif(!empty($post->permalink_url)) {
$item['body'] .= "\n[url]" . $post->permalink_url ."[/url]\n";
} elseif(!empty($post->source_url) && !empty($post->source_title)) {
$item['body'] .= "\n[url=" . $post->source_url ."]" . $post->source_title . "[/url]\n";
} elseif(!empty($post->source_url)) {
$item['body'] .= "\n[url]" . $post->source_url ."[/url]\n";
} elseif (!empty($post->thumbnail_url)) {
$item['body'] .= "\n[url=" . $post->permalink_url . "][img]" . $post->thumbnail_url . "[/img][/url]\n";
} elseif (!empty($post->permalink_url)) {
$item['body'] .= "\n[url]" . $post->permalink_url . "[/url]\n";
} elseif (!empty($post->source_url) && !empty($post->source_title)) {
$item['body'] .= "\n[url=" . $post->source_url . "]" . $post->source_title . "[/url]\n";
} elseif (!empty($post->source_url)) {
$item['body'] .= "\n[url]" . $post->source_url . "[/url]\n";
}
break;
case 'audio':
$item['body'] = HTML::toBBCode($post->caption);
if(!empty($post->source_url) && !empty($post->source_title)) {
$item['body'] .= "\n[url=" . $post->source_url ."]" . $post->source_title . "[/url]\n";
} elseif(!empty($post->source_url)) {
$item['body'] .= "\n[url]" . $post->source_url ."[/url]\n";
if (!empty($post->source_url) && !empty($post->source_title)) {
$item['body'] .= "\n[url=" . $post->source_url . "]" . $post->source_title . "[/url]\n";
} elseif (!empty($post->source_url)) {
$item['body'] .= "\n[url]" . $post->source_url . "[/url]\n";
}
break;
@ -832,6 +950,10 @@ function tumblr_get_npf_data(DOMNode $node): array
function tumblr_get_attributes($node): array
{
if (empty($node->attributes)) {
return [];
}
$attributes = [];
foreach ($node->attributes as $key => $attribute) {
$attributes[$key] = trim($attribute->value);
@ -843,7 +965,7 @@ function tumblr_get_type_replacement(array $data, string $plink): string
{
switch ($data['type']) {
case 'poll':
$body = '[p][url=' . $plink. ']'. $data['question'] . '[/url][/p][ul]';
$body = '[p][url=' . $plink . ']' . $data['question'] . '[/url][/p][ul]';
foreach ($data['answers'] as $answer) {
$body .= '[li]' . $answer['answer_text'] . '[/li]';
}
@ -859,7 +981,7 @@ function tumblr_get_type_replacement(array $data, string $plink): string
$body = '[video]' . $data['url'] . '[/video]';
break;
}
default:
Logger::notice('Unknown type', ['type' => $data['type'], 'data' => $data, 'plink' => $plink]);
$body = '';
@ -930,7 +1052,7 @@ function tumblr_insert_contact(stdClass $blog, int $uid)
'name' => $blog->title,
'nick' => $blog->name,
'addr' => $blog->name . '@tumblr.com',
'about' => $blog->description,
'about' => HTML::toBBCode($blog->description),
'updated' => date(DateTimeFormat::MYSQL, $blog->updated)
];
return Contact::insert($fields);
@ -947,8 +1069,7 @@ function tumblr_insert_contact(stdClass $blog, int $uid)
*/
function tumblr_update_contact(stdClass $blog, int $uid, int $cid, int $pcid)
{
$connection = tumblr_connection($uid);
$info = $connection->get('blog/' . $blog->uuid . '/info');
$info = tumblr_get($uid, 'blog/' . $blog->uuid . '/info');
if ($info->meta->status > 399) {
Logger::notice('Error fetching dashboard', ['meta' => $info->meta, 'response' => $info->response, 'errors' => $info->errors]);
return;
@ -981,7 +1102,7 @@ function tumblr_update_contact(stdClass $blog, int $uid, int $cid, int $pcid)
'name' => $info->response->blog->title,
'nick' => $info->response->blog->name,
'addr' => $info->response->blog->name . '@tumblr.com',
'about' => BBCode::convertForUriId($uri_id, $info->response->blog->description, BBCode::CONNECTORS),
'about' => HTML::toBBCode($info->response->blog->description),
'updated' => date(DateTimeFormat::MYSQL, $info->response->blog->updated),
'header' => $info->response->blog->theme->header_image_focused,
'rel' => $rel,
@ -1029,12 +1150,7 @@ function tumblr_get_page(int $uid, array $blogs = []): string
*/
function tumblr_get_blogs(int $uid): array
{
$connection = tumblr_connection($uid);
if (empty($connection)) {
return [];
}
$userinfo = $connection->get('user/info');
$userinfo = tumblr_get($uid, 'user/info');
if ($userinfo->meta->status > 299) {
Logger::notice('Error fetching blogs', ['meta' => $userinfo->meta, 'response' => $userinfo->response, 'errors' => $userinfo->errors]);
return [];
@ -1047,13 +1163,231 @@ function tumblr_get_blogs(int $uid): array
return $blogs;
}
function tumblr_enabled_for_user(int $uid)
{
return !empty($uid) && !empty(DI::pConfig()->get($uid, 'tumblr', 'access_token')) &&
!empty(DI::pConfig()->get($uid, 'tumblr', 'refresh_token')) &&
!empty(DI::config()->get('tumblr', 'consumer_key')) &&
!empty(DI::config()->get('tumblr', 'consumer_secret'));
}
/**
* Creates a OAuth connection for the given user
* Get a contact array from a Tumblr url
*
* @param string $url
* @return array
*/
function tumblr_get_contact_by_url(string $url): array
{
$consumer_key = DI::config()->get('tumblr', 'consumer_key');
if (empty($consumer_key)) {
return [];
}
if (!preg_match('#^https?://tumblr.com/(.+)#', $url, $matches) && !preg_match('#^https?://www\.tumblr.com/(.+)#', $url, $matches) && !preg_match('#^https?://(.+)\.tumblr.com#', $url, $matches)) {
try {
$curlResult = DI::httpClient()->get($url);
} catch (\Exception $e) {
return [];
}
$html = $curlResult->getBody();
if (empty($html)) {
return [];
}
$doc = new DOMDocument();
@$doc->loadHTML($html);
$xpath = new DomXPath($doc);
$body = $xpath->query('body');
$attributes = tumblr_get_attributes($body->item(0));
$blog = $attributes['data-urlencoded-name'] ?? '';
} else {
$blogs = explode('/', $matches[1]);
$blog = $blogs[0] ?? '';
}
if (empty($blog)) {
return [];
}
$curlResult = DI::httpClient()->get('https://api.tumblr.com/v2/blog/' . $blog . '/info?api_key=' . $consumer_key);
$body = $curlResult->getBody();
$data = json_decode($body);
if (empty($data)) {
return [];
}
$baseurl = 'https://tumblr.com';
$url = $baseurl . '/' . $data->response->blog->name;
return [
'url' => $url,
'nurl' => Strings::normaliseLink($url),
'addr' => $data->response->blog->name . '@tumblr.com',
'alias' => $data->response->blog->url,
'batch' => '',
'notify' => '',
'poll' => 'tumblr::' . $data->response->blog->uuid,
'poco' => '',
'name' => $data->response->blog->title,
'nick' => $data->response->blog->name,
'network' => Protocol::TUMBLR,
'baseurl' => $baseurl,
'pubkey' => '',
'priority' => 0,
'guid' => $data->response->blog->uuid,
'about' => HTML::toBBCode($data->response->blog->description),
'photo' => $data->response->blog->avatar[0]->url,
'header' => $data->response->blog->theme->header_image_focused,
];
}
/**
* Perform an OAuth2 GET request
*
* @param integer $uid
* @return TumblrOAuth|null
* @param string $url
* @param array $parameters
* @return stdClass
*/
function tumblr_connection(int $uid): ?TumblrOAuth
function tumblr_get(int $uid, string $url, array $parameters = []): stdClass
{
$url = 'https://api.tumblr.com/v2/' . $url;
if (!empty($parameters)) {
$url .= '?' . http_build_query($parameters);
}
$curlResult = DI::httpClient()->get($url, HttpClientAccept::JSON, [HttpClientOptions::HEADERS => ['Authorization' => ['Bearer ' . tumblr_get_token($uid)]]]);
return tumblr_format_result($curlResult);
}
/**
* Perform an OAuth2 POST request
*
* @param integer $uid
* @param string $url
* @param array $parameters
* @return stdClass
*/
function tumblr_post(int $uid, string $url, array $parameters): stdClass
{
$url = 'https://api.tumblr.com/v2/' . $url;
$curlResult = DI::httpClient()->post($url, $parameters, ['Authorization' => ['Bearer ' . tumblr_get_token($uid)]]);
return tumblr_format_result($curlResult);
}
/**
* Perform an OAuth2 DELETE request
*
* @param integer $uid
* @param string $url
* @param array $parameters
* @return stdClass
*/
function tumblr_delete(int $uid, string $url, array $parameters): stdClass
{
$url = 'https://api.tumblr.com/v2/' . $url;
$opts = [
HttpClientOptions::HEADERS => ['Authorization' => ['Bearer ' . tumblr_get_token($uid)]],
HttpClientOptions::FORM_PARAMS => $parameters
];
$curlResult = DI::httpClient()->request('delete', $url, $opts);
return tumblr_format_result($curlResult);
}
/**
* Format the get/post result value
*
* @param ICanHandleHttpResponses $curlResult
* @return stdClass
*/
function tumblr_format_result(ICanHandleHttpResponses $curlResult): stdClass
{
$result = json_decode($curlResult->getBody());
if (empty($result) || empty($result->meta)) {
$result = new stdClass;
$result->meta = new stdClass;
$result->meta->status = 500;
$result->meta->msg = '';
$result->response = [];
$result->errors = [];
}
return $result;
}
/**
* Fetch the OAuth token, update it if needed
*
* @param integer $uid
* @param string $code
* @return string
*/
function tumblr_get_token(int $uid, string $code = ''): string
{
$access_token = DI::pConfig()->get($uid, 'tumblr', 'access_token');
$expires_at = DI::pConfig()->get($uid, 'tumblr', 'expires_at');
$refresh_token = DI::pConfig()->get($uid, 'tumblr', 'refresh_token');
if (empty($code) && !empty($access_token) && ($expires_at > (time()))) {
Logger::debug('Got token', ['uid' => $uid, 'expires_at' => date('c', $expires_at)]);
return $access_token;
}
$consumer_key = DI::config()->get('tumblr', 'consumer_key');
$consumer_secret = DI::config()->get('tumblr', 'consumer_secret');
$parameters = ['client_id' => $consumer_key, 'client_secret' => $consumer_secret];
if (empty($refresh_token) && empty($code)) {
$result = tumblr_exchange_token($uid);
if (empty($result->refresh_token)) {
Logger::info('Invalid result while exchanging token', ['uid' => $uid]);
return '';
}
$expires_at = time() + $result->expires_in;
Logger::debug('Updated token from OAuth1 to OAuth2', ['uid' => $uid, 'expires_at' => date('c', $expires_at)]);
} else {
if (!empty($code)) {
$parameters['code'] = $code;
$parameters['grant_type'] = 'authorization_code';
} else {
$parameters['refresh_token'] = $refresh_token;
$parameters['grant_type'] = 'refresh_token';
}
$curlResult = DI::httpClient()->post('https://api.tumblr.com/v2/oauth2/token', $parameters);
if (!$curlResult->isSuccess()) {
Logger::info('Error fetching token', ['uid' => $uid, 'code' => $code, 'result' => $curlResult->getBody(), 'parameters' => $parameters]);
return '';
}
$result = json_decode($curlResult->getBody());
if (empty($result)) {
Logger::info('Invalid result when updating token', ['uid' => $uid]);
return '';
}
$expires_at = time() + $result->expires_in;
Logger::debug('Renewed token', ['uid' => $uid, 'expires_at' => date('c', $expires_at)]);
}
DI::pConfig()->set($uid, 'tumblr', 'access_token', $result->access_token);
DI::pConfig()->set($uid, 'tumblr', 'expires_at', $expires_at);
DI::pConfig()->set($uid, 'tumblr', 'refresh_token', $result->refresh_token);
return $result->access_token;
}
/**
* Create an OAuth2 token out of an OAuth1 token
*
* @param int $uid
* @return stdClass
*/
function tumblr_exchange_token(int $uid): stdClass
{
$oauth_token = DI::pConfig()->get($uid, 'tumblr', 'oauth_token');
$oauth_token_secret = DI::pConfig()->get($uid, 'tumblr', 'oauth_token_secret');
@ -1061,10 +1395,27 @@ function tumblr_connection(int $uid): ?TumblrOAuth
$consumer_key = DI::config()->get('tumblr', 'consumer_key');
$consumer_secret = DI::config()->get('tumblr', 'consumer_secret');
if (!$consumer_key || !$consumer_secret || !$oauth_token || !$oauth_token_secret) {
Logger::notice('Missing data, connection is not established', ['uid' => $uid]);
return null;
}
$stack = HandlerStack::create();
return new TumblrOAuth($consumer_key, $consumer_secret, $oauth_token, $oauth_token_secret);
}
$middleware = new Oauth1([
'consumer_key' => $consumer_key,
'consumer_secret' => $consumer_secret,
'token' => $oauth_token,
'token_secret' => $oauth_token_secret
]);
$stack->push($middleware);
try {
$client = new Client([
'base_uri' => 'https://api.tumblr.com/v2/',
'handler' => $stack
]);
$response = $client->post('oauth2/exchange', ['auth' => 'oauth']);
return json_decode($response->getBody()->getContents());
} catch (RequestException $exception) {
Logger::notice('Exchange failed', ['code' => $exception->getCode(), 'message' => $exception->getMessage()]);
return new stdClass;
}
}