[activitypub] v0.0.2: make posts loadable by url, with text and attached imgs

This commit is contained in:
octospacc 2023-09-13 02:02:54 +02:00
parent 2067f380b5
commit 5bd77be244
7 changed files with 244 additions and 64 deletions

View File

@ -14,14 +14,16 @@
<fieldset>
<legend>{{ lang('SETTINGS') }}</legend>
<dl>
<dt><label for="spaccincphpbb_activitypub_setfederation">{{ lang('ACP_SPACCINC_ACTIVITYPUB_SETFEDERATION') ~ lang('COLON') }}</label></dt>
<dd><label><input type="radio" class="radio" name="spaccincphpbb_activitypub_setfederation" value="1"{% if SPACCINCPHPBB_ACTIVITYPUB_SETFEDERATION %} checked="checked"{% endif %} /> {{ lang('YES') }}</label>
<label><input type="radio" class="radio" name="spaccincphpbb_activitypub_setfederation" value="0"{% if not SPACCINCPHPBB_ACTIVITYPUB_SETFEDERATION %} checked="checked"{% endif %} /> {{ lang('NO') }}</label></dd>
<dt><label for="spaccinc_activitypub_setfederation">{{ lang('ACP_SPACCINC_ACTIVITYPUB_SETFEDERATION') ~ lang('COLON') }}</label></dt>
<dd><label><input type="radio" class="radio" name="spaccinc_activitypub_setfederation" value="1" {% if SPACCINC_ACTIVITYPUB_SETFEDERATION %} checked="checked" {% endif %} /> {{ lang('YES') }}</label>
<label><input type="radio" class="radio" name="spaccinc_activitypub_setfederation" value="0" {% if not SPACCINC_ACTIVITYPUB_SETFEDERATION %} checked="checked" {% endif %} /> {{ lang('NO') }}</label></dd>
</dl>
<!--<dl>
<dt><label for="spaccincphpbb_activitypub_setdomain">{{ lang('ACP_SPACCINC_ACTIVITYPUB_SETDOMAIN') ~ lang('COLON') }}</label><br /><span>{{ lang('ACP_SPACCINC_ACTIVITYPUB_SETDOMAIN_INFO') }}</span></dt>
<dd><input name="spaccincphpbb_activitypub_setdomain" type="text" placeholder="TODO" value="TODO" disabled="TODO" /></dd>
</dl>-->
<!--
<dl>
<dt><label for="spaccinc_activitypub_setdomain">{{ lang('ACP_SPACCINC_ACTIVITYPUB_SETDOMAIN') ~ lang('COLON') }}</label><br /><span>{{ lang('ACP_SPACCINC_ACTIVITYPUB_SETDOMAIN_INFO') }}</span></dt>
<dd><input name="spaccinc_activitypub_setdomain" type="text" placeholder="{{ SYS_SERVER_NAME }}" value="{{ SPACCINC_ACTIVITYPUB_SETDOMAIN }}" {% if SPACCINC_ACTIVITYPUB_SETDOMAIN != '' %} disabled="disabled" {% endif %} /></dd>
</dl>
-->
</fieldset>
<fieldset class="submit-buttons">

View File

@ -3,8 +3,8 @@
"type": "phpbb-extension",
"description": "ActivityPub for phpBB",
"homepage": "https://gitlab.com/SpaccInc/SpaccCommunityPlatform",
"version": "0.0.2-dev",
"time": "2023-09-09",
"version": "0.0.2",
"time": "2023-09-13",
"license": "[to be defined]",
"authors": [
{

View File

@ -3,8 +3,8 @@ services:
class: spaccincphpbb\activitypub\controller\activitypub_controller
arguments:
- '@config'
- '@request'
- '@dbal.conn'
- '@request'
spaccincphpbb.activitypub.controller.acp:
class: spaccincphpbb\activitypub\controller\acp_controller

View File

@ -43,7 +43,8 @@ class acp_controller
if (empty($errors))
{
$this->config->set('spaccincphpbb_activitypub_setfederation', $this->request->variable('spaccincphpbb_activitypub_setfederation', 0));
$this->config->set('spaccinc_activitypub_setfederation', $this->request->variable('spaccinc_activitypub_setfederation', 0));
//$this->config->set('spaccinc_activitypub_setfederation', $this->request->variable('spaccinc_activitypub_setdomain', ''));
$this->log->add('admin', $this->user->data['user_id'], $this->user->ip, 'LOG_ACP_SPACCINC_ACTIVITYPUB_SETTINGS');
trigger_error($this->language->lang('CONFIG_UPDATED') . adm_back_link($this->u_action));
}
@ -53,9 +54,11 @@ class acp_controller
$this->template->assign_vars([
'S_ERROR' => $s_errors,
'ERROR_MSG' => $s_errors ? implode('<br />', $errors) : '',
'ERROR_MSG' => ($s_errors ? implode('<br />', $errors) : ''),
'U_ACTION' => $this->u_action,
'SPACCINCPHPBB_ACTIVITYPUB_SETFEDERATION' => (bool)$this->config['spaccincphpbb_activitypub_setfederation'],
'SYS_SERVER_NAME' => $this->config['server_name'],
'SPACCINC_ACTIVITYPUB_SETFEDERATION' => (bool)$this->config['spaccinc_activitypub_setfederation'],
'SPACCINC_ACTIVITYPUB_SETDOMAIN' => $this->config['spaccinc_activitypub_setdomain'],
]);
}

View File

@ -3,48 +3,45 @@
namespace spaccincphpbb\activitypub\controller;
use ErrorException;
use DOMDocument;
use DOMXpath;
use Symfony\Component\HttpFoundation\Response;
class activitypub_controller
{
protected $config;
protected $request;
protected $db;
protected $request;
// Quick way to force any PHP warning to be an error that halts execution and makes nothing return,
// so that we don't need complex error handling in case of bad queries
public function exception_error_handler($severity, $message, $file, $line)
{
if ($message !== 'Only variables should be passed by reference')
{
if ($message !== 'Return type of phpbb\datetime::format($format = \'\', $force_absolute = false) should either be compatible with DateTime::format(string $format): string, or the #[\ReturnTypeWillChange] attribute should be used to temporarily suppress the notice' && $message !== 'Only variables should be passed by reference' && !(str_contains($message, ' service is private, getting it from the container is deprecated ') || str_contains($message, 'You should either make the service public, or stop using the container directly and use dependency injection instead.'))
){
print $message;
throw new ErrorException($message, 0, $severity, $file, $line);
}
}
public function __construct(
\phpbb\config\config $config,
\phpbb\request\request $request,
\phpbb\db\driver\driver_interface $db,
\phpbb\request\request $request,
){
$this->config = $config;
$this->request = $request;
$this->db = $db;
$this->request = $request;
$this->site_name = $this->get_sql_row('
SELECT config_value
FROM ' . CONFIG_TABLE . "
WHERE config_name = 'sitename'
")['config_value'];
$this->server_name = $this->get_sql_row('
SELECT config_value
FROM ' . CONFIG_TABLE . "
WHERE config_name = 'server_name'
")['config_value'];
$this->server_name = $this->config['server_name'];
$this->server_addr = ((!empty($this->request->server('HTTPS')) && (strtolower($this->request->server('HTTPS')) == 'on' || $this->request->server('HTTPS') == '1')) ? 'https://' : 'http://') . $this->server_name;
}
private function iso_time($time)
{
return gmdate("Y-m-d\TH:i:s\Z", $time);
}
private function get_sql_row($sql)
{
$result = $this->db->sql_query($sql);
@ -53,9 +50,122 @@ class activitypub_controller
return $data;
}
private function get_sql_rows($sql)
{
$result = $this->db->sql_query($sql);
$data = $this->db->sql_fetchrowset($result);
$this->db->sql_freeresult($result);
return $data;
}
private function get_bbcode_flags($data)
{
return
($data['enable_bbcode'] ? OPTION_FLAG_BBCODE : 0) +
($data['enable_smilies'] ? OPTION_FLAG_SMILIES : 0) +
($data['enable_magic_url'] ? OPTION_FLAG_LINKS : 0);
}
private function make_post_attachments($html)
{
$attachments = [];
$dom = new DOMDocument;
$dom->loadHTML($html);
$xpath = new DOMXpath($dom);
$imgs = $dom->getElementsByTagName('img');
$atts = $xpath->query('//div[@class="inline-attachment"]');
// TODO: currently this picks up emojis, and must so be fixed
//foreach($imgs as $item){
// $attachments[] = [
// 'type' => 'Document',
// 'mediaType' => 'image/*',
// 'url' => $item->getAttribute('src'),
// //'name' => null,
// ];
//}
unset($item);
foreach($atts as $item){
$file_name = explode('<!--', explode('-->', $item->ownerDocument->saveHtml($item))[1])[0];
$file_id = $this->get_sql_row('
SELECT attach_id
FROM ' . ATTACHMENTS_TABLE . "
WHERE real_filename='" . $this->db->sql_escape($file_name) . "'
")['attach_id'];
$attachments[] = [
'type' => 'Document',
'mediaType' => 'image/jpeg',
'url' => $this->server_addr . '/download/file.php?id=' . $file_id,
'name' => null,
];
}
unset($item);
return $attachments;
}
private function make_post_object($data, $in_create)
{
$uri_id = $this->server_addr . '/activitypub?&mode=post&post_id=' . $data['post_id'];
$uri_user = $this->server_addr . '/activitypub?&mode=user&user_id=' . $data['poster_id'];
$post_html = generate_text_for_display($data['post_text'], $data['bbcode_uid'], $data['bbcode_bitfield'], $this->get_bbcode_flags($data));
$post_time = $this->iso_time($data['post_time']);
// Note: #Public in to and followers in cc for public post, opposite for unlisted!
$post_to = ['https://www.w3.org/ns/activitystreams#Public'];
$post_cc = [];
$note = [
'id' => $uri_id,
'type' => 'Note',
'published' => $post_time,
//'updated' => ,
'attributedTo' => $uri_user,
//'inReplyTo' => null,
'to' => $post_to,
'cc' => $post_cc,
'url' => $this->server_addr . '/viewtopic.php?p=' . $data['post_id'] . '#p' . $data['post_id'],
//'mediaType' => 'text/html',
//'summary' => null,
'content' => $post_html,
//'contentMap' => [ 'it' => '' ],
'attachment' => $this->make_post_attachments($post_html),
];
if ($in_create)
{
return [
'@context' => [
'https://www.w3.org/ns/activitystreams',
],
'id' => $uri_id,
'type' => 'Create',
'actor' => $uri_user,
'published' => $post_time,
//'updated' => ,
'to' => $post_to,
'cc' => $post_cc,
'object' => $note,
];
}
else
{
$response[] = $note;
return array_merge([
'@context' => [
'https://www.w3.org/ns/activitystreams',
]
], $note);
}
}
public function nodeinfo_known()
{
if (!$this->config['spaccincphpbb_activitypub_setfederation']) {
if (!$this->config['spaccinc_activitypub_setfederation']) {
return;
}
@ -74,7 +184,7 @@ class activitypub_controller
public function webfinger()
{
if (!$this->config['spaccincphpbb_activitypub_setfederation']) {
if (!$this->config['spaccinc_activitypub_setfederation']) {
return;
}
@ -86,11 +196,6 @@ class activitypub_controller
$subject = '';
$href = [];
//if ($resource == '')
//{
// return new JsonResponse([], 500);
//}
if (str_starts_with($resource, 'acct:'))
{
$tokens = explode('@', substr($resource, strlen('acct:')));
@ -107,8 +212,8 @@ class activitypub_controller
WHERE username_clean = '" . $this->db->sql_escape($name) . "'
");
$href['self'] = '&mode=user&u=' . $data['user_id'];
$href['profile-page'] = '/memberlist.php?mode=viewprofile&u=' . $data['user_id'];
$href['self'] = '&mode=user&user_id=' . $data['user_id'];
$href['profile-page'] = '/memberlist.php?mode=viewprofile&user_id=' . $data['user_id'];
}
$response = new Response(json_encode([
@ -130,7 +235,7 @@ class activitypub_controller
public function activitypub()
{
if (!$this->config['spaccincphpbb_activitypub_setfederation']) {
if (!$this->config['spaccinc_activitypub_setfederation']) {
return;
}
@ -139,7 +244,7 @@ class activitypub_controller
$uri_id = htmlspecialchars_decode($server_addr . $this->request->server('REQUEST_URI'));
$mode = $this->request->variable('mode', '');
$response = [];
$response = null;
switch ($mode)
{
@ -173,7 +278,7 @@ class activitypub_controller
//],
//'openRegistrations' => true,
'metadata' => [
'nodeName' => $this->site_name,
'nodeName' => $this->config['sitename'],
],
];
break;
@ -181,12 +286,12 @@ class activitypub_controller
break;
case 'user':
$u = $this->request->variable('u', '');
$user_id = $this->request->variable('user_id', '');
$data = $this->get_sql_row('
SELECT *
FROM ' . USERS_TABLE . "
WHERE user_id = '" . $this->db->sql_escape($u) . "'
WHERE user_id = '" . $this->db->sql_escape($user_id) . "'
");
$icon_ext = end(explode('.', $data['user_avatar']));
@ -208,18 +313,18 @@ class activitypub_controller
'https://w3id.org/security/v1',
],
'id' => $uri_id,
'inbox' => $server_addr . '/activitypub?&mode=inbox&u=' . $u,
'outbox' => $server_addr . '/activitypub?&mode=outbox&u=' . $u,
'inbox' => $server_addr . '/activitypub?&mode=inbox&user_id=' . $user_id,
'outbox' => $server_addr . '/activitypub?&mode=outbox&user_id=' . $user_id,
'endpoints' => [
'sharedInbox' => $server_addr . '/activitypub&mode=inbox',
],
'type' => 'Person',
//'discoverable' => true,
//'manuallyApprovesFollowers' => false,
//'published' => $data['user_regdate'],
'published' => $this->iso_time($data['user_regdate']),
'preferredUsername' => $data['username_clean'],
'name' => $data['username'],
'url' => $server_addr . '/memberlist.php?mode=viewprofile&u=' . $u,
'url' => $server_addr . '/memberlist.php?mode=viewprofile&user_id=' . $user_id,
'icon' => [
'type' => 'Image',
'mediaType' => 'image/' . ($icon_ext === 'jpg' ? 'jpeg' : $icon_ext),
@ -238,27 +343,95 @@ class activitypub_controller
break;
case 'outbox':
$u = $this->request->variable('u', '');
$user_id = $this->request->variable('user_id', '');
$page = $this->request->variable('page', '');
//$min_id = $this->request->variable('min_id', -1);
//$max_id = $this->request->variable('max_id', -1);
$user_posts = $this->get_sql_row('
$post_count = (int)$this->get_sql_row('
SELECT user_posts
FROM ' . USERS_TABLE . "
WHERE user_id = '" . $this->db->sql_escape($u) . "'
WHERE user_id = '" . $this->db->sql_escape($user_id) . "'
")['user_posts'];
$response = [
'@context' => 'https://www.w3.org/ns/activitystreams',
'id' => $uri_id,
'type' => 'OrderedCollection',
'totalItems' => (int)$user_posts,
'first' => $uri_id . '&view=page',
'last' => $uri_id . '&view=page&min_id=0',
];
if ($page === '')
{
$response = [
'@context' => [
'https://www.w3.org/ns/activitystreams',
],
'id' => $uri_id,
'type' => 'OrderedCollection',
'totalItems' => $post_count,
'first' => $uri_id . '&page=-1',
'last' => $uri_id . '&page=0',
];
}
else
{
$items = [];
$order = 'DESC';
$limit = 20;
$offset = 0;
switch ($page)
{
// Oldest
case '0':
$order = 'DESC';
break;
// Newest
case '-1':
// ...
break;
// Any other page
default:
// ...
break;
}
$data = $this->get_sql_rows('
SELECT *
FROM ' . POSTS_TABLE . "
WHERE poster_id = '" . $this->db->sql_escape($user_id) . "'
ORDER BY post_id " . $order . '
LIMIT ' . $limit . '
OFFSET ' . $offset
);
foreach ($data as &$item)
{
$items[] = $this->make_post_object($item, true);
}
unset($item);
$response = [
'@context' => [
'https://www.w3.org/ns/activitystreams',
],
'id' => $uri_id,
'type' => 'OrderedCollectionPage',
//'prev' => '',
//'next' => '',
// ...
'partOf' => $server_addr . '/activitypub?&mode=outbox&user_id=' . $user_id,
//'ordererdItems' => $items,
];
}
break;
//case 'post':
//case 'thread':
//
//break;
case 'post':
$response = $this->make_post_object($this->get_sql_row('
SELECT *
FROM ' . POSTS_TABLE . "
WHERE post_id = '" . $this->db->sql_escape($this->request->variable('post_id', '')) . "'
"), false);
break;
}
$response = new Response(json_encode($response), 200);

View File

@ -14,5 +14,6 @@ $lang = array_merge($lang, [
'ACP_SPACCINC_ACTIVITYPUB_TITLE' => 'ActivityPub for phpBB Module',
'ACP_SPACCINC_ACTIVITYPUB_SETFEDERATION' => 'Enable ActivityPub federation for this board',
'ACP_SPACCINC_ACTIVITYPUB_SETDOMAIN' => 'Full domain name to use for federation',
'ACP_SPACCINC_ACTIVITYPUB_SETDOMAIN_INFO' => 'The domain on which federation will be handled. NEVER change this after enabling federation, unless you know the implications.',
'ACP_SPACCINC_ACTIVITYPUB_SETDOMAIN_INFO' => 'The domain on which federation will be handled. You will never be able to change this later! (unless you know all implications)',
'ACP_SPACCINC_ACTIVITYPUB_SETDOMAIN_CONFIRM' => 'I understand that the federation domain will not be able to be changed after having set it.'
]);

View File

@ -6,7 +6,7 @@ class install_acp_module extends \phpbb\db\migration\migration
{
public function effectively_installed()
{
return isset($this->config['spaccincphpbb_activitypub_setfederation']);
return isset($this->config['spaccinc_activitypub_setfederation']);
}
public static function depends_on()
@ -17,7 +17,8 @@ class install_acp_module extends \phpbb\db\migration\migration
public function update_data()
{
return [
['config.add', ['spaccincphpbb_activitypub_setfederation', 0]],
['config.add', ['spaccinc_activitypub_setfederation', 0]],
['config.add', ['spaccinc_activitypub_setdomain', '']],
['module.add', [
'acp',